【问题标题】:Is it better to implement strtol() without errno?在没有errno的情况下实现strtol()会更好吗?
【发布时间】:2018-12-09 05:30:11
【问题描述】:

传统的strtol()通常是这样使用的:

int main()
{
    errno = 0;
    char *s = "12345678912345678900";
    char *endptr;
    long i = strtol(s,  &endptr, 10);
    if(i == LONG_MAX && errno == ERANGE) 
        printf("overflow");
}

我们需要访问errno 两次,而现在errno 通常是一个C 宏最终扩展为一个函数。考虑到将字符串解析为整数并不是一项繁重的工作,这似乎有点贵。

那么,在没有errno 的情况下实现strtol 而是使用其他方式来指示溢出是否更好?

喜欢:

long strtol(const char *nptr, char **endptr, int base, bool *is_overflow);

而不是

long strtol(const char *nptr, char **endptr, int base);

【问题讨论】:

  • 我不确定问题是什么。你不实现 strtol,你使用它。您是在问是否应该重新实现 strtol 以使其不使用 errno?
  • 我相信 Posix 需要 errno 所以它不是真的可行。此外,您将把大部分时间花在转换算法上。转换可能在 O(n) 中运行。设置 errnoO(1) 中运行。设置errno 几乎是微不足道的。
  • 如果没有errno,怎么知道没有溢出?
  • @ShmuelH。它是一个左值(一个返回指针的函数,伪装成一个变量。)
  • 相关,这里是libc的strtol.c implementation

标签: c linux string strtol


【解决方案1】:

在没有errno的情况下实现strtol会更好...

没有。

...但是用其他方式表示溢出?

没有。

long int strtol(const char * restrict nptr, char ** restrict endptr, int base);

strtol() 是标准 C 库函数,任何实现都必须遵守正确使用 3 个输入和 errno 的要求。


当然,OP 可以根据需要实现一些其他的my_strtol()

任何与避免 errno 相关的性能问题都是微优化,但却是合理的设计目标。

这真的归结为如何将字符串的问题传达给long

  • 溢出"12345678912345678901234567890"

  • 没有转化"abc"

  • 多余的垃圾"123 abc"

  • 允许前导空格,允许尾随空格?

  • 允许各种碱基?

一旦定义了有关所有异常情况的功能,而不仅仅是溢出,那么有关errno 的编码问题就会很有用,即使不太可能带来任何有意义的性能改进。

IMO,仅编码到一个基础可能比errno 更有效地加快改进速度。


OP 代码不是强大的strtol() 用法。建议:

char *s = "12345678912345678900";

char *endptr;
errno = 0;
long i = strtol(s,  &endptr, 10);
if (errno == ERANGE) printf("Overflow %ld\n", i);
else if (s == endptr) printf("No conversion %ld\n", i);
else if (*endptr) printf("Extra Junk %ld\n", i);
else printf("Success %ld\n", i);

【讨论】:

    【解决方案2】:

    strtol() 中除了errno 之外实际上还有一些开销,比如跳过空格、处理基数(10 或 hexa)、检查字符...

    特定环境中速度至关重要并且您知道所提供的字符串是以10为底的数字适合long,您可以自己制作快速函数,比如

    #include <ctype.h>
    
    long mystrtol(char *s) {
       long res = 0, minus = *s == '-';
       if (minus || *s == '+') s++;
    
       while (isdigit(*s)) {
          res = res*10 + (*s++ - '0');
       }
    
       return minus ? -res : res;
    }
    

    并选择内联它。

    【讨论】:

    • Nit,但 +10 也是可以处理的有效数字表示。在极端情况下,使用标准库函数比使用自己的函数更可取。
    • 角落问题:isdigit(*s) 是技术 UB,而 *s &lt; 0 (cast to unsigned char) 。此外,当目标结果在LONG_MIN 的范围内时,res = res*10 + (*s++ - '0'); 是 UB(有符号整数溢出)。在这些情况下,通常的 UB 是可以的,但仍然是 UB。
    • 除了已经列出的问题之外,该函数应该是 const-correct,即使用const。许多合理的编码标准需要指向以这种方式声明的字符串文字的指针(这在大多数平台上都是很好的做法)。此外,前提充其量也是值得怀疑的。标准库函数通常实现高效,直至优化的汇编代码。此外,原件的灵活性可能对用户来说是一个很好的额外好处(例如,能够使用其他基地)。
    • @chux 不是 技术 UB 而是 如果输入负字节,则在任何使用带符号字符的平台上完全不可预测的行为的严重 UB...
    • isdigit的正确用法如下:isdigit((unsigned char)*s).
    猜你喜欢
    • 1970-01-01
    • 2021-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-30
    • 2013-10-15
    • 1970-01-01
    相关资源
    最近更新 更多