根据strtol
的规范:
如果主题序列具有预期形式且base的值为0,则以第一个数字开头的字符序列应解释为整数常量。如果主题序列具有预期的形式且base的值在2到36之间,则它应被用作转换的基础,将其值归于每个字母,如上所述。如果主题序列以减号开头,则转换产生的值应予以否定。如果endptr不是空指针,则指向最终字符串的指针应存储在endptr指向的对象中。
目前的问题是,在否定之前,该值不在long
的范围内。例如,在C89中(整数常量不能采用类型long long
),写-2147483648
可能是溢出;你必须写(-2147483647-1)
或类似的。
由于使用“整数常量”的措辞可以被解释为对整数常量的类型应用C规则,这可能足以让我们从这里保存未定义的行为,但同样的问题(没有这么容易)适用于strtoll
。
编辑:最后,请注意即使它确实溢出,也应返回“正确”值。所以这个问题实际上只是关于在这种情况下是否可以设置errno
。
答案 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)
-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