There are times when I am very sympathetic to people whose code gets miscompiled by aggressive C compiler "optimizations" which (ab)use undefined behavior. Times when I think C compiler authors are really making things unacceptably painful and limiting, because the well-defined parts of the C language were never meant to cover everything useful about C.
For example, any time a loop gets "optimized" in a way that changes whether or not it is infinite or terminates. Or a check gets removed. My first reaction is "what the hell? you just don't do that! if the author put a check here, maybe - wild concept, but hear me out - maybe it was for a good reason, so don't remove it!"
But then there are other times when I look at the actual logic that got optimized out, and my sympathy dies on the spot. For example:
for(int i = 0; i >= 0; i++) //try it a bunch of times
Now, I still have sympathy in the sense that I empathize and relate - I can imagine what it is like to get so used to thinking in terms of low level machine operations that this is how you naturally think to express it. I've been there. And if you get used to relying on that, it sucks to have the compiler screw you over by violating your knowledge like that, especially when that knowledge is correct for your target hardware. And it's horrifying when you realize that this could be code for an embedded controller in a medical device or power reactor, which relies on that loop terminating after all the tries to not kill people. I sympathize when it comes to the consequences, and the desire to attribute some fault to the compiler "optimizing" that out.
But I'm no longer sympathetic in the sense that I start to think "this was never the code you ought to have written in this case, even independent of undefined behavior, so you 'started it', you committed the first wrong without which this would never have happened". You know what would be more readable and intuitive to a human reader? More clear and direct? What would take less mental steps and knowledge for the human reader to think through? This:
for(int i = 0; i < INT_MAX; i++)
Where `INT_MAX` is from the standard `limits.h` header, or you can define your own if you're on a weird minimal embedded platform. (I would also rename `i` to `tries`, at which point the line comment is redundant since the code itself tells the reader all of the same information.)
Why is this better besides being well-defined, which is just a happy coincidence? Because the code logic is closer to directly expressing the actual behavior and intent. The intent is "iterate a lot of times" and the behavior is "iterate from zero until we reach the highest positive value the counter can express". The second variant explicitly directly just says that.
The first variant doesn't say that: it says "count up from zero for as long as the result is zero or higher". Now I'm all for being conscious of what kind of arithmetic you're doing, but in the first and most common one that humans are taught and usually think in, the above sentence implies an infinite loop. And I'm all for saying "if you're coding in C you should be better at thinking in wraparound arithmetic", but what I'm saying is that wraparound arithmetic is inherently more complex as a concept: when interpreting the code you can't just have the invariant that n+1 always produces a number one larger than n, you have to maintain the possibility that sometimes n+1 does something else. And even if we treat just having that in the mind as free, the reader still has to do extra mental steps to actually apply it:
the termination condition is when the counter becomes less than zero
can this become less than zero?
yes, if it wraps around (assuming that's what happens on this hardware, which I guess I'll have to just trust that it does because the code seems to intend to use that behavior)
right after the counter reaches the maximum integer value that an `int` can hold.
So your readers have to bring in more knowledge and do more mental steps just to reach "oh we're basically doing `INT_MAX` tries". Or you could just say that with your code. Because if you just say `i < INT_MAX`, then your mental steps are just:
the termination condition is when the counter reaches the maximum integer value that an `int` can hold.
By the way, did you notice I reduced the number of tries by one in my alternative? That's not a gotcha, I'm making the point that noticing would cost extra time and mental effort. And where is the majority of that cost coming from? In decoding the first variant.