为长值显式声明 L 或 UL 的原因是什么 [英] what is the reason for explicitly declaring L or UL for long values
问题描述
举个例子
无符号长 x = 12345678UL
我们一直都知道,编译器只需要在上面的例子中看到long"就可以设置 4 个字节(32 位)的内存.问题是为什么我们应该在 long 常量中使用 L/UL,即使在声明它是 long 之后也是如此.
当不使用后缀L
或UL
时,编译器使用第一个可以包含的类型列表中的常量(参见 C99 标准第 6.4.4:5 节中的详细信息.对于十进制常量,列表为 int
、long int
、long长整数
).
因此,大多数时候,没有必要使用后缀.它不会改变程序的含义.对于大多数体系结构,它不会改变 x
的示例初始化的含义,尽管如果您选择了一个不能表示为 long long
的数字,它会改变.有关后缀的 U
部分是必需的示例,另请参见 codebauer 的答案.
在某些情况下,程序员可能想要显式设置常量的类型.一个例子是使用可变参数函数时:
printf("%lld", 1LL);//正确,因为 1LL 的类型为 long longprintf("%lld", 1);//未定义的行为,因为 1 的类型为 int
<小时>
使用后缀的一个常见原因是确保计算结果不会溢出.两个例子是:
长 x = 10000L * 4096L;unsigned long long y = 1ULL <<36;
在这两个示例中,如果没有后缀,常量将具有 int
类型,并且计算将作为 int
进行.在每个示例中,这都会导致溢出风险.使用后缀意味着计算将在更大的类型中完成,它有足够的结果范围.
正如 Lightness Races in Orbit 所说,litteral 的后缀出现在 分配之前.在上面的两个例子中,仅仅将 x
声明为 long
并将 y
声明为 unsigned long long
并不足以防止分配给它们的表达式的计算溢出.
另一个例子是比较 x <12U
其中变量 x
的类型为 int
.如果没有 U
后缀,编译器会将常量 12
键入为 int
,因此比较是对有符号整数的比较.
int x = -3;printf("%d
", x <12);//打印 1 因为确实 -3 <12
使用 U
后缀,比较变成无符号整数的比较.通常的算术转换"意味着 -3 被转换为一个大的无符号整数:
printf("%d
", x <12U);//打印 0 因为 (unsigned int)-3 很大
事实上,常量的类型甚至可能会改变算术计算的结果,这也是因为通常的算术转换"的工作方式.
<小时>请注意,对于十进制常量,C99 建议的类型列表不包含 unsigned long long
.在 C90 中,该列表以当时最大的标准化无符号整数类型(即 unsigned long
)结束.结果是通过将标准类型 long long
添加到 C99 来改变某些程序的含义:在 C90 中键入为 unsigned long
的相同常量现在可以键入作为签名的 long long
代替.我相信这就是为什么在 C99 中决定在十进制常量的类型列表中不包含 unsigned long long
的原因.请参阅 this 和 this 博客文章为例.p>
From an Example
unsigned long x = 12345678UL
We have always learnt that the compiler needs to see only "long" in the above example to set 4 bytes (in 32 bit) of memory. The question is why is should we use L/UL in long constants even after declaring it to be a long.
When a suffix L
or UL
is not used, the compiler uses the first type that can contain the constant from a list (see details in C99 standard, clause 6.4.4:5. For a decimal constant, the list is int
, long int
, long long int
).
As a consequence, most of the times, it is not necessary to use the suffix. It does not change the meaning of the program. It does not change the meaning of your example initialization of x
for most architectures, although it would if you had chosen a number that could not be represented as a long long
. See also codebauer's answer for an example where the U
part of the suffix is necessary.
There are a couple of circumstances when the programmer may want to set the type of the constant explicitly. One example is when using a variadic function:
printf("%lld", 1LL); // correct, because 1LL has type long long
printf("%lld", 1); // undefined behavior, because 1 has type int
A common reason to use a suffix is ensuring that the result of a computation doesn't overflow. Two examples are:
long x = 10000L * 4096L;
unsigned long long y = 1ULL << 36;
In both examples, without suffixes, the constants would have type int
and the computation would be made as int
. In each example this incurs a risk of overflow. Using the suffixes means that the computation will be done in a larger type instead, which has sufficient range for the result.
As Lightness Races in Orbit puts it, the litteral's suffix comes before the assignment. In the two examples above, simply declaring x
as long
and y
as unsigned long long
is not enough to prevent the overflow in the computation of the expressions assigned to them.
Another example is the comparison x < 12U
where variable x
has type int
. Without the U
suffix, the compiler types the constant 12
as an int
, and the comparison is therefore a comparison of signed ints.
int x = -3;
printf("%d
", x < 12); // prints 1 because it's true that -3 < 12
With the U
suffix, the comparison becomes a comparison of unsigned ints. "Usual arithmetic conversions" mean that -3 is converted to a large unsigned int:
printf("%d
", x < 12U); // prints 0 because (unsigned int)-3 is large
In fact, the type of a constant may even change the result of an arithmetic computation, again because of the way "usual arithmetic conversions" work.
Note that, for decimal constants, the list of types suggested by C99 does not contain unsigned long long
. In C90, the list ended with the largest standardized unsigned integer type at the time (which was unsigned long
). A consequence was that the meaning of some programs was changed by adding the standard type long long
to C99: the same constant that was typed as unsigned long
in C90 could now be typed as a signed long long
instead. I believe this is the reason why in C99, it was decided not to have unsigned long long
in the list of types for decimal constants.
See this and this blog posts for an example.
这篇关于为长值显式声明 L 或 UL 的原因是什么的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!