8

是否可以通过显式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。

4

3 回答 3

8

将有符号整数类型转换为相同宽度的无符号整数类型可以更改表示,如果您可以找到具有符号幅度或补码符号表示的机器。但是类型int32_tuint32_t保证是二进制补码表示,因此在这种特殊情况下,表示不能改变。

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

  1. 当整数类型的值转换为_Bool以外的其他整数类型时,如果该值可以用新类型表示,则保持不变。
  2. 否则,如果新类型是无符号的,则通过在新类型中可以表示的最大值的基础上反复加减一,直到该值在新类型的范围内。
  3. ...

因此,实际上,如果将负数存储在 2 的补码中,则结果必须是逐位复制的结果。允许一致的实现使用符号幅度或补码;在这两种情况下,都必须修改负整数的表示以强制转换为无符号。


在评论中总结了一个冗长而有趣的讨论:

  • 在使用int32_tand的 OP 中的精确示例中,如果程序编译uint32_t,则表示必须相等,因为 C99 要求and的长度正好为 32 位且没有填充,并且需要使用 2 的补码表示。但是,它不要求这些类型存在;一个补码实现可以简单地不定义,并且仍然符合。int32_tuint32_tint32_tint32_t

  • 我对类型双关的解释低于水平规则。@R .. 向我们指出了 2004 年的缺陷报告,该报告似乎说类型双关是可以的,或者引发了一个陷阱,它更接近于实现定义的行为而不是未定义的行为。另一方面,该 DR 的建议解决方案似乎不在 C11 文档中,该文档说 (6.2.6.1(5)):

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

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

我之前关于类型双关的声明如下:


storage-punning union 有未定义的行为。但是在不调用 lagartos voladores 的情况下,如果某个值以无符号形式存储然后以有符号形式访问,则在某种程度上可以预期符号幅度或补码机器可能会引发硬件异常。

one -complement 和 sign-magnitude 都有两种可能的表示0,每个流行的符号位都有一个。带负号位的“负零”允许为“陷阱值”;因此,将值作为有符号整数访问(甚至只是复制它)可能会触发陷阱。

尽管 C 编译器有权抑制陷阱,例如通过使用 memcpy 或无符号操作码复制值,但不太可能这样做,因为这会让知道自己的程序在机器上运行的程序员感到惊讶带有陷阱负零,并且期望陷阱在非法值的情况下触发。

于 2013-09-21T02:16:19.940 回答
4

In the particular case you mention, a conversion from int32_t to uint32_t, the bit representation will be the same.

The standard specifically requires intN_t to be "a signed integer type with width N , no padding bits, and a two’s complement representation". Furthermore, corresponding signed and unsigned types must have the same representation for values within their shared range:

A valid (non-trap) object representation of a signed integer type where the sign bit is zero is a valid object representation of the corresponding unsigned type, and shall represent the same value.

There is one very small possible loophole: in principle, an implementation could, for example, make int32_t a typedef for int, and uint32_t a typedef for unsigned long, where intandlong are both 32 bits but have different byte orders. But that would only happen in a deliberately perverse implementation. Correction: This is not possible for a conforming implementation. int32_t and uint32_t must denote corresponding signed and unsigned types.

The above applies only because you happened to choose int32_t and uint32_t for your example, and the standard places very specific restrictions on their representation. (And if an implementation can't meet those restrictions, then it simply won't define int32_t or uint32_t.)

More generally, though, signed types are permitted to have one of three representations:

  • sign and magnitude, where setting the sign bit to 1 negates a number;
  • two's complement, where negation is equivalent to a bitwise complement followed by adding 1; and
  • one's complement, where negation is equivalent to a bitwise complement.

The vast majority of modern systems use two's complement (and have no padding bits). On such systems, signed-to-unsigned conversion with types of the same size generally does not change the bit representation. (The semantics of type conversions are defined in terms of values, but are designed to be convenient for two's complement systems.)

But for a system that uses either sign and magnitude or one's complement, signed-to-unsigned conversion must preserve the value, which means that conversion of a negative value must change the representation.

于 2013-09-21T03:05:32.980 回答
2

如果值在有符号和无符号类型的范围内,则值和表示都不会因转换而改变。

否则,只有当实现对类型的负值表示是二进制补码时,才允许有符号到无符号的转换保留位表示。对于一个补码或符号大小,它的转换必须改变表示。另一个方向的转换是实现定义的,因此它可能会也可能不会改变表示。

于 2013-09-21T02:21:51.560 回答