9

我一直在尝试将 char 数组正确转换为 long with strtol,检查是否存在溢出或下溢,然后对 long 进行 int 转换。一路走来,我注意到很多看起来像这样的代码

if ((result == LONG_MAX || result == LONG_MIN) && errno == ERANGE)
{
   // Handle the error
}

为什么你不能只说

if(errno == ERANGE)
{
    // Handle the error
}

据我了解,如果发生下溢或溢出,两种情况下 errno 都设置为 ERANGE。那么前者真的有必要吗?单独检查 ERANGE 会不会有问题?

这是我的代码现在的样子

 char *endPtr;
 errno = 0;
 long result = strtol(str, &endPtr, 10);

 if(errno == ERANGE)
 {
     // Handle Error
 }
 else if(result > INT_MAX || result < INT_MIN)
 {
    // Handle Error
 }
 else if(endPtr == str || *endPtr != '\0')
 {
     // Handle Error
 }

 num = (int)result;
 return num;

如果有前者的原因,请告诉我。

4

2 回答 2

8

第一个代码片段完全是错误的,我稍后会解释原因,但首先我们需要一些背景知识。

errno线程局部变量。当系统调用或某些库函数失败时,它被设置为非零值。当系统调用成功时它保持不变。所以它总是包含上次失败的调用的错误号。

这意味着您有两个选择。在每次调用之前设置errno为 0,或者使用标准的errno. 这是标准成语的伪代码

if ( foo() == some_value_that_indicates_that_an_error_occurred )
    then the value in errno applies to foo
else
    foo succeeded and the errno must be ignored because it could be anything

大多数程序员会使用标准的习惯用法,因为errno在每次系统调用之前设置为 0 是烦人且重复的。更不用说您可能会忘记errno在一个真正重要的地方设置为 0。


回到第一个代码片段。这是错误的,因为没有返回值strtol明确表示strtol失败。如果strtol返回LONG_MAX,则可能是发生了错误,或者字符串实际上包含数字LONG_MAX。没有办法知道strtol调用是成功还是失败。这意味着标准习语(这是第一个代码片段试图实现的)不能与strtol.

strtol正确使用,需要errno在调用前设置为0,像这样

errno = 0;
result = strtol( buffer, &endptr, 10 );
if ( errno == ERANGE )
{
    // handle the error
    // ERANGE is the only error mentioned in the C specification
}
else if ( endptr == buffer )
{
    // handle the error
    // the conversion failed, i.e. the input string was empty,
    // or only contained whitespace, or the first non-whitespace 
    // character was not valid
}

请注意,某些实现定义了其他非零值errno。有关详细信息,请参阅适用的手册页。

于 2016-03-18T02:33:04.783 回答
3

如果你打电话

result = strtol("-2147483648", NULL, 0);

或者

result = strtol("2147483647", NULL, 0);

在 32 位机器上,您将得到LONG_MINLONG_MAXin result,即使没有出现错误。

正如 user3386109 解释的那样,检测错误的一种方法strtol是首先设置errno为 0。另一种方法是让它给你一个结束指针并查看它。有三四种情况:

char *endptr;
long int result = strtol(str, &endptr, 10);
if(*str == '\0') {
    /* str was empty */
} else if(endptr == str) {
    /* str was completely invalid */
} else if(*endptr != '\0') {
    /* numeric result followed by trailing nonnumeric character(s) */
} else {
    /* str was a completely valid number (perhaps with leading whitespace) */
}

根据您的需要,前两个或三个案例可能会折叠在一起。errno然后您可能需要担心 (a ) “完全有效的数字strtol”是否可表示(您可以使用t检查你,所以如果你关心你必须检查自己)。

于 2016-03-18T02:46:02.583 回答