Loop starting at -1 doesn't print anything

rohit kumar picture rohit kumar · Aug 13, 2017 · Viewed 7.3k times · Source

This program is supposed to print out the elements of array, but when it is run, no output is shown.

#include <stdio.h>

#define TOTAL_ELEMENTS  (sizeof(array) / sizeof(array[0]))

int array[] = { 23, 34, 12, 17, 204, 99, 16 };

int main() {
    int d;
    for (d = -1; d <= (TOTAL_ELEMENTS - 2); d++) 
        printf("%d\n", array[d + 1]);
    return 0;
}

Why doesn't this program show any output?

Answer

cs95 picture cs95 · Aug 13, 2017

sizeof returns an unsigned integer, so TOTAL_ELEMENTS is also unsigned.

d is signed. Initially, d is -1. However, when doing the comparison, d is implicitly typecast to unsigned, so it is no longer -1 when being compared to TOTAL_ELEMENTS, it is actually UINT_MAX (which is 4294967295 on my machine, but might differ for others).

Also,

If you want to fix this, typecast TOTAL_ELEMENTS to int:

for(d = -1; d <= (int)(TOTAL_ELEMENTS - 2); d++) 

This will print:

23
34
12
17
204
99
16

As you'd expect. You may also want to look at Comparison operation on unsigned and signed integers for more information on the topic of signed-unsigned comparisons.

It is worth noting that turning on compiler warnings would've helped you figure out what was going on (as observed by hyde in his comment):

$ gcc -Wall -Wextra test.c
test.c:7:17: warning: comparison of integers of different signs: 'int' and 'unsigned long' [-Wsign-compare]
      for(d = 0; d < TOTAL_ELEMENTS; d++) 
              ~ ^ ~~~~~~~~~~~~~~
1 warning generated.

Alternatively, why not start d at 0 and run to TOTAL_ELEMENTS - 1 instead? You can even drop the typecast, that is necessary only for the corner case of d = -1.

for(d = 0; d < TOTAL_ELEMENTS; d++) 
    printf("%d\n", array[d]);

As a footnote, here are the relevant C99 Standard excerpts:

  1. 6.3.1.8p2 defines the conversion from signed to unsigned type.

    If the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with unsigned integer type.

  2. 6.3.1.3p2 defines how the conversion is done: By adding UINT_MAX + 1 to the signed representation.

    If the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.

    So -1 => -1 + (UINT_MAX + 1) = UINT_MAX, for this scenario.