在编译为unsigned时,C编译器是否可以更改位表示?

Ale*_*ira 8 c c99 language-lawyer

是否有可能的,比方说,一个显式类型转换int32_tuint32_t,改变值的位表示?

例如,鉴于我有以下联合:

typedef union {
    int32_t signed_val;
    uint32_t unsigned_val;
} signed_unsigned_t;
Run Code Online (Sandbox Code Playgroud)

规范保证这些代码段是否具有相同的行为?

uint32_t reinterpret_signed_as_unsigned(int32_t input) {
    return (uint32_t) input;
}
Run Code Online (Sandbox Code Playgroud)

uint32_t reinterpret_signed_as_unsigned(int32_t input) {
    signed_unsigned_t converter;
    converter.signed_val = input;
    return converter.unsigned_val;
}
Run Code Online (Sandbox Code Playgroud)

我在这里考虑C99.我已经看到了一些类似的问题,但他们似乎都在讨论C++,不C.

ric*_*ici 8

如果可以找到具有符号幅度或单补码符号表示的计算机,则将有符号整数类型转换为具有相同宽度的无符号整数类型可以更改表示.但是类型int32_t并且uint32_t保证是二进制补码表示,因此在该特定情况下,表示不能改变.

有符号整数到无符号整数的转换在标准6.3.1.3节中有明确定义.相关算法是第二段:

  1. 当具有整数类型的值转换为除_Bool之外的另一个整数类型时,如果该值可以由新类型表示,则它将保持不变.
  2. 否则,如果新类型是无符号的,则通过重复地添加或减去一个可以在新类型中表示的最大值来转换该值,直到该值在新类型的范围内.
  3. ...

因此,结果必须实际上是,如果负数存储在2的补码中,则会导致比特复制.允许符合标准的实现使用符号幅度或单补码; 在这两种情况下,必须修改负整数的表示以转换为无符号.


总结评论中冗长而有趣的讨论:

  • 在OP的精确示例中,使用int32_tuint32_t,如果程序编译,表示必须相等,因为C99需要int32_t并且uint32_t正好是32位长而没有填充,并且需要int32_t使用2的补码表示.但是,它不需要存在这些类型; 一个补充实现可能根本无法定义int32_t,仍然符合.

  • 我对类型惩罚的解释低于横向规则.@R ..向我们指出了2004年的一个缺陷报告,它似乎表明类型惩罚要么正常,要么触发陷阱,这比未定义的行为更接近实现定义的行为.另一方面,建议的DR的解决方案似乎不在C11文件中,该文件说(6.2.6.1(5)):

某些对象表示不需要表示对象类型的值.如果对象的存储值具有这样的表示并且由不具有字符类型的左值表达式读取,则行为是未定义的.

在我看来,如果其中一个参与类型具有陷阱表示(如果读取类型没有陷阱表示,则不是未定义的行为),类型 - 双关语是未定义的行为.另一方面,没有类型需要具有陷阱表示,并且只有少数类型被禁止具有一个: charunion类型 - 但不是联合类型的成员 - 以及[u]int*K_t实现的任何类型.

我之前关于打字的声明如下:


存储打孔联合具有未定义的行为.但是,如果没有调用lagartos voladores,可以预期,如果将某个值存储为unsigned,然后以signed形式进行访问,则sign-magnitude或one-complement机器可能会抛出硬件异常.

补码和符号幅度都有两种可能的表示形式0,一种是每种流行的符号位.具有负号位"负零"的那个被允许为"陷阱值"; 因此,访问该值(即使只是复制它)作为有符号整数可能会触发陷阱.

虽然C编译器可以通过使用memcpy或无符号操作码复制值来限制陷阱,但是不太可能这样做,因为对于知道她的程序在机器上运行的程序员来说这会让人感到惊讶捕获负零,并期望陷阱在非法值的情况下触发.