C 如果LONG_MAX为2147483647,strtol(";-2147483648";,0,0)是否溢出?

C 如果LONG_MAX为2147483647,strtol(";-2147483648";,0,0)是否溢出?,c,language-lawyer,integer-overflow,C,Language Lawyer,Integer Overflow,根据标准规范: 如果主题序列具有预期的形式,且base的值为0,则从第一个数字开始的字符序列应解释为整数常量。如果主题序列具有预期的形式,且基数的值介于2和36之间,则应将其用作转换的基数,将每个字母的值归因于上文给出的值。如果主题序列以减号开头,则转换产生的值应为负数。如果endptr不是空指针,则指向最终字符串的指针应存储在endptr指向的对象中 当前的问题是,在求反之前,该值不在long范围内。例如,在C89中(整数常量不能采用long-long),写入-2147483648可能是溢出;

根据标准规范:

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

当前的问题是,在求反之前,该值不在
long
范围内。例如,在C89中(整数常量不能采用
long-long
),写入
-2147483648
可能是溢出;您必须编写
(-2147483647-1)
或类似代码

由于使用“integer constant”的措辞可以解释为对整型常量的类型应用C规则,因此这可能足以避免我们在这里出现未定义的行为,但同样的问题(没有这样容易解决的问题)也适用于
strtoll


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

在32位平台上
-2147483648
不是c89下的溢出,它对于和errno==0来说是很长的时间

直接引用标准

返回值 成功完成后,strtol()返回转换后的值,如果 任何如果无法执行任何转换,则返回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;
}
生成以下输出:

$ ./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

尽管我不能指出今天标准中的一点特殊措辞,但当我在20世纪90年代为4BSD编写
strtol
时,我非常确定这不应该设置
errno
,并且确保我不会。无论这是基于标准中的措辞,还是与某人的个人讨论,我都不记得了

为了避免溢出,这意味着必须非常小心地进行计算。我在
unsigned long
中做了这件事,并在各种BSD的
libc
源代码中包含了这条评论:


在某种程度上,我对C语言库中的这个动作和语言本身的语法之间的不对称感到恼火(在这种情况下,负数是两个独立的标记,
-
后跟数字,所以写
-217483648
意味着
-(217483648)
,变成
-(217483648U)
这当然是
217483648U
,因此是正的!(当然,假设是32位
int
;问题值因其他位大小而异。)

为什么不将问题更新为仅运行代码无法确定的问题,而不是仅在底部进行编辑。这样的问题本质上无法仅通过运行代码来回答。这是一个关于C语言要求的问题(请参见
语言律师
标签)这个问题是在comp.std.c这里提出的,c委员会成员劳伦斯·琼斯说,
errno
没有设置为
ERANGE
@R.。我建议你将问题的标题改为测试无法完成的内容,你只需在底部进行编辑。标题是溢出一词的正确用法。在大多数c例如,“X溢出了吗?”可以通过测试得到肯定的回答,但不能得到否定的回答;因为溢出通常会导致未定义的行为,测试永远无法确定溢出没有发生。在
strtol
的情况下,“溢出”如果发生这种情况,将是实现内部的,并且函数需要通过
errno
报告,因此这应该可以在任何方向进行测试,但是测试不是回答关于C语言的问题,而是关于特定实现的问题,该实现可能是正确的,也可能是不正确的。
$ ./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
    /*
     * 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.
     */