Почему ввод, превышающий unsigned int, приводит к потере данных, но не для size_t в 64-битной системе?

Я работаю с 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 для всех типов с переполнением ввода.

port70.net/~nsz/c/c11/n1570.html#7.21.6.2p10 кажется, предполагает, что поведение этого параметра не определено. Таким образом, конкретный scanf, похоже, имеет разные реализации для этих двух разных спецификаторов.
Eugene Sh. 09.08.2024 17:48

Это лишь одна из причин, почему *scanf() опасен: вы не можете знать, что он безопасен, если вы предварительно не проанализируете входные данные, что делает *scanf() лишним.

Andrew Henle 09.08.2024 18:49

@AndrewHenle Спасибо, теперь я понял поведение scanf

Nalan PandiKumar 09.08.2024 18:50
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
3
52
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Поведение не определено стандартом 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, что является максимальным значением для беззнакового целого числа.

Nalan PandiKumar 09.08.2024 18:06

Другие вопросы по теме