您的问题的问题是您假设问题出在符号位上(并且您不能用有效位溢出),但它不存在,您在接近符号位之前就遇到了错误。在分析您的代码后,在最后一次乘以 10 时会出现最终的无符号 32 位整数溢出(乘以 10,而不是乘以 2,因此每次乘法最多可以得到四个新位,从而更容易溢出) , 即使你做unsigned 算术(推荐这样你不需要玩符号)
只需修改一点代码以使用unsigned 算术,并在reverse 函数中对该版本的程序发出一些跟踪:
#include <stdio.h>
#include <stdlib.h>
unsigned reverse(unsigned x, unsigned base)
{
unsigned reversednum=0;
while(x) {
unsigned digit = x % base;
reversednum *= base;
reversednum += digit;
x /= base;
printf("reverse: digit=%u, reversednum=%u, x=%u, base=%u\n",
digit, reversednum, x, base);
}
return reversednum;
}
int main()
{
char buffer[100];
while ( fgets(buffer, sizeof buffer, stdin) ) {
unsigned x = (unsigned) atol(buffer);
printf("reverse(%u) => %u\n", x, reverse(x, 10));
} /* while */
} /* main */
使用您的输入执行:
$ reversed
23
reverse: digit=3, reversednum=3, x=2, base=10
reverse: digit=2, reversednum=32, x=0, base=10
reverse(23) => 32
1534236469
reverse: digit=9, reversednum=9, x=153423646, base=10
reverse: digit=6, reversednum=96, x=15342364, base=10
reverse: digit=4, reversednum=964, x=1534236, base=10
reverse: digit=6, reversednum=9646, x=153423, base=10
reverse: digit=3, reversednum=96463, x=15342, base=10
reverse: digit=2, reversednum=964632, x=1534, base=10
reverse: digit=4, reversednum=9646324, x=153, base=10
reverse: digit=3, reversednum=96463243, x=15, base=10
reverse: digit=5, reversednum=964632435, x=1, base=10 <-- after multipliying
by base, unsigned
overflows here to
9,646,324,350 -
2*4,294,967,296 ==
1,056,389,758
reverse: digit=1, reversednum=1056389759, x=0, base=10
reverse(1534236469) => 1056389759
您的代码的主要问题是您的号码(十位数字)中的最后一位数字可以是0..9,并且您的十位数字中的第一位数字不能超过4 (如果是4,那么第二个不能超过2...或者发生溢出,给出一个大于无符号最大可能值的数字)例如,所有大于1,000,000,000的数字以 5 或更多结束的数字将溢出,从以 4 结束的数字集合中,所有以 2 或更多结束的数字将从以 24 结束的数字中溢出,
在924 中完成,其前一个数字大于4,所有都将溢出,......所以直到在基数中形成数字MAX_UINT,您正在进行计算。
签名数字也会发生这种情况,因此您必须限制函数的域不溢出。由于最后一位为5 的所有十位数字都会溢出,我建议为第一个溢出的数字设置最大值1,000,000,005。对于不同的基数,您将有不同的限制,因此您必须小心(我不应该允许大于或等于 base^(floor(log(MAX_UINT)/log(base))) ==> 1,000,000,000 的数字,因为您将开始获得溢出的数字窗口,并穿插不t --- 在 32 位无符号的情况下,第一个失败的数字是 1,000,000,005,而对于有符号的数字,它是 1,000,000,003)
对于 64 位 unsigneds,限制应为 1.0E19 (== 10^((int)(log((double)MAX_ULONG_LONG)/log(10.0)))),因为 10,000,000,000,000,000,002 是第一个失败的数字。对于signeds,限制应该是1.0E18,因为1,000,000,000,000,000,039应该是第一个失败的数字。
只是为了完成
要考虑使用一组 32 位整数正常工作的函数,您应该像您所做的那样做出断言,但不要使用 INT_MAX 或 INT_MIN,您必须使用实际的数字边界限制.类似的东西
#include <stdint.h>
#include <assert.h>
int32_t reverse(int32_t x)
{
assert(x > -1000000000 && x < 1000000000);
int32_t reversednum = 0;
while(x) {
int digit = x % 10;
reversednum *= 10;
reversednum += digit;
x /= 10;
} /* while */
return reversednum;
} /* reverse */
甚至可以在运行时禁用生产代码的范围检查。 base 已固定为 10,因为这是您的函数,而 assert(3) 限制取决于编号基数的选择。