用scanf检测积分溢出

pax*_*blo 13 c iso language-lawyer

在最近回答另一个问题时,我发现了代码的问题,例如:

int n;
scanf ("%d", &n);
Run Code Online (Sandbox Code Playgroud)

使用strtol,您可以检测溢出,因为在这种情况下,插入的最大值被设置为nerrno设置为指示溢出,如下所示C11 7.22.1.4 The strtol, strtoll, strtoul, and strtoull functions /8:

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

但是,在标准处理的部分中scanf,具体来说C11 7.21.6.2 The fscanf function /10,我们看到:

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

现在,对我而言,这意味着可以返回任何值,并且没有提到errno任何设置.这一点已经曝光,因为上面链接问题的提问者进入9,999,999,999了32位int并且返回1,410,065,407,一个值太小,表明它只是在类型的极限处缠绕.233

尝试它时,我回来了2,147,483,647,最大可能的32位无符号值.

所以我的问题如下.在使用scanf函数族时,如何以可移植的方式检测积分溢出?它甚至可能吗?

现在,我要指出,我的系统(Debian的7)上,errno 实际设置为ERANGE在这种情况下,但我能找到什么的标准是强制要求这一点.此外,从返回值scanf1,表示扫描项目的成功.

maf*_*fso 5

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

POSIX在这里没有指定更多,也没有提及ERANGE.

此联机帮助页errno仅在以下情况下提及设置EOF; glibc文档根本没有提到ERANGE.

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