В чем разница между strtol и strtoul?

Я встретил незамеченный результат strtol в c

Вот пример программы.

#include <string.h>
#include <stdio.h>    
#include <stdlib.h>

int main()
{
    printf("%x\n", strtol("0xfffff70A", NULL, 0));
    return 0;
}

и вывод этой простой программы

0x7fffffff

а не 0xfffff70A. И если я использую strtoul, результат будет ровно 0xfffff70a. Я использую 32-битную машину, и мне интересно, что происходит. PS. Я использую gcc 4.7.2


person user1668903    schedule 23.05.2013    source источник
comment
Установите errno в 0 перед вызовом strtol или strtoul и проверьте это после. Не используйте это значение, если errno имеет другое значение.   -  person pmg    schedule 23.05.2013
comment
Кстати, хороший вопрос: рядом с минимальным примером кода, вводом, наблюдаемым выводом, ожидаемым выводом. +1 за то, что на это так легко ответить.   -  person DevSolar    schedule 23.05.2013


Ответы (3)


Из пункта 8 7.22.1.4 (из проекта N1570 редакция стандарта 2011 г.):

Если правильное значение находится за пределами диапазона представляемых значений, возвращается LONG_MIN, LONG_MAX, LLONG_MIN, LLONG_MAX, ULONG_MAX или ULLONG_MAX (в соответствии с типом возвращаемого значения и знаком значения, если он есть), а значение макроса ERANGE равно хранится в errno.

Поскольку правильное значение входной строки слишком велико для данного типа, вы получаете LONG_MAX, а errno устанавливается на ERANGE.

Всякий раз, когда одна из функций strto(u)l(l) возвращает одно из значений TYPE_MAX или TYPE_MIN, вам необходимо проверить errno, чтобы узнать, является ли это правильным результатом или введенные вами данные выходят за допустимые пределы.

person Daniel Fischer    schedule 23.05.2013
comment
Блин, я только собирался вставить то же самое. :-D @PascalCuoq: поведение функций strto... очень четко определено даже в случае переполнения. Это то, что делает их превосходящими, например. *scanf() функции для разбора числового ввода. - person DevSolar; 23.05.2013
comment
Можно еще уточнить, откуда вы цитируете? - person Olaf Dietsche; 23.05.2013
comment
@OlafDietsche: это будет ISO/IEC 9899 (стандарт языка C). - person DevSolar; 23.05.2013
comment
@DevSolar Не все это знают :-) - person Olaf Dietsche; 23.05.2013
comment
Напротив, почему (int)strtoul("-2",NULL,0) может вернуть правильное значение? - person user1668903; 23.05.2013
comment
@user1668903 user1668903 Из абзаца 5: если последовательность объектов начинается со знака минус, значение, полученное в результате преобразования, инвертируется (в возвращаемом типе). Часть "2" ввода анализируется как значение 2 типа unsigned long. Затем это значение инвертируется, что делает его ULONG_MAX + 1 - 2 (математический результат — уменьшение по модулю ULONG_MAX + 1 для получения значения в диапазоне [0, ULONG_MAX]). Затем это преобразуется в int способом, определяемым реализацией (при условии, что ULONG_MAX > INT_MAX). Обычно наименее значащие биты просто переинтерпретируются, и в дополнении до двух это -2. - person Daniel Fischer; 23.05.2013
comment
@DevSolar Вы имеете в виду ошибку, которая была в моем ответе всего две минуты! Вспомнил и сразу исправил. Вы правы, функции этого семейства имеют четко определенное поведение, пока им передается правильно сформированная строка. - person Pascal Cuoq; 23.05.2013
comment
@PascalCuoq: я был здесь всего минуту или около того. ;-) На самом деле функции strto*() хорошо определены даже для неправильных строк. Неважно, я думаю, что на этот вопрос был дан удовлетворительный ответ. ;-) - person DevSolar; 23.05.2013
comment
@DevSolar Вот использование strtol(), которое приводит к неопределенному поведению. Неопределенное поведение вызвано тем, что s не является правильно сформированной строкой. ideone.com/Qs2i7n - person Pascal Cuoq; 23.05.2013
comment
@PascalCuoq: Ах... Хорошо, эта неправильная форма. ;-) Выхожу из обсуждения, вы нашли способ сломать функцию. ;-) - person DevSolar; 23.05.2013

Вы сталкиваетесь с переполнением типа long, которое является подписанным.

Вероятно, вам следует использовать:

print("%lx\n", strtoul("0xfffff70a", NULL, 0));
                    ^
                    |
                 important!

вместо этого обратите внимание на «u» для «без знака» (см. страницу руководства).

Также обратите внимание, что вы не можете напечатать unsigned long с обычным %x, вам нужно квалифицировать его как большее, чем int.

person unwind    schedule 23.05.2013

Ваша архитектура имеет 32-битный тип long. 0xfffff70A не может быть представлено как 32-битное long со знаком. errno должно было быть установлено на ERANGE.

В дополнении до 2 представляемые значения для 32-битных целых чисел со знаком находятся в диапазоне от -0x80000000 до 0x7fffffff.

person Pascal Cuoq    schedule 23.05.2013