2017-04-03 61 views
0

有一个问题已经回答变量声明的具体情况,但对于其他字面常量用途?标准C中需要文字后缀吗?

例如:

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

是最后一块代码当量到下一个在任何标准的C语言编译器?

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

换句话说,是xINTn_C宏需要在所有(假设我们使用显式强制在隐含一个是错的情况下)?

EDIT

当编译器读取1000000000,是它允许将其存储为int在内部表示(丢弃所有溢出位),或者必须存储它在最高的可能精度(长长),直到它解析整个表达式类型?它是一种实现定义的行为,还是由标准强制的?

+0

这是不好的编辑您的问题,以便它使现有答案中的问题无效。 –

+0

另外,我很好奇为什么你问的是旧的C99标准,而不是“通用C”或C11标准。当然,''和''设施不是C90的一部分,但现在C99也很老旧。 –

+0

@JonathanLeffler我修正了编辑问题。我问的是C99,因为它是ANSI C之后最广泛支持的标准,并且是我正在使用的标准。如果所有的编译器都支持C11,那么世界会更好,但事实并非如此...... – user3368561

回答

3

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

a/UINT64_C(1000000000)a/1000000000之间的差异?不,他们会以相同的操作结束。但我不认为这真的是你的问题。

我认为你的问题归结到会出现什么整数文字的类型“10亿”是什么?它会是一个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相对应的任何类型。

所以类型的常量是不一样的。第一个将被签名,而第二个未签名。第二个很可能会有更多的“长”,这取决于编译器的基本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,编译器将需要促进auint64_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类型对应的整型常量表达式。

+0

为了更清楚,我更新了这个问题。 – user3368561

+0

鉴于'uint16_t x;'编译器可能会针对'x = UINT16_C(65539)发布诊断;'它们不会针对'x = 65539;'发布?后者会定义行为(将x设为3),但在某些情况下,诊断可能更有意义。 – supercat

+0

'UINT16_C(65539)'在65539被签名时是无符号的,所以可以得到一个隐式的带符号到无符号转换诊断。更相关的是将字面量隐式转换为uint16_t的溢出,无论哪种情况,都会得到这种溢出。它不会与'x =(uint16_t)65539;'一起生成,因为转换不再隐含。 – TrentP