c - 为长值显式声明 L 或 UL 的原因是什么

标签 c constants

来自一个例子

unsigned long x = 12345678UL

我们一直了解到,编译器只需要在上面的示例中看到“long”即可设置 4 个字节(32 位)的内存。问题是为什么我们应该在 long 常量中使用 L/UL,即使在声明它是 long 之后。

最佳答案

当未使用后缀 LUL 时,编译器使用第一个可以包含列表中常量的类型(参见 C99 标准中的详细信息,条款 6.4.4:5。对于十进制常量,列表为 int , long int , long long int ).

因此,大多数时候,没有必要使用后缀。它不会改变程序的含义。对于大多数架构,它不会改变 x 示例初始化的含义,尽管如果您选择了一个无法表示为 long long 的数字,它会改变。另请参阅 codebauer 的回答,以了解后缀的 U 部分是必需的示例。


有几种情况,程序员可能想要显式设置常量的类型。一个例子是使用可变参数函数时:

printf("%lld", 1LL); // correct, because 1LL has type long long
printf("%lld", 1);   // undefined behavior, because 1 has type int

使用后缀的一个常见原因是确保计算结果不会溢出。两个例子是:

long x = 10000L * 4096L;
unsigned long long y = 1ULL << 36;

在这两个示例中,如果没有后缀,常量的类型为 int 并且计算结果为 int 。在每个示例中,这都会带来溢出的风险。使用后缀意味着计算将在更大的类型中完成,结果有足够的范围。

正如 Lightness Races in Orbit 所说,垃圾的后缀出现在任务之前。在上面的两个示例中,简单地将 x 声明为 long 并将 y 声明为 unsigned long long 不足以防止在计算分配给它们的表达式时发生溢出。


另一个示例是比较 x < 12U,其中变量 x 的类型为 int。如果没有 U 后缀,编译器会将常量 12 键入为 int ,因此比较是有符号整数的比较。

int x = -3;
printf("%d\n", x < 12); // prints 1 because it's true that -3 < 12

有了U后缀,比较就变成了unsigned int的比较。 “通常的算术转换”意味着 -3 被转换为一个大的 unsigned int:

printf("%d\n", x < 12U); // prints 0 because (unsigned int)-3 is large

事实上,常量的类型甚至可能会改变算术计算的结果,这也是因为“通常的算术转换”的工作方式。


请注意,对于十进制常量,C99 建议的类型列表不包含 unsigned long long 。在 C90 中,列表以当时最大的标准化无符号整数类型结束(即 unsigned long )。结果是,通过将标准类型 long long 添加到 C99,某些程序的含义发生了变化:在 C90 中键入为 unsigned long 的相同常量现在可以键入为带符号的 long long。我相信这就是为什么在 C99 中,决定在十进制常量类型列表中不包含 unsigned long long 的原因。 有关示例,请参阅 thisthis 博客文章。

关于c - 为长值显式声明 L 或 UL 的原因是什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38871847/

相关文章:

c# - 在 WCF 项目中放置常量的位置

c++ - SSE:从 const __m128 * 转换为 const float *

c++ const 成员函数,返回一个 const 指针。但是返回的指针是什么类型的 const?

c++ - C++ 中 vlas 的编译时 vs 运行时 const 变量赋值和分配

C编译错误: expected declaration or statement at end of input

C - 使用 sprintf 读取文件

c - 将数据存储在数组中并循环,1个元素为空

c - 为什么 qsort 中的排序方法改变了我的数组?

c++ - 我应该使用哪个版本的 MinGW

ios - 如果常量是不可变的,为什么我可以使用 let 重新分配它们?