Solaris(64 位)开发者指南

符号扩展

转换到 64 位时,经常会遇到非故意符号扩展问题。该问题很难在实际发生前检测到,因为 lint(1) 不会针对它发出警告。此外,类型转换和提升规则也有些模糊。要解决非故意符号扩展的问题,必须使用显式强制类型转换来实现预期结果。

要了解出现符号扩展的原因,了解 ANSI C 的转换规则会有所帮助。以下是可能会导致 32 位和 64 位整数值之间大多数符号扩展问题的转换规则:

  1. 整型提升

    无论有无符号,均可以在任何调用类型为 int 的表达式中使用 charshort、枚举类型或位字段。如果 int 可以支持初始类型的所有可能值,则值会转换为 int 类型。否则,值会转换为 unsigned int 类型。

  2. 带符号整数和无符号整数之间的转换

    将带负号的整数提升为同一类型或更长类型的无符号整数时,该整数首先提升为更长类型的带符号相同值,然后再转换为无符号值。

有关转换规则的更详细讨论,请参阅 ANSI C 标准。该标准中还包括适用于普通运算转换和整数常量的规则。

以下示例编译为 64 位程序时,即使 addra.base 均是 unsigned 类型,addr 变量仍可成为带符号扩展变量。


示例 4–1 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);

}

进行此符号扩展的原因是按以下方式应用了转换规则:

  1. a.base 由于整型提升规则而从类型 unsigned int 转换为 int。因此,表达式 a.base << 13 的类型为 int,但是未进行符号扩展。

  2. 表达式 a.base << 13 的类型为 int,但是在赋值给 addr 之前,由于带符号和无符号整型提升规则,会转换为类型 long,然后再转换为类型 unsigned long。从类型 int 转换为 long 时,会进行符号扩展。


% cc -o test64 -xarch=v9 test.c

% ./test64

addr 0xffffffff80000000

addr 0x80000000

%

如果将同一示例编译为 32 位程序,则不显示任何符号扩展:


% cc -o test32 test.c

% ./test32

addr 0x80000000

addr 0x80000000

%