56

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?

cs95
  • 379,657
  • 97
  • 704
  • 746
rohit kumar
  • 739
  • 5
  • 8
  • 37
    A macro with a hardcoded variable name is asking for trouble. – jackarms Aug 13 '17 at 05:54
  • 1
    Changing `d=0` does output things though – Tony Tannous Aug 13 '17 at 05:56
  • 1
    @TonyTannous but it doesn't explain what is the problem in the OP – CIsForCookies Aug 13 '17 at 05:59
  • 2
    @CIsForCookies it is the comment section. I didn't tell him to change `d=0` or provide a solution. I was just wondering. – Tony Tannous Aug 13 '17 at 05:59
  • 21
    You should ask your compiler. It often will tell you what the problem is. Also in this case. For *gcc* and *clang* use `-Wall -Wextra`, for Visual C(++) at least `/W3`, preferably `/W4` at least when troubleshooting a problem. – hyde Aug 13 '17 at 06:17
  • Just in passing, you don't need the parentheses around `TOTAL_ELEMENTS - 2`. – Pete Becker Aug 13 '17 at 12:43
  • @jackarms: Normally I'd agree with you but the variable is global. This code is equivalent to #define TOTAL_ELEMENTS 7 int array [7] = {...}; but easier to maintain. – Joshua Aug 13 '17 at 21:07
  • 1
    @Joshua There are plenty of other problems that are a lot bigger; might as well call out bad code when you see it, to discourage people from copying it. – Clearer Aug 14 '17 at 08:50
  • 1
    Can users please settle on one canonical title and leave overly specific details out of it? Also, for users voting to close as off-topic - it is pretty self explanatory. – cs95 Aug 14 '17 at 12:33
  • This question has been [answered a huge number of times](https://stackoverflow.com/search?q=define+TOTAL_ELEMENTS+sizeof%28array%29). I have added a number of dups and they all answer this sufficiently. – P.P Aug 26 '17 at 09:59
  • @cᴏʟᴅsᴘᴇᴇᴅ At least 3 links above has got correct, quality answers and at least one of them quotes the C standard. That's good enough IMO. Obviously you are claiming yours is the *best* among them.You are free to re-open it if you disagree. – P.P Aug 26 '17 at 13:22

3 Answers3

157

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.

cs95
  • 379,657
  • 97
  • 704
  • 746
39

My gcc outputs this warning:

warning: comparison of integers of different signs: 'int' and 'unsigned long' [-Wsign-compare]
      for(d = 0; d < TOTAL_ELEMENTS; d++) 

which means that (TOTAL_ELEMENTS-2) is unsigned int while d is signed int. This makes the expression always false for the initial value of d, since (unsigned int)(-1) > (TOTAL_ELEMENTS-2).

Dennis Jaheruddin
  • 21,208
  • 8
  • 66
  • 122
CIsForCookies
  • 12,097
  • 11
  • 59
  • 124
5

Binary operations between different integral types are performed within a "common" type defined by so called usual arithmetic conversions. So int d is of singed type initialized with value -1. Which when convert into unsigned int it will return maximum of unsigned int which is much much greater than the value returned by TOTAL_ELEMENTS.

Patt
  • 69
  • 8