【问题标题】:Are literal suffixes needed in standard C?标准 C 中是否需要文字后缀?
【发布时间】:2017-04-03 19:47:01
【问题描述】:

有一个问题已经回答了变量声明的特殊情况,但是其他文字常量的用途呢?

例如:

uint64_t a;
...
int32_t b = a / 1000000000;

最后一段代码是否等同于任何标准 C 编译器中的下一段代码?

uint64_t a;
...
int32_t b = (int32_t)(a / UINT64_C(1000000000));

换句话说,是否需要 xINTn_C 宏(假设我们在隐式转换错误的情况下使用显式转换)?

编辑

当编译器读取1000000000 时,是否允许在内部表示中将其存储为int(删除所有溢出位),或者它必须以尽可能高的精度(long long)存储它,直到它解析整个表达式类型?它是实现定义的行为还是标准规定的?

【问题讨论】:

  • 编辑你的问题是不好的,这样会使现有答案中的观点无效。
  • 另外,我很好奇您为什么要询问旧的 C99 标准,而不仅仅是“通用 C”或 C11 标准。当然,<stdint.h><inttypes.h> 设施不是 C90 的一部分,但 C99 现在也很老了。
  • @JonathanLeffler 我修复了编辑问题。我问的是 C99,因为它是继 ANSI C 之后受支持最广泛的标准,也是我正在使用的标准。如果所有编译器都支持 C11,世界会更好,但事实并非如此......

标签: c c99


【解决方案1】:

您的第二个示例不是有效的 C99,看起来像 C++。也许你想要的是一个演员,即(int32_t)(a / UINT64_C(1000000000))

a / UINT64_C(1000000000)a / 1000000000 有区别吗?不,他们最终会执行相同的操作。但我不认为这真的是你的问题。

我认为您的问题归结为整数文字“1000000000”的类型是什么?它是 int32_t 还是 int64_t? C99 中的答案来自 §6.4.4.1 第 5 段:

整数常量的类型是对应列表中第一个可以表示其值的类型。

对于没有后缀的十进制常量,列表为intlong intlong long int。所以第一个文字几乎肯定是int(取决于int 的大小,它可能是32 位,因此大到足以容纳十亿)。 UINT64_C 宏的第二个字面量可能是unsigned longunsigned long long,具体取决于平台。它将是与uint64_t 对应的任何类型。

所以常量的类型是不一样的。第一个将签名,而第二个未签名。第二个很可能有更多的“long”,具体取决于编译器的基本 int 类型的大小。

在您的示例中,文字具有不同的类型没有区别,因为 / 运算符需要将文字提升为 a 的类型(因为 a 的等级将等于或大于在任何情况下都是字面的)。这就是为什么我不认为这真的是你的问题。

关于为什么UINT64_C() 很重要的一个例子,考虑一个表达式,如果文字被提升为更大的类型,结果会发生变化。即,溢出将发生在文字的本机类型中。

int32_t a = 10;
uint64_t b = 1000000000 * a;  // overflows 32-bits
uint64_t c = UINT64_C(1000000000) * a; // constant is 64-bit, no overflow

要计算 c,编译器需要将 a 提升为 uint64_t 并执行 64 位乘法。但是为了计算b,编译器将使用 32 位乘法,因为这两个值都是 32 位。

在最后一个示例中,可以使用强制转换代替宏:

uint64_t c = (uint_least64_t)(1000000000) * a;

这也将强制乘法至少为 64 位。

为什么你会使用宏而不是转换文字?一种可能性是因为十进制文字是有符号的。假设您想要一个不能表示为有符号值的常量?例如:

uint64_t x = (uint64_t)9888777666555444333;    // warning, literal is too large
uint64_t y = UINT64_C(9888777666555444333);    // works
uint64_t z = (uint64_t)(9888777666555444333U); // also works

另一种可能性是预处理器表达式。强制转换不是用于#if 指令表达式的合法语法。但是UINTxx_C() 宏是。

由于宏使用粘贴到文字上的后缀并且没有短后缀,因此可能会发现 UINT16_C(x) 和 UINT32_C(x) 是相同的。这给出了(uint_least16_t)(65537) != UINT16_C(65537) 的结果。不是人们所期望的。事实上,我很难看出这如何符合 C99 §7.18.4.1:

宏 UINTN_C(value) 应扩展为对应于类型 uint_leastN_t 的整数常量表达式。

【讨论】:

  • 给定uint16_t x;,编译器是否会为x=UINT16_C(65539); 发布他们不会为x=65539; 发布的诊断信息?后者将定义行为(将 x 设置为 3),但在某些情况下,诊断可能更有意义。
  • UINT16_C(65539) 是无符号的,而 65539 是有符号的,因此可以通过后者获得隐式的有符号到无符号转换诊断。将文字隐式转换为 uint16_t 中的溢出更相关,在任何一种情况下都会得到。它不会使用x=(uint16_t)65539; 生成,因为转换不再是隐式的。
猜你喜欢
  • 2015-07-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多