Why does the 'for' loop condition fail? [d

2019-04-04 18:36发布

问题:

This question already has an answer here:

  • A riddle (in C) 4 answers

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?

回答1:

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]);
}


回答2:

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]))


回答3:

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

This evaluates to an unsigned type. In your loop, however, d is a signed value. In an expression where a signed and unsigned value participate, the signed value is converted to an unsigned. But d is -1, which cannot fit in an unsigned, so it "wraps around" to the highest unsigned value on the machine (on 2's complement).



回答4:

As already explained by other answers, the cause are usual arithmetic conversions, the type size_t obtained using sizeof causes int to get converted to unsigned type corresponding to type size_t.

I would like to add that the behavior is implementation defined1. The loop can be taken or not. This depends on the definition of the type size_t.

C Standard permits that type size_t has a lower rank than type int. In that case integer promotions promote the type size_t to type int. At that point both sides have the same type int, so the conversions stop. The comparison d <= (TOTAL_ELEMENTS - 2) then yields true, and the loop is taken.


1 The program is therefore not strictly conforming as the output relies on implementation-defined behavior.



回答5:

You know #define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0])) returns an unsigned number, and -1 may become the largest number, because it converts to a unsigned number.

And you can test the expression "printf("%d\n", -1 < TOTAL_ELEMENTS);"; it prints 0. So we can solve by adding (int) before (TOTAL_ELEMENTS - 2) or alter the loop:

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

And I don't think making the d variable dependent is a good way, because d is a variable in for loop.