Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the 'for' loop condition fail? [duplicate]

In the code shown below, nothing gets printed, which means the condition in the for loop fails. What could be the reason?

I'm wondering because when I print TOTAL_ELEMENTS separately, it gives 5, so naturally this must be 5-2=3 => -1<=3, so it should print something.

#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;
}

Can someone explain this code?

like image 566
Arivarasan.K Avatar asked Aug 19 '16 14:08

Arivarasan.K


2 Answers

This is a result of the "usual arithmetic conversions".

From section 6.3.1.8 of the C standard:

If both operands have the same type, then no further conversion is needed.

Otherwise, if both operands have signed integer types or both have unsigned integer types, the operand with the type of lesser integer conversion rank is converted to the type of the operand with greater rank.

Otherwise, 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.

Otherwise, if the type of the operand with signed integer type can represent all of the values of the type of the operand with unsigned integer type, then the operand with unsigned integer type is converted to the type of the operand with signed integer type.

Otherwise, both operands are converted to the unsigned integer type corresponding to the type of the operand with signed integer type.

The sizeof operator returns a size_t, which is an unsigned value. So (sizeof(array) / sizeof(array[0])) - 2 is also unsigned.

Because you are comparing a signed and an unsigned value, the signed value is converted to unsigned. Converting -1 to unsigned results in the largest unsigned value, which results in the comparison being false.

If you cast the right hand side to int, it will work as expected.

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

Output:

23
34
12
17
204
99
16

Or you could avoid the issue by normalizing how you index the array:

for (d = 0; d < TOTAL_ELEMENTS; d++) {
    printf("%d\n", array[d]);
}
like image 178
dbush Avatar answered Sep 23 '22 07:09

dbush


When I try to print TOTAL_ELEMENTS - 2 like this:

printf("total %d\n", TOTAL_ELEMENTS - 2);

I got an warning (using gcc 4.8) saying:

test.c:8:2: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘long unsigned int’ [-Wformat=]
  printf("total %d\n", TOTAL_ELEMENTS - 2);
  ^

The warning means that TOTAL_ELEMENTS - 2 is long unsigned. Now when you compare a signed int with unsigned int, that signed int is treated as unsigned. So in d <= (TOTAL_ELEMENTS-2), d becomes a very high valued positive number (assuming 2's complement number system is used).

You can cast the result to int to fix the issue.

d <= (int)(TOTAL_ELEMENTS-2)

Or if you are using the macro in many places then you can change that like this:

#define TOTAL_ELEMENTS (int)(sizeof(array) / sizeof(array[0]))
like image 24
taskinoor Avatar answered Sep 24 '22 07:09

taskinoor