最近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
,表示扫描项目成功。
答案 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
的行为也未定义。