The Fix Wasn't Easy, or C Precedence Bites
Posted3 months agoActive2 months ago
boston.conman.orgTechstory
calmnegative
Debate
40/100
C ProgrammingOperator PrecedenceSoftware Bugs
Key topics
C Programming
Operator Precedence
Software Bugs
The author describes a difficult-to-debug issue caused by C operator precedence rules, sparking a discussion on programming language design and debugging challenges.
Snapshot generated from the HN discussion
Discussion Activity
Very active discussionFirst comment
3d
Peak period
30
72-84h
Avg / period
9
Comment distribution45 data points
Loading chart...
Based on 45 loaded comments
Key moments
- 01Story posted
Oct 22, 2025 at 2:26 AM EDT
3 months ago
Step 01 - 02First comment
Oct 25, 2025 at 12:40 AM EDT
3d after posting
Step 02 - 03Peak activity
30 comments in 72-84h
Hottest window of the conversation
Step 03 - 04Latest activity
Oct 26, 2025 at 7:07 PM EDT
2 months ago
Step 04
Generating AI Summary...
Analyzing up to 500 comments to identify key contributors and discussion patterns
ID: 45665515Type: storyLast synced: 11/20/2025, 6:42:50 PM
Want the full context?
Jump to the original sources
Read the primary article or dive into the live Hacker News thread when you're ready.
The precedence stuff here is pretty basic. When in doubt, using parentheses to make order explicit is never wrong. Or consult https://en.cppreference.com/w/c/language/operator_precedence... .
https://news.ycombinator.com/item?id=20311080
Just like there's nothing wrong with the ternary operator if you are sensible. I've seen nested ?: abominations that would make Jesus give you a funny look.
The code gets ridiculously easier to read if you write src[0] and src[1] instead of (*src) and (*(src+1)). And, as a bonus, the precedence problem disappears.
I really don't understand people why write C code like the original code. It's just asking for a bug.
Making the first thing a block doesn't add any lines and makes it less brittle, and makes future diffs better
And they do some weird alignment of assignments, and for some reason carry on adding extra spaces for some assignments even when they're alone in a block?
And they go out of their way to do pointer arithmetic rather than array operations that are more readable
And the code is essentially sscanf(str, "%%%2x", &value) plus some checks, so why not write that instead?
Also what kind of psycho uses CppStyleFunctionNames() in C?
Does C not have the equivalent of Prettier?
I am curious what their editing process was that changed:
> assert(isxdigit((src+1)));
to
> if (!isxdigit(
src+1)) return '\0';They said:
> I typed in the new code as that's faster than modifying the existing code
There is clang-format, but it is not always easy to use on a code base because you don't want to have it run on imported/3rd party source which may mix in with your regular source tree. I've been meaning to use it on a project but only have run on an explicit list of files.
> But even if you fail in getting emacs to do sane formatting, not everything is lost: use indent.
> Now, again, GNU indent has the same brain-dead settings that GNU emacs has, which is why you need to give it a few command line options. However, that’s not too bad, because even the makers of GNU indent recognize the authority of K&R (the GNU people aren’t evil, they are just severely misguided in this matter), so you just give indent the options -kr -i8 (stands for K&R, 8 character indents), or use scripts/Lindent, which indents in the latest style.
> indent has a lot of options, and especially when it comes to comment re-formatting you may want to take a look at the man page. But remember: indent is not a fix for bad programming.
(Linux kernel coding style, https://www.kernel.org/doc/html/latest/process/coding-style....)
People influenced by the Win32 C API. I prefer it that way myself.
You will find it on MS-DOS, Amiga, OS/2, Mac OS.
It was based on what was common in ALGOL derived languages.
On UNIX you will find it on X Windows, and Motif.
One that has gotten me more than once in Python (I really don’t code Python that much) is “1 + 2 if True else 3”. I keep thinking the parenthesis are “1 + (2 if True else 3)”, but it’s actually (1 + 2). Or am I lying to you and it’s the other way around?! I don’t know, why don’t you go check the Python interpreter :)
You could write it clearly by saying foo[1] instead of *(foo+1) which is what they ended up doing, but hey, pointer arithmetic looks complicated and clever, so let's show off with a WEEDLYWEEDLYWEEDLY guitar solo bit of code.
Keep in mind that precedence rules are arbitrary constructs, typically based upon what the rule maker perceived as more convenient. Perceptions will vary from person to person, so there is no objective obvious about them. Heck, there isn't even anything obvious about infix notation (see Forth or Lisp). Or, in the case of unary operators, it isn't obvious that the operator should come before or after the object it is operating on (consider how we negate as a prefix, while factorial is a suffix).
Parentheses are free and makes it absolutely clear what the intention is.
Think of
like a shorthand for: which can be a common pattern in languages that don't really have conditional expressions, like bash.Often, just the act of making a cheat sheet somehow helps to fix the principles in the brain, so you rarely if ever need to refer to it afterwards. Something I've personally found multiple times anyway.
The function has undefined behavior if given any value that is not in the range 0 to UCHAR_MAX, or the value EOF.
Some implementations are robust in the face of the values -128 to -1 but ISO C doesn't require that.