The situation:
I have a piece of code that works when compiled for 32-bits but fails when compiled for 64-bits with gcc 4.6. After identifying the problem and reading up on the standards, I cannot really understand why it is working for 32-bits. I hope someone can explain what is going on.
The code (somewhat simplified and cut down to the interesting parts):
// tbl: unsigned short *, can be indexed with positive and negative values
// v: unsigned int
// p: unsigned char *
tmp = tbl[(v >> 8) - p[0]]; // Gives segfault when not compiled with -m32
When compiled with -m32
the code works. When compiled without -m32
it gives a segfault. The reason for the segfault is that (v >> 8) - p[0]
is interpreted as an unsigned int
when compiled for 64-bit which for "negative" results will be way off.
According to this question, the C99 standard says the following:
6.2.5c9: A computation involving unsigned operands can never overflow, because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting type.
From this it seems that unsigned
minus unsigned
will always result in unsigned
output which is consistent with what happens in the 64-bit case. This does not seem to happen in the 32-bit case which is what I find extremely strange.
Can anyone explain what is happening in the 32-bit case?
In both cases you get a very large number because the unsigned int
wraps around, but in the 32-bit case, the pointer arithmetic also wraps around, and so it cancels out.
In order to do the pointer arithmetic, the compiler promotes the array index to the same width as the pointer. So for unsigned
with 32-bit pointers, you get the same result as int
with 32-bit pointers.
For example,
char *p = (char *) 0x1000;
// always points to 0x0c00
// in 32-bit, the index is 0xfffffc00
// in 64-bit, the index is 0xfffffffffffffc00
int r = p[(int) -0x400];
// depends on architecture
// in 32-bit, the index is 0xfffffc00 (same as int)
// in 64-bit, the index is 0x00000000fffffc00 (different from int)
int r = p[(unsigned) -0x400];
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With