[英]Detect and prevent overflow for unsigned long in C
I have some lines in my code to check whether the resulting value overflows (by comparing it to it's previous iteration), and therefore if the input value is too large.我的代码中有一些行来检查结果值是否溢出(通过将其与之前的迭代进行比较),因此输入值是否太大。 This works for some values, but not for values whose increment is so large that it not only overflows, but overflows so much that the resulting value is larger than the previous iteration.
这适用于某些值,但不适用于增量如此之大以至于它不仅溢出,而且溢出太多以至于结果值大于前一次迭代的值。 For example, it triggers for
18446744073709551616
( MAX_ULONG + 1
), but not for 184467440737095516150
( MAX_ULONG * 10
).例如,它会触发
18446744073709551616
( MAX_ULONG + 1
),但不会184467440737095516150
( MAX_ULONG * 10
)。 How can I address this issue?我该如何解决这个问题? The code is as follows:
代码如下:
unsigned long result = 0;
unsigned long overflowCheck = 0;
int power = 0;
for (int i = (strlen(input) - 1); i >= 0; i--) {
if ((input[i] > ('0' - 1)) && (input[i] < ('9' + 1))) {
result += (input[i] - '0') * (unsigned long)pow(iBase, power++);
} else {
printf("Invalid input string.");
valid = 0;
return -1;
}
if (result < overflowCheck) {
printf("Input value too large.");
valid = 0;
return -1;
}
overflowCheck = result;
}
return result;
There are multiple problems in your code:您的代码中有多个问题:
pow
to perform integer arithmetics: type double
may have less value bits than type unsigned long
(for example on 64-bit linux, double
has 53 value bits and unsigned long
has 64).pow
来执行 integer 算术: double
类型的值位可能少于unsigned long
类型(例如在 64 位 linux 上, double
有 53 个值位, unsigned long
有 64 个)。 It is simpler to multiply the current value by iBase
and add the digit value for each new digit parsed.iBase
并为解析的每个新数字添加数字值更简单。 Here is a modified version:这是修改后的版本:
#include <errno.h>
#include <limits.h>
unsigned long atoul(const char *input, unsigned int iBase) {
if (iBase < 2 || iBase > 36) {
errno = EINVAL;
return 0;
}
unsigned long result = 0;
unsigned long maxval = ULONG_MAX / iBase;
int maxdigit = ULONG_MAX % iBase;
for (;;) {
int c = *input++;
int digit;
if (c >= '0' && c <= '9') {
digit = c - '0';
} else
if (c >= 'A' && c <= 'Z') {
digit = c - 'A' + 10;
} else
if (c >= 'a' && c <= 'z') {
digit = c - 'a' + 10;
} else {
break;
}
if (digit >= iBase)
break;
if (result > maxval || (result == maxval && digit > maxdigit) {
/* overflow detected */
errno = ERANGE;
return ULONG_MAX;
}
result = result * iBase + digit;
}
return result;
}
Suppose you want to check if x + y
overflows where x
and y
are both unsigned long
.假设您要检查
x + y
是否溢出x
和y
都是unsigned long
。 The naive approach would be to do this:天真的方法是这样做:
if (ULONG_MAX < x + y)
But this will always be false because of overflow.但是由于溢出,这总是错误的。 Instead, you would do this:
相反,你会这样做:
if (ULONG_MAX - x < y)
This check is algebraically the same as the first attempt but avoids issues of overflow.此检查在代数上与第一次尝试相同,但避免了溢出问题。 You can do a similar check in your case:
您可以在您的情况下进行类似的检查:
if ((input[i] > ('0' - 1)) && (input[i] < ('9' + 1))) {
int digit = input[i] - '0';
if (ULONG_MAX / 10 < result) {
printf("overflow");
return -1;
}
result *= 10;
if (ULONG_MAX - digit < result) {
printf("overflow");
return -1;
}
result += digit;
} else {
printf("Invalid input string.");
valid = 0;
return -1;
}
result < 0
will always return false since result
is unsigned (and can never be less than 0. One way to check for overflow is to make sure pow()
(as a double
) is within the bounds for long
. However, the real solution here is to not use pow()
and keep everything as integers. If you work starting with the most significant digit, you can multiply result by the base (16 in this case) and add the new digit each time. This works because 1234 = base*(base*(base*(0 + 1) + 2) + 3) + 4
result < 0
将始终返回 false,因为result
是无符号的(并且永远不能小于 0。检查溢出的一种方法是确保pow()
(作为double
)在long
范围内。但是,真正的解决方案这里是不要使用pow()
并将所有内容都保留为整数。如果您从最高有效数字开始工作,您可以将结果乘以基数(在这种情况下为 16)并每次添加新数字。这是因为1234 = base*(base*(base*(0 + 1) + 2) + 3) + 4
Some (incomplete) code would be:一些(不完整的)代码将是:
int input_len = strlen(input);
for (int i = 0; i < input_len; i++) {
// After finding out which digit group input[i] is in:
result = result * iBase + (input[i] - '0');
}
Since result
will only change by a factor of 16 at most, you can check for overflow by comparing with the previous result every iteration:由于
result
最多只会改变 16 倍,因此您可以通过每次迭代与之前的结果进行比较来检查溢出:
unsigned long previous = result;
// Add in the next digit
if (result < previous) {
// Overflow
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.