为什么不能只检查 errno 是否等于 ERANGE?
Why can't you just check if errno is equal to ERANGE?
我一直在尝试使用 strtol
将 char 数组正确转换为 long,检查是否存在溢出或下溢,然后对 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 != '[=12=]')
{
// Handle Error
}
num = (int)result;
return num;
如果有前者的原因,请告诉我。
第一个代码片段完全错误,稍后我会解释原因,但首先我们需要一些背景知识。
errno
是一个 thread-local 变量。当系统调用或某些库函数失败时,它被设置为非零值。当系统调用成功时它保持不变。所以它总是包含上次失败调用的错误号。
这意味着你有两个选择。在每次调用之前将 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
中没有 return 值明确表示 strtol
失败。如果strtol
returns 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
定义了其他非零值。有关详细信息,请参阅适用的手册页。
如果你打电话
result = strtol("-2147483648", NULL, 0);
或
result = strtol("2147483647", NULL, 0);
在 32 位机器上,您将在 result
中得到 LONG_MIN
或 LONG_MAX
,即使没有错误。
正如 user3386109 所解释的,检测 strtol
错误的一种方法是先将 errno
设置为 0。另一种方法是让它给你一个结束指针并查看它。有三四种情况:
char *endptr;
long int result = strtol(str, &endptr, 10);
if(*str == '[=12=]') {
/* str was empty */
} else if(endptr == str) {
/* str was completely invalid */
} else if(*endptr != '[=12=]') {
/* numeric result followed by trailing nonnumeric character(s) */
} else {
/* str was a completely valid number (perhaps with leading whitespace) */
}
根据您的需要,前两个或三个案例可能会折叠在一起。然后您可能需要担心 (a) "completely valid number" 是否可表示(您可以使用 errno
进行测试),以及 (b) 任何 "trailing nonnumeric character(s)" 是否是无害的空白(唉, strtol
不会为您检查,所以如果您在乎,则必须自己检查)。
我一直在尝试使用 strtol
将 char 数组正确转换为 long,检查是否存在溢出或下溢,然后对 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 != '[=12=]')
{
// Handle Error
}
num = (int)result;
return num;
如果有前者的原因,请告诉我。
第一个代码片段完全错误,稍后我会解释原因,但首先我们需要一些背景知识。
errno
是一个 thread-local 变量。当系统调用或某些库函数失败时,它被设置为非零值。当系统调用成功时它保持不变。所以它总是包含上次失败调用的错误号。
这意味着你有两个选择。在每次调用之前将 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
中没有 return 值明确表示 strtol
失败。如果strtol
returns 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
定义了其他非零值。有关详细信息,请参阅适用的手册页。
如果你打电话
result = strtol("-2147483648", NULL, 0);
或
result = strtol("2147483647", NULL, 0);
在 32 位机器上,您将在 result
中得到 LONG_MIN
或 LONG_MAX
,即使没有错误。
正如 user3386109 所解释的,检测 strtol
错误的一种方法是先将 errno
设置为 0。另一种方法是让它给你一个结束指针并查看它。有三四种情况:
char *endptr;
long int result = strtol(str, &endptr, 10);
if(*str == '[=12=]') {
/* str was empty */
} else if(endptr == str) {
/* str was completely invalid */
} else if(*endptr != '[=12=]') {
/* numeric result followed by trailing nonnumeric character(s) */
} else {
/* str was a completely valid number (perhaps with leading whitespace) */
}
根据您的需要,前两个或三个案例可能会折叠在一起。然后您可能需要担心 (a) "completely valid number" 是否可表示(您可以使用 errno
进行测试),以及 (b) 任何 "trailing nonnumeric character(s)" 是否是无害的空白(唉, strtol
不会为您检查,所以如果您在乎,则必须自己检查)。