转换到 64 位编译环境时,经常会遇到符号扩展问题,这是因为类型转换和提升规则有些模糊。为防止出现符号扩展问题,请使用显式强制类型转换以取得预期结果。
要了解出现符号扩展的原因,了解 ISO C 的转换规则会有所帮助。可能会导致 32 位和 64 位编译环境之间大多数符号扩展问题的转换规则在以下操作过程中有效:
整型提升
无论有无符号,均可在调用整型的任何表达式中使用 char、short、枚举类型或位字段。
如果一个整型可以容纳初始类型的所有可能值,则值转换为整型;否则,值转换为无符号整型数。
带符号整型数和无符号整型数之间的转换
当一个带负号的整数被提升为同一类型或更长类型的无符号整型数时,它首先被提升为更长类型的带符号等价值,然后转换为无符号值。
以下示例被编译为 64 位程序时,即使 addr 和 a.base 均为无符号类型,addr 变量仍可成为带符号扩展变量。
%cat test.c struct foo { unsigned int base:19, rehash:13; }; main(int argc, char *argv[]) { struct foo a; unsigned long addr; a.base = 0x40000; addr = a.base << 13; /* Sign extension here! */ printf("addr 0x%lx\n", addr); addr = (unsigned int)(a.base << 13); /* No sign extension here! */ printf("addr 0x%lx\n", addr); } |
发生此符号扩展的原因是按以下方式应用了转换规则:
由于整型提升规则,a.base 将从无符号 int 转换为 int。因此,表达式 a.base << 13 的类型为 int,但是未发生符号扩展。
表达式 a.base << 13 的类型为 int,但是在赋值给 addr 之前,由于带符号和无符号整型数提升规则,会转换为 long,然后转换为无符号 long。从 int 转换为 long 时,会发生符号扩展。
% cc -o test64 -xarch=v9 test.c % ./test64 addr 0xffffffff80000000 addr 0x80000000 % |
如果将同一示例编译为 32 位程序,则不显示任何符号扩展:
cc -o test test.c %test addr 0x80000000 addr 0x80000000 |
有关转换规则的详细讨论,请参见 ISO C 标准。此标准中还包含对普通算术转换和整型常量有用的规则。