如果LONG_MAX是2147483647,strtol(“ - 2147483648”,0,0)是否会溢出?

时间:2013-06-08 19:54:08

标签: c language-lawyer integer-overflow

根据strtol的规范:

  

如果主题序列具有预期形式且base的值为0,则以第一个数字开头的字符序列应解释为整数常量。如果主题序列具有预期的形式且base的值在2到36之间,则它应被用作转换的基础,将其值归于每个字母,如上所述。如果主题序列以减号开头,则转换产生的值应予以否定。如果endptr不是空指针,则指向最终字符串的指针应存储在endptr指向的对象中。

目前的问题是,在否定之前,该值不在long的范围内。例如,在C89中(整数常量不能采用类型long long),写-2147483648可能是溢出;你必须写(-2147483647-1)或类似的。

由于使用“整数常量”的措辞可以被解释为对整数常量的类型应用C规则,这可能足以让我们从这里保存未定义的行为,但同样的问题(没有这么容易)适用于strtoll

编辑:最后,请注意即使它确实溢出,也应返回“正确”值。所以这个问题实际上只是关于在这种情况下是否可以设置errno

2 个答案:

答案 0 :(得分:6)

虽然我今天不能指出标准中的一些措辞,但是当我在20世纪90年代为4BSD写strtol时,我非常确定这应该 not set {{ 1}},并确保我不会。无论这是基于标准中的措辞,还是与某人的个人讨论,我都不再记得。

为了避免溢出,这意味着必须非常仔细地进行计算。我是在errno中完成的,并且包含了此评论(仍在各种BSD的unsigned long来源中):

libc

我曾(并且在某种程度上)对C库中此操作与语言本身语法之间的不对称感到恼火(其中负数是两个单独的标记, /* * Compute the cutoff value between legal numbers and illegal * numbers. That is the largest legal value, divided by the * base. An input number that is greater than this value, if * followed by a legal input character, is too big. One that * is equal to this value may be valid or not; the limit * between valid and invalid numbers is then based on the last * digit. For instance, if the range for longs is * [-2147483648..2147483647] and the input base is 10, * cutoff will be set to 214748364 and cutlim to either * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated * a value > 214748364, or equal but the next digit is > 7 (or 8), * the number is too big, and we will return a range error. * * Set 'any' if any `digits' consumed; make it negative to indicate * overflow. */ 后跟数字,所以写-意味着-217483648成为-(217483648)当然是-(217483648U)因此是正面的!(假设当然是32位217483648U;问题是值因其他位数而异。)

答案 1 :(得分:1)

在32位平台-2147483648上的

不是c89下的溢出,它是LONG_MIN for和errno == 0.

直接从标准语言中引用

  

返回值

     

成功完成后,strtol()返回转换后的值if   任何。如果不能执行转换,则返回0并且可以执行errno   设置为[EINVAL]。如果正确的值超出范围   可表示的值,返回LONG_MAX或LONG_MIN(根据   值的符号),并且errno设置为[ERANGE]。

测试时,这似乎符合以下测试:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>

int main(int argc, char *argv[]) {
long val = strtol(argv[1], NULL, 10);
fprintf(stderr, "long max: %ld, long min: %ld\n", LONG_MAX, LONG_MIN);
fprintf(stderr, "val: %ld, errno: %d\n", val, errno);
    perror(argv[1]);
return 0;
}

使用以下命令在32位x86系统上编译时使用:

$ gcc -std=c89 foo.c -o foo

产生以下输出:

$ ./foo -2147483648
long max: 2147483647, long min: -2147483648
val: -2147483648, errno: 0
-2147483648: Success

$ ./foo -2147483649             
long max: 2147483647, long min: -2147483648
val: -2147483648, errno: 34
-2147483649: Numerical result out of range