【问题标题】:Does strtol("-2147483648", 0, 0) overflow if LONG_MAX is 2147483647?如果 LONG_MAX 为 2147483647,strtol("-2147483648", 0, 0) 是否会溢出?
【发布时间】:2013-06-04 21:24:45
【问题描述】:

按照strtol的规范:

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

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

由于使用“整数常量”的措辞可以解释为将 C 规则应用于整数常量的类型,这可能足以让我们避免此处未定义的行为,但同样的问题(没有这么简单的问题)将适用于strtoll

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

【问题讨论】:

  • 为什么不将问题更新为仅通过运行代码无法确定的问题 - 而不是仅在底部进行编辑。
  • 像这样的问题本质上是无法通过运行代码来回答的。这是一个关于C语言要求的问题(见language-lawyer标签)。
  • 这个问题是在 comp.std.c 中提出的 groups.google.com/d/msg/comp.std.c/KOVzuLFen6Q/x2laO7KPCJ4J 和 C 委员会成员 Lawrence Jones 说 errno 没有设置为 ERANGE
  • @R.. 我建议您将问题的标题更改为无法通过测试来完成的事情,您只能在底部进行编辑。
  • 标题是溢出这个词的正确用法。在大多数情况下,“X 是否溢出?”通过测试可以得到肯定的回答,但不能得到否定的回答;由于溢出通常会导致未定义的行为,因此测试永远无法确定没有发生溢出。在strtol 的情况下,如果发生“溢出”,它将在实现内部,并且函数需要通过errno 报告,所以这应该可以在任一方向进行测试,但是测试不是回答有关 C 语言的问题,但有关特定实现的问题可能正确也可能不正确。

标签: c language-lawyer integer-overflow


【解决方案1】:

在 32 位平台上 -2147483648 不是 c89 下的溢出,它是 LONG_MIN 并且 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;
}

在 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

【讨论】:

    【解决方案2】:

    虽然我不能指出今天标准中的特定措辞,但当我在 1990 年代为 4BSD 写 strtol 时,我很确定这应该设置errno,并确保我不会。无论是基于标准中的措辞,还是与某人的个人讨论,我都记不得了。

    为了避免溢出,这意味着必须非常小心地进行计算。我是在 unsigned long 中完成的,并包含了这条评论(仍然在各种 BSD 的 libc 源中):

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-11
      • 2022-09-28
      • 2023-03-17
      • 1970-01-01
      • 2018-01-11
      • 1970-01-01
      相关资源
      最近更新 更多