我如何处理整数溢出?
How I can handle integer overflow?
我正在尝试处理整数溢出。我的代码是:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<limits.h>
int isInt (char *s)
{
char *ep = NULL;
long i = strtol (s, &ep, 10);
if ((*ep == 0) || (!strcmp(ep,"\n")))
return 1; // it's an int
return 0;
}
int main()
{
char *buffer = NULL;
size_t count = 0;
ssize_t ret;
//AMINO *a_acid;
int num;
for(;;)
{
printf("Please enter an integer:");
if((ret = getline(&buffer, &count, stdin)) < 0)
{
perror("getline: error\n");
free(buffer);
exit(EXIT_FAILURE);
}
if(!isInt(buffer))
{
perror("you are not entering int , Try again:");
continue;
}
sscanf(buffer, "%d",&num);
printf("%d\n", num);
if ((num > INT_MAX)|| (num < 0))
{
perror("you overflowed int variable , Try again:\n ");
continue;
}
break;
}
}
现在我正在检查这段代码是如何响应的。我看到了一些东西 weird.When 我输入了这么大的数字,然后它被检测到了。但有时没有被发现。
这是我的终端视图:
> nazmul@nazmul-Lenovo-G50-80:~/2nd_sem/biophysics$ gcc torson.c
> nazmul@nazmul-Lenovo-G50-80:~/2nd_sem/biophysics$ ./a.out
> Please enter an integer:ksdjfjklh
> you are not entering int , Try again:: Success
> Please enter an integer:338479759475637465765
> -1
> you overflowed int variable , Try again: : Numerical result out of
> range
> Please enter an integer:58678946895785
> 1103697833
> nazmul@nazmul-Lenovo-G50-80:~/2nd_sem/biophysics$
*为什么它适用于这个数字 338479759475637465765。但它不适用于 58678946895785。我在我的程序中使用的逻辑是当它超出范围时,然后 int 变量给出一些 -1 或负值。看了很多文章,还是不太清楚。
你把类型搞混了。
在 isInt
函数中,您使用 strtol
,其中 return 和 long
来检查值。然后在 main
函数中使用 sscanf
和 %d
,它读入 int
。
在您的系统上,long
似乎是 64 位,而 int
是 32 位。所以 strtol
无法完全转换 338479759475637465765 因为它大于 64 位变量可以容纳的大小。然后您尝试转换 58678946895785, 将 适合 64 位变量,但 不 适合 32 位变量。
您应该将 sscanf
读入 long
。然后您可以将该值与 INT_MAX
:
进行比较
long num;
...
sscanf(buffer, "%ld", &num);
printf("%ld\n", num);
if ((num > INT_MAX)|| (num < INT_MIN))
{
printf("you overflowed int variable , Try again:\n ");
continue;
}
还要注意这里调用perror
是没有意义的。您只能在调用设置 errno
.
的函数后立即使用它
sscanf(buffer, any_format_without_width, &anytype);
不足以检测溢出。
if the result of the conversion cannot be represented in the object, the behavior is undefined. C11dr §7.21.6.2 10
不要使用 *scanf()
系列来检测溢出。它可能在 select 情况下有效,但一般情况下无效。
改为使用 strto**()
函数。然而,即使是 OP 的 isInt()
也被错误编码,因为它错误地将 isInt("\n")
、isInt("")
、isInt("999..various large values ...999")
评估为良好的 int
。
选择:
bool isint_alt(const char *s) {
char *endptr;
errno = 0;
long y = strtol(s, &endptr, 10);
if (s == endptr) {
return false; // No conversion
}
if (errno == ERANGE) {
return false; // Outside long range
}
if (y < INT_MIN || y > INT_MAX) {
return false; // Outside int range
}
// Ignore trailing white space
while (isspace((unsigned char)*endptr)) {
endptr++;
}
if (*endptr) {
return false; // Trailing junk
}
return true;
}
strtol
将值转换为 long int
,其范围可能不同于 int
。此外,它 returns LONG_MAX
或 LONG_MIN
如果该值可以转换但 超出 long int
的范围。在这种情况下,errno
将被设置为 ERANGE
(但不是其他情况!)另外,在匹配失败的情况下,返回的值为 0,但未设置 errno
;但是 ep
指向字符串的开头。
int isInt (char *s)
{
char *ep = NULL;
// zero errno first!
errno = 0;
long i = strtol (s, &ep, 10);
if (errno) {
return 0;
}
// matching failure.
if (ep == s) {
return 0;
}
// garbage follows
if (! ((*ep == 0) || (!strcmp(ep,"\n")))) {
return 0;
}
// it is outside the range of `int`
if (i < INT_MIN || i > INT_MAX) {
return 0;
}
return 1;
}
不过,dbush 关于使用 perror
的说法是正确的。 strtol
仅在 long
溢出的情况下设置错误,这不是您的函数中唯一可能失败的情况,因此 perror
可以打印 Is a directory
或 Multihop attempted
.
如果 必须 使用 sscanf()
来检测 int
溢出而不是稳健的 strtol()
,这是一种麻烦的方法。
使用更宽的类型和宽度限制以防止扫描时溢出。
bool isint_via_sscanf(const char *s) {
long long y;
int n = 0;
if (sscanf(s, "18%lld %n", &y, &n) != 1) { // Overflow not possible
return false; // Conversion failed
}
if (y < INT_MIN || y > INT_MAX) {
return false; // Outside int range
}
if (s[n]) {
return false; // Trailing junk
}
return true;
}
在 INT_MAX > 1e18
.
的罕见平台上不足
它也错误地 returns 输入像 "lots of leading space and/or lot of leading zeros 000123"
一样无效。
使用 sscanf()
的更复杂的代码可以解决这些缺点,但最好的方法是 strto*()
。
我正在尝试处理整数溢出。我的代码是:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<limits.h>
int isInt (char *s)
{
char *ep = NULL;
long i = strtol (s, &ep, 10);
if ((*ep == 0) || (!strcmp(ep,"\n")))
return 1; // it's an int
return 0;
}
int main()
{
char *buffer = NULL;
size_t count = 0;
ssize_t ret;
//AMINO *a_acid;
int num;
for(;;)
{
printf("Please enter an integer:");
if((ret = getline(&buffer, &count, stdin)) < 0)
{
perror("getline: error\n");
free(buffer);
exit(EXIT_FAILURE);
}
if(!isInt(buffer))
{
perror("you are not entering int , Try again:");
continue;
}
sscanf(buffer, "%d",&num);
printf("%d\n", num);
if ((num > INT_MAX)|| (num < 0))
{
perror("you overflowed int variable , Try again:\n ");
continue;
}
break;
}
}
现在我正在检查这段代码是如何响应的。我看到了一些东西 weird.When 我输入了这么大的数字,然后它被检测到了。但有时没有被发现。 这是我的终端视图:
> nazmul@nazmul-Lenovo-G50-80:~/2nd_sem/biophysics$ gcc torson.c
> nazmul@nazmul-Lenovo-G50-80:~/2nd_sem/biophysics$ ./a.out
> Please enter an integer:ksdjfjklh
> you are not entering int , Try again:: Success
> Please enter an integer:338479759475637465765
> -1
> you overflowed int variable , Try again: : Numerical result out of
> range
> Please enter an integer:58678946895785
> 1103697833
> nazmul@nazmul-Lenovo-G50-80:~/2nd_sem/biophysics$
*为什么它适用于这个数字 338479759475637465765。但它不适用于 58678946895785。我在我的程序中使用的逻辑是当它超出范围时,然后 int 变量给出一些 -1 或负值。看了很多文章,还是不太清楚。
你把类型搞混了。
在 isInt
函数中,您使用 strtol
,其中 return 和 long
来检查值。然后在 main
函数中使用 sscanf
和 %d
,它读入 int
。
在您的系统上,long
似乎是 64 位,而 int
是 32 位。所以 strtol
无法完全转换 338479759475637465765 因为它大于 64 位变量可以容纳的大小。然后您尝试转换 58678946895785, 将 适合 64 位变量,但 不 适合 32 位变量。
您应该将 sscanf
读入 long
。然后您可以将该值与 INT_MAX
:
long num;
...
sscanf(buffer, "%ld", &num);
printf("%ld\n", num);
if ((num > INT_MAX)|| (num < INT_MIN))
{
printf("you overflowed int variable , Try again:\n ");
continue;
}
还要注意这里调用perror
是没有意义的。您只能在调用设置 errno
.
sscanf(buffer, any_format_without_width, &anytype);
不足以检测溢出。
if the result of the conversion cannot be represented in the object, the behavior is undefined. C11dr §7.21.6.2 10
不要使用 *scanf()
系列来检测溢出。它可能在 select 情况下有效,但一般情况下无效。
改为使用 strto**()
函数。然而,即使是 OP 的 isInt()
也被错误编码,因为它错误地将 isInt("\n")
、isInt("")
、isInt("999..various large values ...999")
评估为良好的 int
。
选择:
bool isint_alt(const char *s) {
char *endptr;
errno = 0;
long y = strtol(s, &endptr, 10);
if (s == endptr) {
return false; // No conversion
}
if (errno == ERANGE) {
return false; // Outside long range
}
if (y < INT_MIN || y > INT_MAX) {
return false; // Outside int range
}
// Ignore trailing white space
while (isspace((unsigned char)*endptr)) {
endptr++;
}
if (*endptr) {
return false; // Trailing junk
}
return true;
}
strtol
将值转换为 long int
,其范围可能不同于 int
。此外,它 returns LONG_MAX
或 LONG_MIN
如果该值可以转换但 超出 long int
的范围。在这种情况下,errno
将被设置为 ERANGE
(但不是其他情况!)另外,在匹配失败的情况下,返回的值为 0,但未设置 errno
;但是 ep
指向字符串的开头。
int isInt (char *s)
{
char *ep = NULL;
// zero errno first!
errno = 0;
long i = strtol (s, &ep, 10);
if (errno) {
return 0;
}
// matching failure.
if (ep == s) {
return 0;
}
// garbage follows
if (! ((*ep == 0) || (!strcmp(ep,"\n")))) {
return 0;
}
// it is outside the range of `int`
if (i < INT_MIN || i > INT_MAX) {
return 0;
}
return 1;
}
不过,dbush 关于使用 perror
的说法是正确的。 strtol
仅在 long
溢出的情况下设置错误,这不是您的函数中唯一可能失败的情况,因此 perror
可以打印 Is a directory
或 Multihop attempted
.
如果 必须 使用 sscanf()
来检测 int
溢出而不是稳健的 strtol()
,这是一种麻烦的方法。
使用更宽的类型和宽度限制以防止扫描时溢出。
bool isint_via_sscanf(const char *s) {
long long y;
int n = 0;
if (sscanf(s, "18%lld %n", &y, &n) != 1) { // Overflow not possible
return false; // Conversion failed
}
if (y < INT_MIN || y > INT_MAX) {
return false; // Outside int range
}
if (s[n]) {
return false; // Trailing junk
}
return true;
}
在 INT_MAX > 1e18
.
它也错误地 returns 输入像 "lots of leading space and/or lot of leading zeros 000123"
一样无效。
使用 sscanf()
的更复杂的代码可以解决这些缺点,但最好的方法是 strto*()
。