C在short和int之间进行强制转换的规则是什么?

时间:2013-10-09 04:00:04

标签: c

当使用C在short和int之间进行转换时,我很困惑。我假设short是16位而int是32位。我测试了以下代码:

unsigned short a = 0xFFFF;
signed short b = 0xFFFF;

unsigned int u16tou32 = a;
unsigned int s16tou32 = b;
signed int u16tos32 = a;
signed int s16tos32 = b;

printf("%u %u %d %d\n", u16tou32, s16tou32, u16tou32, s16tou32);

我得到的是:

  • u16tou32:65535
  • s16tou32:4294967295
  • u16tos32:65535
  • s16tos32:-1

我感到困惑的是s16到u32之间的转换,以及u16到s32之间的转换。似乎s16到u32正在进行“符号扩展”,而u16到s32则没有。这背后的规则究竟是什么?这也是依赖于实现的吗?在C中进行这种类型的转换是否安全,或者我应该自己使用位操作来避免意外结果?

4 个答案:

答案 0 :(得分:5)

任何时候整数类型被转换为不同的整数类型,它都属于标准中称为整数提升的分类,并且所有这些都被定义(其中一个是实现,但是我们会到达最后一个;扰流板在上面的一般评论中提到了)。

关于价值认定的一般概述:

C99 6.3.1.1-p2

  

如果int可以表示原始类型的所有值(由宽度限制,对于位字段),则该值将转换为int;否则,它将转换为 unsigned int 。这些被称为整数促销。所有其他类型都不会被整数促销更改。

那就是说,让我们来看看你的转换。 signed-shortunsigned int由以下内容涵盖,因为转换的属于unsigned int域之外:

C99 6.3.1.3-p2

  

否则,如果新类型是无符号的,则通过重复地添加或减去一个可以在新类型中表示的最大值来转换该值,直到该值在新类型的范围内。

这基本上意味着“添加UINT_MAX + 1”。在您的机器上,UINT_MAX是4294967295,因此,这变为

-1 + 4294967295 + 1 = 4294967295

关于您的unsigned shortsigned int转化,这是由常规价值促销促销所涵盖的。具体做法是:

C99 6.3.1.3-p1

  

当整数类型的值转换为_Bool以外的另一个整数类型时,如果该值可以用新类型表示,则它将保持不变。

换句话说,因为unsigned short的价值属于signed int的可覆盖范围,所以没有什么特别的,只是保存了价值。

最后,正如上面的一般评论中所提到的,b

的声明会发生一些特殊情况
signed short b = 0xFFFF;

在这种情况下,0xFFFF是有符号整数。小数值是65535.但是,该值可由signed short表示,因此还会发生另一次转换,可能是您不知道的转换:

C99 6.3.1.3-p3

  

否则,新类型已签名且值无法在其中表示;结果是实现定义的,或者引发实现定义的信号。

换句话说,您的实现选择将其存储为(-1),但您不能依赖于其他实现。

答案 1 :(得分:4)

如问题所述,假设为16位short和32位int

unsigned short a = 0xFFFF;

这会将a初始化为0xFFFF65535。表达式0xFFFF的类型为int;它隐式转换为unsigned short,并保留该值。

signed short b = 0xFFFF;

这有点复杂。同样,0xFFFF的类型为int。它隐式转换为signed short - 但由于该值超出了signed short的范围,转换无法保留该值。

当无法表示值时,将整数转换为有符号整数类型会产生实现定义的值。原则上,b的值可以是-32768+32767之间的任何值。在实践中,它几乎肯定是-1。我将假设其余部分的值为-1

unsigned int u16tou32 = a;

a的值为0xFFFF,已从unsigned short转换为unsigned int。转换会保留该值。

unsigned int s16tou32 = b;

b的值为-1。它已转换为unsigned int,显然无法存储-1的值。将整数转换为无符号整数类型(与转换为有符号类型不同)由语言定义;结果是模MAX + 1减少,其中MAX是无符号类型的最大值。在这种情况下,s16tou32中存储的值为UINT_MAX - 10xFFFFFFFF

signed int u16tos32 = a;

a0xFFFF的值转换为signed int。该值保留。

signed int s16tos32 = b;

b-1的值转换为signed int。该值保留。

所以存储的值是:

a == 0xFFFF (65535)
b == -1     (not guaranteed, but very likely)
u16tou32 == 0xFFFF (65535)
s16tou32 == 0xFFFFFFFF (4294967295)
u16tos32 == 0xFFFF (65535)
s16tos32 == -1

总结整数转换规则:

如果目标类型可以表示该值,则保留该值。

否则,如果目标类型是无符号的,则以MAX+1为模减少该值,这相当于丢弃除了低位N位之外的所有值。另一种描述这种情况的方法是将值MAX+1重复地添加到值中或从值中减去,直到得到范围内的结果(这实际上是C标准描述的结果)。编译器实际上并不生成执行重复加法或减法的代码;他们只需得到正确的结果。

否则,目标类型已签名且无法表示该值;转换产生实现定义的值。在几乎所有实现中,结果使用二进制补码表示丢弃除了低阶N比特之外的所有比特。 (C99为这种情况添加了一条规则,允许引发实现定义的信号。我不知道有任何编译器这样做。)

答案 2 :(得分:3)

这里发生的是参数的右侧是第一个从16位扩展到32位,而转换到左侧类型只发生在赋值时。这意味着如果右侧有符号,那么当它被转换为32位时它将被符号扩展,同样如果它是无符号的,那么它将只是零填充。

如果你对你的演员阵容小心,那么应该没有任何问题 - 但除非你做一些超级性能密集的事情,否则额外的几个按位操作不应该伤害任何东西。

另一方面,如果您正在为不同的整数类型假设某些位宽,那么您应该明确地使用stdint.h中定义的类型。我最近从* nix到Windows移植(别人的)代码时得到了一点,因为Visual C ++编译器对整数大小(LLP64)使用不同于我使用的任何其他x64或power-7编译器的约定(LP64)。简而言之,如果你想要32位,你最好用uint32_t这样的类型明确地说它。


  

所以当C中发生这种转换时,这总是会成立吗?由C标准定义? - 君

是的,应该始终坚持。来自C99标准的相关引号(带链接):"The integer promotions preserve value including sign."处理通常的算术类型转换时:"... the integer promotions are performed on both operands. Then the following rules are applied to the promoted operands..."

答案 3 :(得分:0)

这是数字65535的无符号短代表:

unsigned short a = 0xFFFF;

这是数字-1的签名短代表:

signed short b = 0xFFFF;

从unsigned short到unsigned int的简单升级,因此u16tou32是数字65535的无符号int表示形式:

unsigned int u16tou32 = a;

b(值为-1)被提升为int。因此它的十六进制表示将是0xFFFFFFFF。然后将其转换为无符号,因此是数字4294967295的表示形式:

unsigned int s16tou32 = b;

从unsigned short到unsigned int的升级值为65535.然后签名为int,这也是数字65535的表示:

signed int u16tos32 = a;

简单推广签名的short to signed int,所以s16tos32也是数字-1的表示:

signed int s16tos32 = b;