[英]Can a C compiler change bit representation when casting signed to unsigned?
是否可以通过显式转换int32_t
到uint32_t
来改变值的位表示?
例如,鉴于我有以下联合:
typedef union {
int32_t signed_val;
uint32_t unsigned_val;
} signed_unsigned_t;
规范保证这些代码段是否具有相同的行为?
uint32_t reinterpret_signed_as_unsigned(int32_t input) {
return (uint32_t) input;
}
和
uint32_t reinterpret_signed_as_unsigned(int32_t input) {
signed_unsigned_t converter;
converter.signed_val = input;
return converter.unsigned_val;
}
我在这里考虑C99。 我看过几个类似的问题,但他们似乎都在讨论C ++,而不是C.
如果可以找到具有符号幅度或单补码符号表示的计算机,则将有符号整数类型转换为具有相同宽度的无符号整数类型可以更改表示。 但int32_t
和uint32_t
类型保证是二进制补码表示,因此在该特定情况下,表示不能改变。
有符号整数到无符号整数的转换在标准6.3.1.3节中有明确定义。 相关算法是第二段:
- 当具有整数类型的值转换为除_Bool之外的另一个整数类型时,如果该值可以由新类型表示,则它将保持不变。
- 否则,如果新类型是无符号的,则通过重复地添加或减去一个可以在新类型中表示的最大值来转换该值,直到该值在新类型的范围内。
- ...
因此,结果必须实际上是,如果负数存储在2的补码中,则会导致比特复制。 允许符合标准的实现使用符号幅度或单补码; 在这两种情况下,必须修改负整数的表示以转换为无符号。
总结评论中冗长而有趣的讨论:
在使用int32_t
和uint32_t
的OP中的精确示例中, 如果程序编译 , 则表示必须相等,因为C99要求int32_t
和uint32_t
正好是32位长而没有填充,并且需要int32_t
使用2的补码表示。 但是,它不需要存在这些类型; 一个补码实现可能根本不能定义int32_t
,并且仍然符合。
我对类型惩罚的解释低于横向规则。 @R ..向我们指出2004年的一个缺陷报告 ,它似乎表明类型惩罚要么正常,要么触发陷阱,这比未定义的行为更接近实现定义的行为。 另一方面,该建议的解决方案似乎没有出现在C11文件中,该文件说(6.2.6.1(5)):
某些对象表示不需要表示对象类型的值。 如果对象的存储值具有这样的表示并且由不具有字符类型的左值表达式读取,则行为是未定义的。
在我看来,如果其中一个参与类型具有陷阱表示(如果读取类型没有陷阱表示,则不是未定义的行为),类型 - 双关语是未定义的行为。 另一方面,没有类型需要具有陷阱表示,并且只有少数类型被禁止具有: char
和union
类型 - 但不是union类型的成员 - 以及[u]int*K_t
类型已实现。
我之前关于打字的声明如下:
存储打孔联合具有未定义的行为。 但是,如果没有调用lagartos voladores,可以预期,如果将某个值存储为unsigned,然后以signed形式进行访问,则sign-magnitude或one-complement机器可能会抛出硬件异常。
补码和符号幅度都有两种可能的0
表示,每种都有一个流行的符号位。 具有负号位“负零”的那个被允许为“陷阱值”; 因此,访问该值(即使只是复制它)作为有符号整数可能会触发陷阱。
尽管C编译器可以通过使用memcpy或无符号操作码复制值来限制陷阱,但它不太可能这样做,因为对于知道她的程序在一台机器上运行的程序员来说这会令人惊讶捕获负零,并期望陷阱在非法值的情况下触发。
在你提到的特殊情况下,从int32_t
到uint32_t
的转换,位表示将是相同的。
该标准特别要求intN_t
为“有符号整数类型,宽度为N ,无填充位,以及二进制补码表示”。 此外,对应的有符号和无符号类型必须对其共享范围内的值具有相同的表示形式:
符号位为零的有符号整数类型的有效(非陷阱)对象表示是相应无符号类型的有效对象表示,并且应表示相同的值。
存在一个
非常小的可能漏洞:原则上,实现可以使
int32_t
成为
int
的typedef,而
uint32_t
是
unsigned long
的typedef,其中int
and
long都是32位但具有不同的字节顺序。
但这只会在故意不正当的实施中发生。
更正 :对于符合要求的实现,这是不可能的。 int32_t
和uint32_t
必须表示相应的有符号和无符号类型。
以上内容仅适用于您为您的示例选择int32_t
和uint32_t
,并且标准对其表示设置了非常具体的限制。 (如果实现不能满足这些限制,那么它就不会定义int32_t
或uint32_t
。)
但更一般地说,签名类型允许具有以下三种表示之一:
绝大多数现代系统使用二进制补码(并且没有填充位)。 在这样的系统上,具有相同大小的类型的带符号到无符号的转换通常不会改变位表示。 (类型转换的语义是根据值定义的,但设计用于方便两个补码系统。)
但是对于使用符号和幅度或者一个补码的系统,有符号到无符号的转换必须保留该值,这意味着负值的转换必须改变表示。
如果该值在signed和unsigned类型的范围内,则值和表示都不会被转换更改。
否则,只有当实现对该类型的负值表示为二进制补码时,才允许签名到无符号转换保留位表示。 对于补码或符号幅度,它的转换必须改变表示。 另一个方向的转换是实现定义的,因此它可能会也可能不会改变表示。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.