C++ 正确使用strtol

C++ 正确使用strtol,c++,c,C++,C,下面的程序将字符串转换为long,但根据我的理解,它也会返回一个错误。我所依赖的事实是,如果strtol成功地将字符串转换为long,那么strtol的第二个参数应该等于NULL。当我用55运行下面的应用程序时,我得到以下消息 ./convertToLong 55 Could not convert 55 to long and leftover string is: 55 as long is 55 如何从strtol成功检测错误?在我的应用程序中,零是一个有效值 代码: #包括 #包括 静

下面的程序将字符串转换为long,但根据我的理解,它也会返回一个错误。我所依赖的事实是,如果
strtol
成功地将字符串转换为long,那么
strtol
的第二个参数应该等于NULL。当我用55运行下面的应用程序时,我得到以下消息

./convertToLong 55
Could not convert 55 to long and leftover string is: 55 as long is 55
如何从strtol成功检测错误?在我的应用程序中,零是一个有效值

代码:

#包括
#包括
静态长解析长(const char*str);
int main(int argc,字符**argv)
{
printf(“%s”长度为%ld\n),argv[1],parseLong(argv[1]);
返回0;
}
静态长解析长(const char*str)
{
长_val=0;
字符*温度;
_val=strtol(str和temp,0);
如果(温度!='\0')
printf(“无法将%s转换为长字符串,剩余字符串为:%s”,str,temp);
返回值;
}

你就快到了
temp
本身不会为空,但如果转换整个字符串,它将指向空字符,因此您需要取消引用它:

if (*temp != '\0')

你错过了一个间接层次。您要检查字符是否为终止的
NUL
,而不是指针是否为
NULL

if (*temp != '\0')
顺便说一下,这不是一个很好的错误检查方法。
strto*
函数族的正确错误检查方法不是通过比较输出指针和字符串结尾来完成的。应该通过检查零返回值并获取您应该检查的
errno

的返回值来完成

*temp != '\0'
您还应该能够在调用strotol后根据以下步骤检查errno的值:

RETURN VALUES
     The strtol(), strtoll(), strtoimax(), and strtoq() functions return the result
     of the conversion, unless the value would underflow or overflow.  If no conver-
     sion could be performed, 0 is returned and the global variable errno is set to
     EINVAL (the last feature is not portable across all platforms).  If an overflow
     or underflow occurs, errno is set to ERANGE and the function return value is
     clamped according to the following table.


       Function       underflow     overflow
       strtol()       LONG_MIN      LONG_MAX
       strtoll()      LLONG_MIN     LLONG_MAX
       strtoimax()    INTMAX_MIN    INTMAX_MAX
       strtoq()       LLONG_MIN     LLONG_MAX

请注意,以下划线开头的名称是为实现保留的;最好避免在代码中使用此类名称。因此,
\u val
应该只是
val

strtol()
及其相关函数的错误处理的完整规范非常复杂,令人惊讶的是,当您第一次遇到它时。有一件事你做得绝对正确,那就是使用函数调用strtol();在代码中使用它“原始”可能是不正确的

由于问题是用C和C++两种标记,我将引用C2011标准;你可以在C++标准中找到适合自己的措辞。 ISO/IEC 9899:2011§7.22.1.4

strtol
strtoll
strtoul
strtoull
功能
long int strtol(const char*restrict nptr,char**restrict endptr,int base)

^2[…]首先, 它们将输入字符串分解为三个部分:一个初始的,可能为空的序列 空白字符(由isspace函数指定),主题序列 类似于一个整数,以基数表示,基数由base的值决定,并且 一个或多个无法识别的字符的最终字符串,包括终止的null 输入字符串的字符。[……]

如果主题序列为空或没有预期的形式,则不允许转换 表演;
nptr
的值存储在
endptr
指向的对象中,提供
endptr
不是空指针

退换商品 8
strtol
strtoll
strtoul
strtoull
函数返回转换后的 价值,如果有的话。如果无法执行任何转换,则返回零。如果选择正确的值 超出了可表示值的范围,LONG_MIN,LONG_MAX,LLONG_MIN, 将返回LLONG_MAX、ULONG_MAX或ULLONG_MAX(根据返回类型 和值的符号(如果有的话),宏变量的值存储在
errno

请记住,没有标准的C库函数将
errno
设置为0。因此,为了可靠,必须在调用
strtol()
之前将
errno
设置为零

因此,您的
parseLong()
函数可能如下所示:

static long parseLong(const char *str)
{
    errno = 0;
    char *temp;
    long val = strtol(str, &temp, 0);

    if (temp == str || *temp != '\0' ||
        ((val == LONG_MIN || val == LONG_MAX) && errno == ERANGE))
        fprintf(stderr, "Could not convert '%s' to long and leftover string is: '%s'\n",
                str, temp);
        // cerr << "Could not convert '" << str << "' to long and leftover string is '"
        //      << temp << "'\n";
    return val;
}
你可以使用它,比如:

if (parseLong(str, &value))
    …conversion successful…
else
    …handle error…
如果需要区分“尾随垃圾”、“无效数字字符串”、“值太大”和“值太小”(以及“无错误”),可以使用整数或
enum
而不是布尔返回码。如果希望允许尾随空格但不允许其他字符,或者不允许任何前导空格,则函数中还有更多工作要做。该代码允许八进制、十进制和十六进制;如果希望严格使用十进制,则需要在调用
strtol()
时将0更改为10

如果您的函数要伪装为标准库的一部分,则它们不应将
errno
永久设置为
0
,因此您需要包装代码以保留
errno

int saved = errno;  // At the start, before errno = 0;

…rest of function…

if (errno == 0)     // Before the return
    errno = saved;
如何从strtol成功检测错误

标准C库规定/支持的3项测试:

  • 有转换吗

     if (str == endptr) puts("No conversion.");
    
  • 在射程内

     else if (errno == ERANGE) puts("Input out of long range.");
    
  • 跟踪垃圾

     else if (*endptr) puts("Extra junk after the numeric text.");
    
  • 成功

        else printf("Success %ld\n", val);
    

    输入像
    str==NULL
    base
    不是0,[2到36]是未定义的行为。各种实现(对C库的扩展)通过
    errno
    提供定义的行为和报告。我们可以增加第四个测试

        else if (errno) puts("Some implementation error found.");
    
    或者与
    errno==ERANGE
    测试结合使用


    还利用了常见实现扩展的简洁代码示例

    long my_parseLong(const char *str, int base, bool *success) {
        char *endptr = 0;
        errno = 0;
        long val = strtol(str, &endptr, base);
       
        if (success) {
          *success = endptr != str && errno == 0 && endptr && *endptr == '\0';
        }
        return val;
    }
    

    再次阅读文档;您还应该处理诸如溢出之类的错误。此外,对
    strto*
    函数的正确错误检查不是通过检查输出指针来完成的。应该通过检查一个零返回值和一个集合<代码> ErnOn/Cuth>,为什么不使用C++中的<代码> STD::Stoi ?(你添加了C++标签)@ BatchyX,它对于字符串“123ABC”(如一致意见)也不太有效。OP正在检查要转换的整个字符串。@chris:可以
        else printf("Success %ld\n", val);
    
        else if (errno) puts("Some implementation error found.");
    
    long my_parseLong(const char *str, int base, bool *success) {
        char *endptr = 0;
        errno = 0;
        long val = strtol(str, &endptr, base);
       
        if (success) {
          *success = endptr != str && errno == 0 && endptr && *endptr == '\0';
        }
        return val;
    }