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

标准C中是否需要文字后缀?,c,c99,C,C99,有一个问题已经回答了变量声明的特殊情况,但是其他文字常量的用法呢 例如: uint64_t a; ... int32_t b = a / 1000000000; uint64_t x = (uint64_t)9888777666555444333; // warning, literal is too large uint64_t y = UINT64_C(9888777666555444333); // works uint64_t z = (uint64_t)(98887776

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

例如:

uint64_t a;
...
int32_t b = a / 1000000000;
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
在任何标准C编译器中,最后一段代码是否与下一段代码等效

uint64_t a;
...
int32_t b = (int32_t)(a / UINT64_C(1000000000));
换句话说,假设我们在隐式转换错误的情况下使用显式转换,那么是否需要xINTn_C宏

编辑


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

< p>您的第二个例子不是有效的C99,看起来像C++。也许您想要的是演员阵容,即int32_ta/UINT64_C1000000000

a/UINT64_C1000000000和a/UINT64_C1000000000之间有区别吗?不,他们会以同样的行动结束。但我不认为这真的是你的问题

我想你的问题可以归结为整型文字100000000的类型是什么?是int32还是int64?C99中的答案来自§6.4.4.1第5段:

整型常量的类型是可以表示其值的相应列表的第一个

对于没有后缀的十进制常量,列表是int,long int,long long int。因此,第一个文本几乎肯定是int,这取决于int的大小,它可能是32位,因此足够容纳10亿。UINT64_C宏的第二个文字可能是无符号长或无符号长,具体取决于平台。它将是与uint64\t对应的任何类型

所以常数的类型不一样。第一个将被签名,而第二个未签名。第二种类型很可能有更多的long,这取决于编译器基本int类型的大小

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

作为UIT64的原因,考虑一个表达式,如果文本被提升到更大的类型,结果会发生变化。也就是说,文本的本机类型将发生溢出

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 a;
...
int32_t b = a / 1000000000;
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_Cx和UINT32_Cx是相同的。这将得出以下结果:uint_least16_t65537!=UINT16_C65537。并非人们所期望的那样。事实上,我很难看出这是如何符合C99§7.18.4.1的:

宏UINTN_Cvalue应扩展为与uint_leastN_类型对应的整数常量表达式


<>你的第二个例子不是有效的C99,看起来像C++。也许您想要的是演员阵容,即int32_ta/UINT64_C1000000000

a/UINT64_C1000000000和a/UINT64_C1000000000之间有区别吗?不,他们会以同样的行动结束。但我不认为这真的是你的问题

我想你的问题可以归结为整型文字100000000的类型是什么?是int32还是int64?C99中的答案来自§6.4.4.1第5段:

整型常量的类型是可以表示其值的相应列表的第一个

对于没有后缀的十进制常量,列表是int,long int,long long int。因此,第一个文本几乎肯定是int,这取决于int的大小,它可能是32位,因此足够容纳10亿。UINT64_C宏的第二个文字可能是无符号长或无符号长,具体取决于平台。它将是与uint64\t对应的任何类型

所以常数的类型不一样。第一个将被签名,而第二个未签名。第二种类型很可能有更多的long,这取决于编译器基本int类型的大小

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

作为UIT64的原因,考虑一个表达式,如果文本被提升到更大的类型,结果会发生变化。也就是说,文本的本机类型将发生溢出

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 a;
...
int32_t b = a / 1000000000;
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_Cx和UINT32_Cx是相同的。这将得出以下结果:uint_least16_t65537!=UINT16_C65537。并非人们所期望的那样。事实上,我很难看出这是如何符合C99§7.18.4.1的:

宏UINTN_Cvalue应扩展为与uint_leastN_类型对应的整数常量表达式


编辑您的问题使现有答案中的观点无效是不好的。此外,我很好奇为什么您会询问旧的C99标准,而不仅仅是“通用C”或C11标准。诚然,设备和设施不是C90的一部分,但C99现在也很旧了。@JonathanLeffler I修复了编辑问题。我问C99是因为它是继ANSI C之后最广泛支持的标准,也是我正在使用的标准。如果所有编译器都支持C11,世界会更美好,但事实并非如此……编辑您的问题使其使现有答案中的观点无效是不好的。此外,我很好奇,为什么您会询问旧C99标准,而不仅仅是“通用C”或C11标准。诚然,设备和设施不是C90的一部分,但C99现在也很旧了。@JonathanLeffler I修复了编辑问题。我问C99是因为它是继ANSI C之后最广泛支持的标准,也是我正在使用的标准。如果所有编译器都支持C11,世界会更好,但事实并非如此……给定uint16_t x;编译器是否可能为x=UINT16_C65539发布诊断;他们不会发行x=65539;?后者将行为集x定义为3,但在某些情况下,诊断可能更有意义。UINT16_C65539是无符号的,而65539是有符号的,因此可以使用后者获得隐式有符号到无符号的转换诊断。更相关的是将文本隐式转换为uint16_t时的溢出,在任何一种情况下都会得到。不会使用x=uint16_t65539生成它;因为转换不再是隐式的;编译器是否可能为x=UINT16_C65539发布诊断;他们不会发行x=65539;?后者将行为集x定义为3,但在某些情况下,诊断可能更有意义。UINT16_C65539是无符号的,而65539是有符号的,因此可以使用后者获得隐式有符号到无符号的转换诊断。更相关的是将文本隐式转换为uint16_t时的溢出,在任何一种情况下都会得到。不会使用x=uint16_t65539生成它;因为转换不再是隐式的。