1

Here is a loop that goes through a C string backwards:

size_t slen = strlen(string);
for (size_t i = slen-1; i >= 0; i--) {
    /* do stuff */
}

And when I compile this code with gcc -Wall -Wextra -std=c99 -o code code.c, I receive this warning:

warning: comparison of unsigned expression >= 0 is always true [-Wtype-limits]

When dealing with this warning, I replaced this loop with instead:

for (int i = (int)slen-1; i >= 0; i--) {

Which works fine, but I was wondering if their was a way to make the first loop still compile without the warning above? This warning only seems to trigger when I compile with -Wextra.

RoadRunner
  • 23,173
  • 5
  • 28
  • 59
  • 3
    Why would you want to make the first loop compile without warnings? It is logically incorrect as the warning tells you. So not sure what you are trying to achieve by trying to keep that incorrect code. – kaylum Feb 21 '17 at 02:46
  • 1
    What's the point of compiling the first loop? An unsigned variable is always `>= 0`, so why even bother checking? – Kerrek SB Feb 21 '17 at 02:49

2 Answers2

2

An unsigned integer can never be less than 0. That's what "unsigned" means. So obviously your loop will never terminate.

Instead you can use:

for (size_t i = slen; i-- > 0; ) {

This performs the test before decrementing i, so the last iteration of the loop will occur with i == 0 and then the test i-- > 0 fails, breaking the loop.

Related thread.

Community
  • 1
  • 1
M.M
  • 130,300
  • 18
  • 171
  • 314
2

The real crime here is that you violated the rule that all loops go forward, even the ones that go backward. The loop should be written as follows:

for (size_t i = 0, e = strlen(string); i != e; ++i) {
    const size_t ri = e - i - 1;

    /* do stuff with index ri */
}

This construction makes the −1-offset for the iterator-to-reverse-iterator relationship obvious, it avoids any signedness issues, and gives you a round counter for free.

Kerrek SB
  • 428,875
  • 83
  • 813
  • 1,025
  • @KerrekSB Could you elaborate on `the rule that all loops go forward`? – dualed Sep 16 '19 at 11:14
  • @dualed: Well, you should conceptualize all loops-over-ranges as having the same basic structure of "iterating over a range", and so they should all take the same general form. This isn't a language requirement, of course, but a "rule" in the sense of regularity among patterns: you start at the beginning of the range and walk until you reach the end. Whether that means going backwards over some other range, or going in strides, is ultimately a detail, and thus I argue that the detail should not appear in the loop head, which should always go forward. It's not 100%, but works in many situations – Kerrek SB Sep 16 '19 at 21:44
  • Thanks, so it's more a pattern rather than a rule, and has no ill-effects other than supposed readability? – dualed Sep 16 '19 at 22:30
  • Yes, right, it's more of a rule for how to conceptualize things than a rule of the language. Similar to, say, the rule-of-five. – Kerrek SB Sep 16 '19 at 23:49
  • The rule of five is a logical consequence of how the standard library defines implicit memory management operators, I wouldn't call it a concept... Also it has prominent publication (like on isocpp.org) and yours... I can't find anything on it, ofc 'the rule that all loops go forward' isn't really an easily searchable term. I'm always open to new ideas and best practices (which is why I asked you about it) but to digest this one, I feel myself wanting a more substantial foundation to accept it as sensible. – dualed Sep 18 '19 at 15:41