Oracle® Solaris Studio 12.4:C 用户指南

退出打印视图

更新时间: 2014 年 12 月
 
 

7.3.3 符号扩展

转换到 64 位编译环境时,经常会遇到符号扩展问题,这是因为类型转换和提升规则有些模糊。为防止出现符号扩展问题,请使用显式强制类型转换以取得预期结果。

要了解发生符号扩展的原因,请考虑 ISO C 的转换规则。执行以下操作期间,导致 32 位和 64 位编译环境之间所发生大多数符号扩展问题的转换规则将生效:

  • 整型提升

    无论有无符号,均可在调用整型的任何表达式中使用 charshort枚举类型或位字段。

    如果一个整型可以容纳初始类型的所有可能值,则值转换为整型;否则,值转换为无符号整型数。

  • 带符号整型数和无符号整型数之间的转换

    当一个带负号的整数被提升为同一类型或更长类型的无符号整型数时,它首先被提升为更长类型的带符号等价值,然后转换为无符号值。

以下示例被编译为 64 位程序时,即使 addra.base 均是 unsigned 类型,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 将从 unsigned int 转换为 int。因此,表达式 a.base << 13 的类型为 int,但是未发生符号扩展。

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

% cc -o test64 -m64 test.c
% ./test64
addr 0xffffffff80000000
addr 0x80000000
%

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

cc -o test -m32 test.c
%test

addr 0x80000000
addr 0x80000000

有关转换规则的详细讨论,请参见 ISO C 标准。此标准中还包含对普通算术转换和整型常量有用的规则。