【发布时间】:2015-04-08 01:52:42
【问题描述】:
我在编写程序时遇到问题,该程序要求用户输入值 N,然后输出结果 5^N。问题是如果用户输入了一个大于 (2^31)-1 的数字,他们的数字就会溢出,程序会给他们一个错误的答案。
我所做的是将用户给定的整数值分配给一个无符号长整数值。这样,如果程序可以检查是否输入了大于 (2^31)-1 的数字,因为 unsigned long 可以容纳的正整数数量比有符号 int 可以容纳的数量多得多。这样做会导致程序正常运行,因为我可以检查是否发生溢出。
但是我的问题是:当用户在原始有符号整数变量中输入大于 2^31 - 1 的值时(这会导致它溢出),为什么我可以将这个溢出的值分配给未签名并得到“正确”的数字是什么?仅在尝试对该号码进行操作时才会发生溢出吗?内存是否存储实际数字(不受位宽限制)?
int endCount;
unsigned long endCountUn; /* power N */
/* Read value of N */
printf("This program will compute 5^N; enter N: ");
scanf("%d", &endCount);
/*
User's value put into unsigned long. Helps with detecting overflow.
*/
endCountUn = endCount;
if ( (endCount < 0) && (endCountUn < 2147483648) )
{
printf("The operation is undefined for negative integers\n");
}
else if ( endCountUn > 2147483647 )
{
printf("The value exceeds the supported numerical range\n");
}
【问题讨论】:
-
在 C 中,整数不会“溢出”。您可以将其视为模运算,其中某些运算的结果总是用“模 2 ^(位)”处理。就像附加到您编写的表达式的不可见部分一样。有符号整数通常用 2 的补码表示。你的第一次测试如果基本上测试你的数字的符号位是否为 1(如果它被解释为有符号值,则表示负数)。因此,您只需查看 2 的补码是如何工作的即可了解发生了什么。
-
@user2225104:无符号整数不会溢出;它们以 2^N 为模,其中 N 是位数。另一方面,当有符号整数溢出时,您会触发未定义的行为,并且任何事情都可能发生(包括通过从正到负包装来工作的代码)。未定义的行为是未定义的。
-
请注意,
scanf()在被要求处理太大而无法放入整数类型的数字时,其行为是未定义的。如果在 32 位有符号类型中输入大于 2^31-1 的值,则得到的结果是未定义的。通常会发生这种情况,但从形式上讲,行为是未定义的。 (ISO 9899:2011 §7.21.6.2fscanf函数¶10 说 '……或者如果转换的结果无法在对象中表示,则行为未定义。' -
@JonathanLeffler 所有“未定义”行为都是正确的。只是实际上与我们的世界,在我们的宇宙中不相关,还没有人编写过“来自地狱的编译器”。没有人生成额外的汇编指令只是为了说明那些未定义的用例可能发生“任何事情”。因此,出于解释的目的,假设负整数“下溢”到最大的正整数就足够了,因为您不会找到没有 2 补码的系统,也不会找到“地狱的编译器”做些别的事,只是为了证明你的观点。
标签: c integer 64-bit overflow 32-bit