Почему отрицательное число, преобразованное в size_t, становится огромным числом в C? - PullRequest
4 голосов
/ 15 апреля 2020

Когда я создаю простую программу, которая позволяет пользователю вводить число (size_t num), я не понимаю, почему ввод отрицательного числа приводит к огромному числу вместо сообщения об ошибке.

size_t num;
printf("enter num:");
scanf("%lu",&num);
printf("%lu",num);

Ответы [ 3 ]

4 голосов
/ 15 апреля 2020

Спецификатор формата %u фактически принимает строковое представление целого числа со знаком, а результат преобразуется в целое число без знака.

Раздел 7.21.6.2p12 стандарта C относительно функции fscanf (и, соответственно, scanf) говорит следующее о спецификаторе преобразования u:

Соответствует необязательному десятичному целому числу со знаком, формат которого совпадает с ожидаемым для предметной последовательности функции strtoul со значением 10 для аргумента base. Соответствующим аргументом должен быть указатель на целое число без знака.

Преобразование из знака в без знака происходит путем логического добавления максимального значения, которое тип без знака может содержать +1, к значению цифры 10 * * со знаком введите, пока результат не окажется в диапазоне без знака. Обратите внимание, что это происходит независимо от базового представления соответствующих целочисленных типов.

Так, например, предполагая, что size_t является 64-битным типом, наибольшее значение, которое он может содержать, равно 18446744073709551615. Поэтому, если вы введете -1 затем 18446744073709551616 добавляется к -1, чтобы получить 18446744073709551615, который является результатом.

Это преобразование задокументировано в разделе 6.3.1.3:

1 Когда значение с целочисленным типом преобразуется в другой целочисленный тип, отличный от _Bool, если значение может быть представлено новым типом, оно не изменяется.

2 В противном случае, если новый тип без знака, значение преобразуется путем многократного сложения или вычитания более одного максимального значения, которое может быть представлено в новом типе, до тех пор, пока значение не окажется в диапазоне нового типа.

3 В противном случае новый тип подписывается и значение не может быть представлено в нем; либо результат определяется реализацией, либо определяется сигнал реализации.

2 голосов
/ 15 апреля 2020

Спецификация для u преобразования в C 2018 7.21.6.2 12 гласит:

Соответствует необязательному десятичному целому числу со знаком, формат которого совпадает с ожидаемым для предметной последовательности функция strtoul со значением 10 для аргумента base. Соответствующим аргументом должен быть указатель на целое число без знака.

(модификатор l далее определяет его как unsigned long.)

Таким образом, знак разрешается, когда сканирование с %lu. Согласно абзацу 10:

... элемент ввода ... преобразуется в тип, соответствующий спецификатору преобразования.

Преобразования в unsigned long wrap по модулю ULONG_MAX + 1 Поэтому маленькие отрицательные значения преобразуются в большие положительные значения.

Кстати, чтобы отсканировать цифру в size_t, вы должны использовать %zu. Модификатор z специально для size_t.

1 голос
/ 15 апреля 2020

size_t являются unsigned. В двоичном представлении числа первый бит представляет знак (для signed int), поэтому, когда компьютер считывает число, думая, что это size_t, он не будет интерпретировать первый бит как отрицательный знак, а как часть номера. Поскольку это первый бит, то есть наибольшая степень двух, вы получите большое число. Вы можете прочитать больше о двоичном представлении здесь: https://en.wikipedia.org/wiki/Binary_number

Нет ошибки, потому что компьютер просто читает биты, указанные в памяти переменной, и это представляет действительный size_t, так что теперь у компьютера есть возможность узнать, что это неправильно.

...