Почему `char` не теряет данные, когда ему присваивается `int`?

Я начинающий программист на C. Я определил две переменные int x = 0xffffffff и char y = x, после чего распечатал их с помощью функции printf("%x\n", y); Я ожидал на терминале чего-то вроде ff, но вместо этого на экране появилось ffffffff. Мне интересно, как char может хранить 4 байта памяти, если размер хранилища символа равен 1.

#include <stdio.h>

int main(void) {
    int x = 0xffffffff;
    char y = x;
    printf("%x\n", y);

    return 0;
}

Если вы передаете переменную меньшего размера, чем int, в вариативную функцию, например printf, она автоматически повышается до int. Функция даже не знает, что вы передали char.

Weather Vane 25.07.2024 21:37

Это потому, что %x предназначен для печати int. Таким образом, он берет ваш char, который подписан в вашем случае и является отрицательным, и расширяет его знак до int того же значения (-1).

Eugene Sh. 25.07.2024 21:37

Проще говоря, 0xffffffff — это представление значения -1 для типа int. Для типа char-1 обозначается 0xff. В обоих типах -1 является представимым, поэтому при его назначении туда и обратно данные не теряются. Потеря произойдет, если значение не представимо ни в одном из типов (подумайте о диапазоне типа).

Eugene Sh. 25.07.2024 21:43

используйте это printf("%hhx\n", y);

Null 25.07.2024 21:46

@emre, это работает! Кстати, что означает спецификатор формата %hhx? Спасибо за ваше предложение.

Greeshma 25.07.2024 21:49

h в %hx означает сокращение или половину unsigned int. повторение как %hhx означает еще короче, следовательно, unsigned char. Значение преобразуется внутри с помощью printf в (unsigned short) или (unsigned char) соответственно перед преобразованием строки. Однако обратите внимание, что стандарт C поддерживает архитектуры, в которых 2 или даже все 3 типа char, short и int имеют одинаковый размер.

chqrlie 25.07.2024 22:44

... и даже в этом случае значение char все равно повышается до int при передаче. Даже если формат %c, это означает, что вы можете передать int в %c.

Weather Vane 25.07.2024 22:55

Если вы измените строку int x = 0xffffffff; на int x = 0xeeeeeeee;, то увидите, что данные действительно теряются. См. эту ссылку для демонстрации.

Andreas Wenzel 26.07.2024 00:40
Стоит ли изучать 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
8
115
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

int x = 0xffffffff; — это поведение, определяемое реализацией, поскольку 0xFFFFFFFF больше, чем 2^31-1 в системе, где int — 32-битная система. Размер char и int определяется реализацией.

Его реализация определяется, если char равно signed или unsigned:

  • Если char равно signed, то char y = x присваивает значение -1x. При вызове printf()char преобразуется в int, размер которого реализован. В системе, где int составляет 32 бита, это кодируется как дополнение до двух 0xFFFFFFFF (начиная с C 2023; в предыдущих версиях это было определено), которое печатается ffffffff.

  • Если char равен unsigned, то он будет печататься ff на платформах, где char равен 8 битам.

Спецификатор формата %x ожидал unsigned int, поэтому вам действительно следует привести его как таковое перед печатью для четко определенного поведения. В любом случае %x не будет генерировать префикс 0x и выводить строчные буквы.

Вы также можете упомянуть поведение, определяемое реализацией в int x = 0xffffffff; в 32-битных системах. Кроме того, выходные данные не будут иметь префикса 0x и использовать заглавные буквы для шестнадцатеричных цифр f. Для полноты картины код также может работать на платформах, где char не имеет знака и имеет 32 бита, и будет выводить ffffffff, и определенно так и должно быть, если тип int имеет 32 бита значения или более.

chqrlie 25.07.2024 22:36

@chqrlie Отредактировано, как было предложено (пожалуйста, сообщите мне еще раз, если я что-то пропустил). Если я это сделаю, gcc выдаст мне результат unsigned char y = x. У вас есть рефери, который может сказать обратное?

Allan Wind 26.07.2024 03:19

случай, о котором я говорил, довольно крайний: количество битов в unsigned char определяется реализацией. Оно должно быть не менее 8, а на большинстве современных платформ оно равно 8, поэтому, если char без знака, на этих платформах вывод будет ff, но на некоторых DSP с более крупными типами char вывод может быть ffff или даже ffffffff.

chqrlie 26.07.2024 08:50

@chqrlie Спасибо. Уточнил и эту часть. Оставьте комментарий, поскольку это полезный контекст, но полная информация запутывает ответ.

Allan Wind 26.07.2024 08:56
Ответ принят как подходящий

На самом деле здесь происходит довольно много всего. Первый:

int x = 0xffffffff;

Целочисленная константа 0xffffffff имеет значение 4 294 967 295 (т. е. 232-1) и тип unsigned int, при условии, что int 32-битное.

Это значение unsigned int затем преобразуется в int для инициализации. Он подвергается преобразованию, определяемому реализацией, которое в большинстве случаев приводит к присвоению значения -1. Это значение имеет то же представление, что и исходное значение в дополнении до двух.

Затем в этой строке:

char y = x;

Мы инициализируем y значением -1. Это значение находится в диапазоне char (при условии, что char подписано), поэтому в этом случае при преобразовании из int в char изменение значения не происходит. Предполагая представление дополнения до двух, это обозначается как 0xff.

Затем, когда вы печатаете:

printf("%x\n", y);

Поскольку printf является переменной функцией, аргумент y, имеющий тип char, преобразуется в тип int перед передачей в функцию. Как и прежде, оба типа могут хранить значение -1, поэтому значение не меняется.

Спецификатор формата %x ожидает аргумент типа unsigned int. Поскольку вместо int был передан unsigned int, это технически неопределенное поведение, однако маловероятно, что несоответствие знака/беззнака вызовет проблему в какой-либо реальной реализации.

В этом случае значение int -1 имеет представление 0xffffffff, которое при интерпретации как unsigned int будет иметь это значение, поэтому ffffffff - это то, что печатается.

Итак, чтобы ответить на ваш вопрос:

как char может хранить 4 байта памяти, если размер хранилища char равен 1.

Это не так. Это просто так кажется из-за преобразований и представлений соответствующих типов.

Предполагая представление дополнения до двух, это обозначается как 0xff. Боюсь, эта часть все еще немного сбивает с толку: если char подписано, значение равно -1, которое в системах, использующих представление с дополнением до двух, хранится в байте со всеми установленными битами. Отладчик покажет значение байта FF, но printf выведет ffffffff из-за правила целочисленного продвижения, как описано.

chqrlie 25.07.2024 22:48

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