标准C中是否需要字面后缀?

时间:2017-04-03 19:47:02

标签: c c99

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

例如:

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)存储它,直到它结算为止整个表达类型?它是一个实现定义的行为还是由标准规定的?

1 个答案:

答案 0 :(得分:4)

你的第二个例子是无效的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位,因此大到足以容纳10亿)。具有UINT64_C宏的第二个文字可能是unsigned longunsigned long long,具体取决于平台。它将是与uint64_t对应的任何类型。

所以常量的类型不一样。第一个是签名而第二个是未签名的。第二个很可能会有更多的“长”,这取决于编译器的基本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(值)应扩展为对应于uint_leastN_t类型的整数常量表达式。