13
#include <cstdint>
#include <iostream>

int main() {
  uint32_t i = -64;
  int32_t j = i;

  std::cout << j;
  return 0;
}

我尝试过的大多数编译器都会创建输出程序-64,但这是定义的行为吗?

  • 将有符号整数分配给无符号整数uint32_t i = -64;是否定义了行为?
  • 有符号整数赋值int32_t j = i;,当i等于时4294967232,是否定义了行为?
4

2 回答 2

8

对于无符号整数越界转换,定义结果;对于有符号整数,它是实现定义的。

C++11(ISO/IEC 14882:2011) §4.7 积分转换 [conv.integral/2]

如果目标类型是无符号的,则结果值是与源整数一致的最小无符号整数(模 2^n,其中 n 是用于表示无符号类型的位数)。[ 注意:在二进制补码表示中,这种转换是概念性的,位模式没有变化(如果没有截断)。——尾注]

如果目标类型是有符号的,则如果它可以在目标类型(和位域宽度)中表示,则该值不变;否则,该值是实现定义的。

此文本对于 C++14 保持不变。

于 2013-09-25T01:13:21.270 回答
1

该标准要求实现以某种方式记录当整数转换为太小而无法容纳它的有符号类型时,它们将如何确定使用什么值。它没有具体说明此类文件将采用的形式。符合标准的实现的文档可以在可读的打印中指定值将被截断并扩展二进制补码符号,然后在不可能的小字体中指定“......除非在一个月的第五个星期二编译程序,在这种情况下范围转换将产生值 24601"。当然,这样的文档不会有帮助,但标准本身并不关心“实施质量”问题。

在实践中,以除 100% 一致截断和补码符号扩展之外的任何方式定义行为的实现极为罕见。如果事实上 100% 的符合 C99 和 C11 的用于生产代码的实现默认以这种方式工作,我不会感到特别惊讶。不幸的是,无论是<limits.h>或任何其他标准头文件都没有定义任何实现可以表明它们遵循基本通用约定的方法。

可以肯定的是,期望通用行为的代码不太可能被任何符合标准的编译器的行为所绊倒。然而,编译器可能会提供一种不一致的模式是合理的,因为这可以使某些类型的代码更有效率。例如,给定:

int32_t x,i;
int16_t *p;
...
x = ++p[i];

如果int大于 16 位,则行为将在p[i]代码执行之前定义为 32767 的情况。增量将产生 -32768,该值将以int16_t实现定义的方式转换为(保证产生 -32768,除非实现记录其他内容),然后该值将存储到xp[i]

在像 ARM 这样总是使用 32 位进行算术运算的处理器上,截断存储的值p[i]不会花费任何成本,但截断写入的值x需要一条指令(或者,对于某些较旧的 ARM 型号,需要两条指令)。在这种情况下允许x接收 +32768 将提高此类处理器的效率。这样的选项不会影响大多数程序的行为,但如果标准定义了一种方法,依赖于行为的代码可以通过该方法说,例如

#ifdef __STDC_UNUSUAL_INT_TRUNCATION
#error This code relies upon truncating integer type conversions
#endif

以便那些将受到影响的程序可以防止在这种模式下意外编译。到目前为止,标准还没有定义任何这样的测试宏。

于 2017-07-26T15:46:13.330 回答