【问题标题】:c: type casting char values into unsigned shortc: 将 char 值类型转换为 unsigned short
【发布时间】:2019-04-16 08:42:43
【问题描述】:

从一个伪代码sn-p开始:

char a = 0x80;
unsigned short b;
b = (unsigned short)a;
printf ("0x%04x\r\n", b); // => 0xff80

根据我目前的理解,“char”既不是有符号字符也不是无符号字符,而是第三种类型的符号。

为什么会发生“a”是第一个符号从(可能取决于平台)扩展为 8 位存储到(可能又是特定于平台)16 位有符号短符号,然后转换为无符号短符号?

有确定扩展顺序的c标准吗?

本标准是否以任何方式指导如何处理“纯”字符(我曾将其称为 X-char,x 表示未确定的签名)的第三种类型的签名,以便结果至少是确定性的?

PS:如果在赋值行的'a'前面插入“(unsigned char)”语句,那么打印行的结果确实会变成0x0080。因此,连续只有两个类型转换将提供某些意图的预期结果。

【问题讨论】:

  • char 怎么可能既不是signed 也不是unsigned?这没有意义。
  • 为什么在处理数字时不使用signed char 而不是unsigned char,而在处理字符时使用char
  • @FiddlingBits -- charsigned charunsigned char 根据标准是不同的类型,因此混淆是可以理解的。可移植代码不能将裸 char 视为 signedunsigned,除非该实现细节已知。
  • @David:感谢“便携式代码”的措辞——这就是我真正的意思。如果您为可移植性编写代码,那么您必须将 /undecorated/ char 作为“第三种类型”处理 - 即使它与其他两种变体之一兼容 - 但您在代码编写时不会知道,只有在编译时或稍后。

标签: c casting char short


【解决方案1】:

char 类型不是“第三”符号。它是signed charunsigned char,具体是哪一个是实现定义的。

这是由C standard 的第 6.2.5p15 节规定的:

charsigned charunsigned char 这三种类型是 统称为字符类型。实施 应将char 定义为具有相同的范围、表示和 signed charunsigned char 的行为。

在您的实现中,char 似乎与signed char 相同,因此由于值为负数且目标类型为无符号,因此必须对其进行转换。

第 6.3.1.3 节规定了整数类型之间的转换如何发生:

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

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

3 否则,新类型有符号,值不能 代表其中;结果是实现定义的或 引发了实现定义的信号。

由于值 0x80 == -128 不能在 unsigned short 中表示,因此第 2 段中的转换发生。

【讨论】:

  • convert -128 to unsinged short => -128 + (USHRT_MAX + 1) = (65535 + 1) - 128 = 65408 = 0xff80(现实世界的实现可能使用不同的数学和逻辑)
  • 任何地方都没有符号扩展。转换为更大的类型是赋值时的左值转换。您引用了标准的正确部分,但这不是符号扩展。符号扩展名是 char c = -128; int i = c; printf("%x", i); 给出 0xffffff80 而不是 0x80。
  • 澄清一下,“在您的实现中,char 似乎与signed char 相同,因此当值转换为更大的类型时会发生符号扩展。”仅当值转换为更大的 signed 类型时才为真。这里不是这样,所以这部分是不正确的。
  • @Lundin 很好的澄清。虽然在双补码实现中它的效果相同,但在一般情况下并不一定如此。已更新以反映。
【解决方案2】:

char 具有实现定义的签名。它是有符号的或无符号的,取决于编译器。在某种程度上,char 确实是第三种字符类型,请参阅thischar 具有不确定性(非便携式)签名,因此不应用于存储原始数字。

但在这种情况下,这并不重要。

  • 在您的编译器上,char 已签名。
  • char a = 0x80; 以特定于编译器的方式强制将0x80 的类型(即int)转换为char。通常在 2 的补码系统上,这将意味着 char 获得值 -128,就像这里的情况一样。
  • b = (unsigned short)a; 强制将char 转换为unsigned short 1)。 C17 6.3.1.3 有符号和无符号整数然后说:

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

    比最大值多一个是65536。所以你可以把它想象成-128 + 65536 = 65408

  • 65408 的无符号十六进制表示是 0xFF80。任何地方都不会进行标志扩展!


1) 不需要演员表。当= 的两个操作数都是算术类型时,在这种情况下,右操作数会隐式转换为右操作数的类型(C17 6.5.16.1 §2)。

【讨论】:

  • 一些编译器进一步根据其警告级别设置将警告类型转换 - 因为它有时是有符号到无符号的转换,这意味着如果符号位设置为负,结果仍然是正的,因此不受欢迎的事情有可能发生。
  • 在你的脚注中,我认为你的意思是 = 运算符的 righthand 操作数会自动转换为 left 的类型手动操作数。
  • @AlexanderStohr 是的,这样的转换确实很可疑。在 gcc 上,我相信您必须明确使用 -Wconversion 才能获得警告。
  • @JohnBollinger 是的,当然,一个错字。已修复 - 谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-11
  • 2013-12-12
相关资源
最近更新 更多