什么是“int i = 1;为什么 (i >= 60 * 60 * 1000 / 1 * 1000)”是真的?
Posted
技术标签:
【中文标题】什么是“int i = 1;为什么 (i >= 60 * 60 * 1000 / 1 * 1000)”是真的?【英文标题】:What is "int i = 1;Why (i >= 60 * 60 * 1000 / 1 * 1000)" true? 【发布时间】:2011-10-05 01:43:52 【问题描述】:首先,定义两个不带括号的常量表达式是我的错:
#define BIG_INTERVAL 60 * 60 * 1000
#define SMALL_INTERVAL 1 * 1000
int i = 1;
if (i >= BIG_INTERVAL / SMALL_INTERVAL - 1)
printf("Oops!\n");
宏展开后的if
语句为if(i >= 60 * 60 * 1000 / 1 * 1000 - 1)
。
这不是我的本意。但是如果我写if (i >= 3600000000 - 1)
,我会发现一些奇怪的东西。是假的。
60 * 60 * 1000 / 1 * 1000 - 1
是什么类型? int
?
【问题讨论】:
这就是为什么明智的程序员不使用#define
作为常量的原因。
@jalf:或者你可以记住用括号括起来......
你可以。如果您确保附近有医生,就可以用同样的方式向自己的脚开枪?为什么不正确地做,并使用实际的类型常量(例如,static const int
,或者可能是枚举)?
@icktoofay:问题是括号将解决这个特定问题,直到你忘记它们,即使你不这样做,你也会遇到一个不同的问题,即将出现宏。使用宏时要跟踪的事情太多了。考虑一下我工作的一个项目中的这个宏:#define for_all( iterator_t, it, container ) for ( iterator_t it = (container).begin(); it != (container).end(); ++it )
,它被用作:for_all( std::vector<int>::const_iterator, it, v ) std::cout << *it;
简单......对吗?
将问题重新标记为“c”。您的代码不是 c++;在 C++ 中你不应该使用 printf() 和 #define's
【参考方案1】:
int
s 上的所有运算符都返回 int
。所以是的,60 * 60 * 1000 / 1 * 1000 - 1
是int
。但是 3599999999 的预期结果对于 int
来说太大了,因此该表达式的实际计算结果为 -694967297(假设 32 位 int
和二进制补码)。
这不会发生在文字 3600000000
上,因为大于 INT_MAX
的整数文字属于 可以保存完整值的类型。
【讨论】:
“可以保存完整值的类型” - 如果存在这样的类型。不保证超过ULONG_MAX
的文字。
整数(相对于堆栈)溢出:)
即使60 * 60 * 1000
对于int
来说也可能太大了;似乎是我的答案。【参考方案2】:
60 * 60 * 1000 / 1 * 1000 - 1 = 3600000 * 1000 - 1,它会溢出 int 类型,所以结果可以是任何东西(在你的情况下它是负数,但不一定是)。
实现你想要的放( ):
#define BIG_INTERVAL (60 * 60 * 1000)
#define SMALL_INTERVAL (1 * 1000)
【讨论】:
+1 用于建议括号 - 这解释了真正的问题 - 尽管避免使用宏会是更好的建议。尽管如此,“溢出 int 类型,所以它是负数”使这听起来像前者总是暗示后者。迂腐,但形式上它是有符号整数类型的未定义行为,实际上它通常意味着你得到可能是正数或负数的垃圾......这里 3,600,000 * 1,000 恰好是 2^31 所以它是常见的负数32位平台int
s....【参考方案3】:
这是我的测试结果:
60 * 60 * 1000 / 1 * 1000 will result to -694967296
(60 * 60 * 1000) / (1*1000) will result to 3600
你的操作有问题,计算的优先级。
您可能需要考虑查看 C++ 运算符优先级http://msdn.microsoft.com/en-us/library/126fe14k%28v=vs.80%29.aspx。你会发现结果变成-694967296的原因,我认为这是溢出的影响。
【讨论】:
@muntoo - 在这里,我已经解释过了。【参考方案4】:如果您使用 int 为 64 位的编译器,您会发现表达式的结果为 false。如果您使用 int 为 32 位或 16 位的编译器,则您的表达式具有未定义的行为,因为有符号整数的溢出不必回绕。可能你的只是环绕,但不是必须的。
3600000000 是编译时可见的常量,因此如果 int 只有 32 位,那么您的编译器将不得不选择 long long(如果 long 是 64 位,则只需选择 long)。所以你的另一个表达式被评估了足够的位以避免溢出,结果是正确的。
【讨论】:
我一直认为负包装是标准的 - 它实际上是未定义的吗? @mafutrct:C 和 C++ 标准对有符号类型的表示没有任何要求,因此不能保证您会环绕,因为 2-complement 不是强制性的(并且操作溢出未定义)。然而,对于 n 位 signed 类型,标准要求模 2^n 算术。 你知道什么编译器知道 int 是 64 位吗? @Alexandre C. 你的意思可能是“对于 n 位 unsigned 类型,然而,......” @AlexandreC.:我明白了,谢谢。所以我多年前编写的 C++ 程序包含另一个 hack。【参考方案5】:可能是您溢出了 2147m 左右有符号的 int 的大小,这意味着如果您检查该表示,则该表示变为负数。正如其他答案所指出的那样,除法在展开时什么都不做,所以用括号括住宏定义
【讨论】:
大约。 21.47 亿,而不是 270 万(假设 4 字节 int) 谢谢,我真的不应该在困倦的时候回答问题:P 值得注意的是,这可能发生在 OP 上,因为内部表示使用 two's complement,但溢出行为通常是未定义的。 @Maxpm 你是完全正确的,因为一些编译器实际上通过将常量值分配给 -1 或其他值来处理常量值的溢出【参考方案6】:您很可能超出了带符号整数的有效值范围 - 3600000000 是一个相当大的数字!
发生这种情况时,该值将成为 int 数据类型的最小负值。
这将导致您的陈述为真。
【讨论】:
【参考方案7】:该表达式的每个参数都是一个整数,因此结果将是一个整数。
【讨论】:
是的,它们是整数。更重要的是,他们是int
s。 (integer
这个词涵盖了多种类型,从 char
到 long long
;int' is a particular type, not *just* an abbreviation of
integer`。)【参考方案8】:
我认为您对宏的工作方式感到困惑。您使用的不是这些宏中的值,而是方程本身。我认为这是你的困惑所在。我认为您应该在宏中加上括号或不使用宏。
【讨论】:
【参考方案9】:我没有看到任何人提到的一点是,即使将宏定义完全括起来也不能完全解决问题。
问题有:
#define BIG_INTERVAL 60 * 60 * 1000
(并且提问者承认缺少括号是一个问题)。但即使有:
#define BIG_INTERVAL (60 * 60 * 1000)
每个常量(60、60 和 1000)绝对可以表示为 int,但乘积是 3600000,而语言只保证 INT_MAX >= 32767
。
该语言说大整数常量的类型足以容纳它们的值(例如,100000
可以是 int
类型或 long int
类型,具体取决于这些类型的范围) ,但是对于表达式没有这样的规则,即使是常量表达式。
您可以像这样解决这个问题:
#define BIG_INTERVAL (60L * 60L * 1000L)
但这使它成为long
类型,即使它不需要。
至于运算符优先级问题,这是我最喜欢的例子:
#include <stdio.h>
#define SIX 1+5
#define NINE 8+1
int main(void)
printf("%d * %d = %d\n", SIX, NINE, SIX * NINE);
return 0;
输出当然是
6 * 9 = 42
(参见道格拉斯·亚当斯)。
【讨论】:
以上是关于什么是“int i = 1;为什么 (i >= 60 * 60 * 1000 / 1 * 1000)”是真的?的主要内容,如果未能解决你的问题,请参考以下文章