Почему вы не можете просто проверить, равен ли errno ERANGE?

Я пытался правильно преобразовать массив символов в длинный с strtol, проверьте, было ли переполнение или underflow, а затем выполните int cast на long. По пути я заметил много кода, который выглядит так

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

Почему вы не можете просто сказать

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

из моего понимания, если происходит underflow или overflow, 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 != '')
 {
     // Handle Error
 }

 num = (int)result;
 return num;

если есть причина для первого, пожалуйста, дайте мне знать.

2 ответов


первый фрагмент кода-это просто неправильно, и я объясню, почему позже, но Сначала нам нужно.

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 что однозначно указывает на то, что 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. См. применимые Man-странице для деталей.


если вы называете

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

или

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

на 32-битной машине вы получите LONG_MIN или LONG_MAX на result, хотя ошибки не было.

как объяснил user3386109, один из способов обнаружения ошибок из strtol установить errno до 0. Другой способ-позволить ему дать вам конечный указатель и посмотреть на это. Есть три или четыре случая:--11-->

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

в зависимости от ваших потребностей, первые два или три случая может быть, рухнули вместе. Затем вам может потребоваться беспокоиться (a) о том, является ли" полностью допустимое число " представимым (которое вы можете проверить с помощью errno) и (Б) были ли какие-либо "конечные нематериальные символы" безобидными пробелами(которые, увы, strtol не проверяет вас, поэтому, если вы заботитесь, вам придется проверить себя).