你为什么不能检查errno是否等于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 != '\0') { // Handle Error } num = (int)result; return num;
如果前者有原因请告诉我。
第一个代码片段是完全错误的,我稍后会解释原因,但首先我们需要一些背景知识。
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
。
要正确使用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, ie 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位计算机上,即使没有出现错误,您也会得到LONG_MIN
或LONG_MAX
。
正如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) */ }
根据您的需要,前两个或三个案例可能会折叠在一起。 然后,您可能需要担心(a)“完全有效数字”是否可表示(您可以使用errno
进行测试),以及(b)是否有任何“尾随非数字字符”是无害的空格(唉, strtol
唉没有检查你,所以,如果你关心你将不得不检查自己)。