This is shorter than some of my other posts, but I keep seeing people doing this wrong. It's even worse now in the LLM era.
Let's say you have a buffer and a counter and you need to iterate it backwards for some reason:
_Bool find_last(size_t count, int *haystack, int needle, size_t *where);
Almost everyone can do the forward version on autopilot:
for(size_t i = 0; i < count; i++){ if(haystack[i] == needle){ *where = i; return 1; } } return 0;
So they try to do the same thing, just iterating backwards:
for(size_t i = count - 1; i >= 0; i--){ // find needle }
But that doesn't work at all! i >=0 is tautologically true (modern compilers will warn). Even worse, if count is 0, i immediately becomes SIZE_MAX.
Ok, so they try the next version:
for(ssize_t i = count - 1; i >= 0; i--){ // find needle }
This works in practice with a signed index but introduces some problems:
- Now your index variable is signed, which can cause problems when used in expressions due to C's conversion rules.
- ssize_t is a POSIX thing so you'd have to define it yourself on windows.
- If your type is 32 bit, you run into promotion problems and might even run into truncation problems.
- This spelling of the loop is overcomplicated and requires special inspection each time. Did you get the second-clause condition right?
Avoiding these problems brings us to the actual, proper way to iterate backwards in C:
for(size_t i = count; i--; ){ // find needle }
So much simpler! Let's break it down: The key is the postdecrement operator on i. It decrements i but yields the value that i was before the operation. In this case, this means that in the body of the loop i is always in bounds as its value will be decremented after initialization so it is at most count - 1, while the condition checks that if it was previously 0 then the loop ends. i will wrap around, but unsigned wrap-around is well defined in C so that is not a problem.
The final version:
_Bool find_last(size_t count, int *haystack, int needle, size_t *where){ for(size_t i = count; i--; ){ if(haystack[i] == needle){ *where = i; return 1; } } return 0; }
Doing it this way means you don't have to mess around with converting to a signed loop index and look at how simple that loop is! You can tell at a glance if someone has implemented the reverse loop properly or not.
So please! If you see someone trying to iterate backwards, show them this way to do it!