你为什么不能检查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_MINLONG_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唉没有检查你,所以,如果你关心你将不得不检查自己)。