Я работаю с C и заметил интересное поведение при обработке больших целочисленных входных данных с помощью scanf. В частности, когда я ввожу число, превышающее максимальное значение, которое может быть сохранено в беззнаковом int, число кажется усеченным, чтобы уместиться в 32 бита беззнакового целого числа. Однако когда я использую size_t в 64-битной системе и ввожу значение больше 2^64 - 1, оно не усекается таким же образом — вместо этого оно просто сохраняет максимально возможное значение size_t.
#include <stdio.h>
int main() {
unsigned int num1;
size_t num2;
printf("Enter a large number for unsigned int: ");
scanf("%u", &num1);
printf("Stored value (unsigned int): %u\n", num1);
printf("Enter a large number for size_t: ");
scanf("%zu", &num2);
printf("Stored value (size_t): %zu\n", num2);
return 0;
}
Пример ввода:
For unsigned int: 12345678901234567890
For size_t: 123456789012345678901234567890
выход:
Для беззнакового целого числа: усеченное значение, не соответствующее входным данным (вероятно, меньшее и неправильное число).
Для size_t: максимальное значение 18446744073709551615 (самое большое 64-битное целое число без знака).
Почему scanf усекает входные данные при сохранении их в виде беззнакового целого числа, что приводит к потере данных, а при использовании size_t в 64-битной системе он просто сохраняет максимально возможное значение, если входные данные выходят за пределы диапазона? Как scanf по-разному справляется с этими ситуациями в зависимости от типа?
в частности, я хочу знать поведение scanf для всех типов с переполнением ввода.
Это лишь одна из причин, почему *scanf()
опасен: вы не можете знать, что он безопасен, если вы предварительно не проанализируете входные данные, что делает *scanf()
лишним.
@AndrewHenle Спасибо, теперь я понял поведение scanf
Поведение не определено стандартом C согласно C 2018 7.21.6.2 10 («… если результат преобразования не может быть представлен в объекте, поведение не определено»), но ваши наблюдения легко объясняются, если Функция scanf
работает с любым запросом на преобразование в беззнаковый целочисленный тип следующим образом:
uintmax_t
, используя функцию strtoumax
(или эквивалентную).strtoumax
, объекту назначения вызывающего объекта, используя запрошенный для него тип.Причина, по которой это приводит к наблюдаемому вами поведению, заключается в том, что strtoumax
указано (в C 2018 7.8.2.3 3) для получения максимального значения uintmax_t
, если правильное значение находится за пределами диапазона представимых значений. Итак, при преобразовании входных данных, находящихся за пределами представимого интервала, вы получаете это значение. Однако когда вы конвертировали 12345678901234567890
, оно было за пределами диапазона unsigned int
, но не за пределами диапазона uintmax_t
. Итак, оно было правильно преобразовано в значение uintmax_t
, а затем младшие биты этого значения uintmax_t
были скопированы в ваше unsigned int
.
В качестве проверки вы можете попробовать ввести 123456789012345678901234567890
для unsigned int
. Если выше описано, как scanf
работает, он будет производить UINTMAX_T
в unsigned int
.
Большое спасибо, я попробовал, в результате получилось (2^32)-1, что является максимальным значением для беззнакового целого числа.
scanf
, похоже, имеет разные реализации для этих двух разных спецификаторов.