atoi - как определить разницу между нулем и ошибкой?


person Nahum    schedule 15.01.2012    source источник
comment
cplusplus.com ошибается насчет atoi, он вообще не обнаруживает ошибок. Не доверяйте этому сайту.   -  person Fred Foo    schedule 05.03.2013
comment
Стандарты C89, C99 и C11 ничего не говорят о значении, возвращаемом atoi(), когда правильное значение выходит за пределы допустимого диапазона. Стандарт C ++ 11 говорит еще меньше (он перечисляет atoi в двух таблицах, и все!). Заявления с сайта cplusplus.com, по сути, являются выдумкой желаемого за действительное и / или распространенными реализациями, не гарантированными никакими стандартами.   -  person Jonathan Leffler    schedule 31.08.2013
comment
TL; DR: используйте en.cppreference.com/w/cpp/string/byte / atoi вместо этого.   -  person Kuba hasn't forgotten Monica    schedule 03.09.2015


Ответы (3)


Это одна из причин, по которой atoi иногда считается небезопасным. Вместо этого используйте strtol / strtoul. И если он у вас есть, используйте strtonum.

Функция atoi более опасна, чем вы думаете. Стандарт POSIX гласит:

Если значение не может быть представлено, поведение не определено.

Стандарт C99 также говорит об этом:

7.20.1

Функции atof, atoi, atol и atoll не должны влиять на значение целочисленного выражения errno при ошибке. Если значение результата невозможно представить, поведение не определено.

person cnicutar    schedule 15.01.2012
comment
Не отвечает на вопрос заголовка. - person BeeOnRope; 24.11.2018

Как описано в @cnicutar и @ouah, atoi не может отличить действительный 0 от недопустимой строки, что делает семейство strtol лучшими вариантами.

Но Почему? и Как? Сначала поймите, что и atoi, и strtol преобразуют только начальный набор чисел в строке в числовые значения. Любые завершающие нечисловые символы просто игнорируются. strtol можно использовать для проверки недопустимых строк, поскольку помимо числового значения он также возвращает указатель на конец числовой части строки. Таким образом, если этот указатель end по-прежнему относится к началу исходной строки, вы можете сказать, что произошла ошибка и символы из строки не были преобразованы.

Как видно из примера кода, есть еще несколько тонкостей:

long lnum;
int num;
char *end;

errno = 0;

lnum = strtol(in_str, &end, 10);        //10 specifies base-10
if (end == in_str)     //if no characters were converted these pointers are equal
    fprintf(stderr, "ERROR: can't convert string to number\n");

//If sizeof(int) == sizeof(long), we have to explicitly check for overflows
if ((lnum == LONG_MAX || lnum == LONG_MIN) && errno == ERANGE)  
    fprintf(stderr, "ERROR: number out of range for LONG\n");

//Because strtol produces a long, check for overflow
if ( (lnum > INT_MAX) || (lnum < INT_MIN) )
    fprintf(stderr, "ERROR: number out of range for INT\n");

//Finally convert the result to a plain int (if that's what you want)
num = (int) lnum;

Примечание. Если вы уверены, что входная строка будет в допустимом диапазоне int, вы можете исключить lnum и просто напрямую привести strtol return: num = (int) strtolen(in_str, &end, 10);

person Bryan P    schedule 31.08.2013
comment
Там много хорошего, но ... если sizeof(int) == sizeof(long), вы не обнаружили переполнения. Вы должны установить errno = 0; перед вызовом strtol(), а затем вам нужно проверить ((lnum == LONG_MAX || lnum == LONG_MIN) && errno == ERANGE), чтобы определить переполнение. Несмотря на то, что он выполняет свою работу, его на самом деле очень сложно использовать правильно - даже сложнее, чем вы демонстрируете. - person Jonathan Leffler; 31.08.2013
comment
@JonathanLeffler Отличный момент. Я соответственно обновил свой ответ, спасибо. - person Bryan P; 31.08.2013
comment
Что касается if ( (lnum > INT_MAX) || (lnum < INT_MIN) ), для симметрии с long переполнением этот блок if() должен также errno = ERANGE; и установить lnum на INT_MAX или INT_MIN. - person chux - Reinstate Monica; 11.08.2017

Тебе нельзя.

atoi не может обнаружить ошибки. Если результат не может быть представлен, atoi вызывает неопределенное поведение. Используйте strtol вместо atoi.

Кодирование Secure CERT рекомендует использовать strtol вместо atoi, прочтите:

INT06-C. Используйте strtol () или связанную функцию для преобразования строкового токена в целое число

person ouah    schedule 15.01.2012