【发布时间】:2012-05-02 04:57:55
【问题描述】:
我遇到了如下代码:
#define SOME_VALUE 0xFEDCBA9876543210ULL
这个SOME_VALUE 稍后被分配给一些unsigned long long。
问题:
- 在这种情况下是否需要像
ULL这样的后缀? - 什么情况下我们需要指定使用的整数类型?
- 在这种情况下,C 和 C++ 的行为是否不同?
【问题讨论】:
我遇到了如下代码:
#define SOME_VALUE 0xFEDCBA9876543210ULL
这个SOME_VALUE 稍后被分配给一些unsigned long long。
问题:
ULL 这样的后缀?【问题讨论】:
在 C 中,十六进制文字获取第一种类型的 int、unsigned int、long、unsigned long、long long 或 unsigned long long,如果它没有后缀,则可以表示其值。如果 C++ 有相同的规则,我不会感到惊讶。
如果你想给一个字面量一个比默认值更大的类型,或者你想强制它的签名,你需要一个后缀,例如
1 << 43;
如果没有后缀,那(几乎可以肯定)是未定义的行为,但例如 1LL << 43; 就可以了。
【讨论】:
1 << 43 的行为几乎肯定不是未定义的(意味着它可能会炸毁您的计算机),但很可能是不可移植的(意味着它可能在我的计算机上与您的计算机上做不同的事情)。
int 至少有 43 位,否则它是未定义的行为。它可能会产生 0 或 512,但它是未定义的。
1 << 43 == 2048.
1<<43 评估为 ARBITRARY_VALUE ? (1<<11) : 0,没有副作用;他们不一定会在何时返回 2048 和何时返回零保持一致,但他们不会做任何其他事情。然而,现代编译器可能会忽略任何代码,这些代码只有在程序接收到会导致过大移位(或导致其他 UB)的数据时才相关,这当然可能会使程序中的其他所有内容变得毫无意义。
printf("%ld", SOME_VALUE); 如果未指定 SOME_VALUE 的整数类型,则可能会导致输出错误。【讨论】:
在 C++ 中使用指定后缀的一个很好的例子是重载函数。举个例子:
#include <iostream>
void consumeInt(unsigned int x)
{
std::cout << "UINT" << std::endl;
}
void consumeInt(int x)
{
std::cout << "INT" << std::endl;
}
void consumeInt(unsigned long long x)
{
std::cout << "ULL" << std::endl;
}
int main(int argc, const char * argv[])
{
consumeInt(5);
consumeInt(5U);
consumeInt(5ULL);
return 0;
}
结果:
INT
UINT
ULL
【讨论】:
当您不提及任何后缀时,编译器会推断出整数文字的类型为int。由于某些整型文字如果推断其类型为int 可能会溢出,因此您添加后缀以告诉编译器推断类型为int 以外的类型。这就是你写0xFEDCBA9876543210ULL时所做的。
写浮点数时也可以使用后缀。 1.2 是 double,而 1.2f 是浮点数。
【讨论】:
如果您的唯一目的是获得数字的正确值,则不需要后缀; C 会自动选择适合该值的类型。
如果你想强制表达式的类型,后缀很重要,例如出于它如何在表达式中交互的目的。当您要执行会溢出较小类型(例如,1ULL<<n 或 x*10LL)的算术运算时,可能需要将其设为 long 或 long long,并且当您想要将其设为无符号时很有用表达式作为一个整体具有无符号语义(例如,c-'0'<10U 或 n%2U)。
【讨论】: