Johan Bezem ist Mitglied bei
Archive for the ‘QAC’ Category
Why 32768 isn’t always the same as 0×8000
Contrary to intuition, the C constants ‘32768′ and ‘0×8000′ have an
identical representation (0×8000), but possibly different types in C.
If you consider a processor with a 16-bit int type, and a 32-bit long
type, 32768 is considered long, whereas 0×8000 (and the octal variant
0100000) is considered ‘unsigned int’.
If you feel the need, check the C standard at
http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf, page 55 at the
bottom, and the table at page 56. (look at Harbison & Steele, 5th edition, and look at section 2.7.1 page 24ff).
Normally, there is no problem when using such values, since the
representations are identical.
However, consider this small example:
#define C_DECIMAL 32768
#define C_HEXADECIMAL 0x8000
void main(int argc, char *argv[])
{
volatile long long_dec = ((long)~C_DECIMAL);
volatile long long_hex = ((long)~C_HEXADECIMAL);
return;
}
When C_DECIMAL is considered long, the negation will invert 32 bits,
resulting in a representation 0xFFFF7FFF with type ‘long’; the cast is
superfluous.
When C_HEXADECIMAL is considered ‘unsigned int’, the negation will invert
16 bits, resulting in a representation 0×7FFF with type ‘unsigned int’;
the cast will then zero-extend to a ‘long’ value of 0×00007FFF.
Checking with a 16-bit integer compiler (CW7.1 ColdFire using ‘-intsize 2′):
0x00000000 _main: ; main: 0x00000000 0x4E560000 link a6,#0 0x00000004 0x518F subq.l #8,a7 0x00000006 0x223CFFFF7FFF move.l #-32769,d1 0x0000000C 0x2D41FFF8 move.l d1,-8(a6) 0x00000010 0x223C00007FFF move.l #32767,d1 0x00000016 0x2D41FFFC move.l d1,-4(a6) 0x0000001A 0x4E5E unlk a6 0x0000001C 0x4E75 rts
For those of you who do not know how to read assembler code I have made the differing values italic. So the compiler confirms the difference in behavior, and this is not a compiler error.
Lucky you if you have Lint to warn you. (Yes, I know, other tools will too, if you let them…)
Happy coding!
Deutsch
English
Nederlands