用scanf检测积分溢出

时间:2015-01-18 06:01:09

标签: c language-lawyer iso

最近answering another question,我发现代码有问题:

int n;
scanf ("%d", &n);

使用strtol,您可以检测到溢出,因为在这种情况下,允许的最大值被插入到n中,errno被设置为指示溢出,按{{1 }}:

  

如果正确的值超出了可表示值的范围,则返回LONG_MIN,LONG_MAX,LLONG_MIN,LLONG_MAX,ULONG_MAX或ULLONG_MAX(根据值的返回类型和符号,如果有的话),以及值宏ERANGE存储在errno。

但是,在处理C11 7.22.1.4 The strtol, strtoll, strtoul, and strtoull functions /8的标准部分,特别是scanf,我们看到:

  

如果此对象没有合适的类型,或者无法在对象中表示转换结果,则行为未定义。

现在,对我而言,这意味着可以返回任何值,并且没有提及C11 7.21.6.2 The fscanf function /10被设置为任何内容。这是因为上面链接问题的提问者将errno输入到32位9,999,999,999并返回int,值1,410,065,407太小,表明它只是在类型的极限处缠绕。

尝试它时,我得到了233,这是可能的最大32位无符号值。

所以我的问题如下。在使用2,147,483,647系列函数时,如何以便携方式检测积分溢出?它甚至可能吗?

现在我应该提一下,在我的系统(Debian 7)上,scanf 在这些情况下实际设置为errno但我在标准要求这样做。此外,ERANGE的返回值为scanf,表示扫描项目成功。

1 个答案:

答案 0 :(得分:5)

唯一可移植的方法是指定字段宽度,例如使用"%4d"(保证甚至适合16位int)或在运行时使用字段宽度(int)(log(INT_MAX) / log(10))构建格式字符串。这当然也拒绝例如32000,尽管它适合16位int。所以不,没有令人满意的便携方式。

POSIX此处未指定更多内容,也未提及ERANGE

This manpage仅在errno返回的情况下提及设置EOF; the glibc documentation根本没有提到ERANGE

这留下了一个问题,建议初学者阅读整数,我不知道。 scanf有太多未定义和未指定的方面非常有用,fgets无法在生产代码中使用,因为您无法正确处理0字节,并且strtol和朋友的便携式错误检查需要比自己实现功能更多的行(并且很容易出错)。对于整数溢出,atoi的行为也未定义。