问题
Possible Duplicate:
Confused about C macro expansion and integer arithmetic
A riddle (in C)
The expected output of the following C program is to print the elements in the array. But when actually run, it doesn't do so.
#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;
}
回答1:
Because sizeof
gives you an unsigned value, which you probably would have noticed had you turned up the warning level, such as using -Wall -Wextra
with gcc
(a):
xyzzy.c: In function 'main':
xyzzy.c:8: warning: comparison between signed and unsigned
If you force it to signed, it works fine:
#define TOTAL_ELEMENTS (int)((sizeof(array) / sizeof(array[0])))
What happens in detail can be gleaned from the ISO standard. In comparisons between different types, promotions are performed to make the types compatible. The compatible type chosen depends on several factors such as sign compatibility, precision and rank but, in this case, it was deemed that the unsigned type size_t
was the compatible type so d
was upgraded to that type.
Unfortunately, casting -1
to an unsigned type (at least for two's complement which is almost certainly what you're using) results in a rather large positive number.
One that's certainly larger the the 5
you get from (TOTAL_ELEMENTS-2)
. In other words, your for statement effectively becomes:
for (d = some big honking number way greater than five;
d <= 5;
d++
) {
// fat chance of getting in here !!
}
(a) This requirement to use extra
remains a point of contention between the gcc
developers and myself. They're obviously using some new definition of the word "all" of which I was previously unaware (with apologies to Douglas Adams).
回答2:
TOTAL_ELEMENTS
is of type size_t
, subtracting 2 is done at compile time and so it is 5UL
(emphasis on the unsigned suffix). The comparison with the signed integer d
is then always false. Try
for(d=-1;d <= (ssize_t)(TOTAL_ELEMENTS-2);d++)
FTW, the intel compiler warns about exactly this when you try and compile the code.
回答3:
To clarify what went wrong: sizeof() translates to a result type of size_t
, which is nothing but an unsigned integer, larger than or equal to unsigned int
.
So the result of (sizeof(array) / sizeof(array[0]))
is a result in two operands of size_t type. The division is performed on these operands:size_t / size_t
. Both operands are of the same type so it works fine. The result of the division is of type size_t
, which is the type that TOTAL_ELEMENTS results in.
The expression (TOTAL_ELEMENTS-2)
therefore have the types size_t - int
, since the integer literal 2
is of type int.
Here we have two different types. What happens then is something called balancing (formally "the usual arithmetic conversions"), which happens when the compiler spots two different types. The balancing rules state that if one operand is signed and the other unsigned, then the signed one is silently, implicitly converted to an unsigned type.
This is what happens in this code. size_t - int
is converted to size_t - size_t
, then the subtraction is executed, the result is size_t
. Then int <= size_t
is converted to size_t <= size_t
. The variable d
turns unsigned, and if it had a negative value, the code goes haywire.
来源:https://stackoverflow.com/questions/9902140/what-is-wrong-with-following-c-code