Следующий код
#include <stdio.h>
int main()
{
long long data = 0xFFFEABCD11112345;
char *pData = (char *)&data;
printf("Value at address %p is %x\n", pData, *pData);
pData = pData + 5;
printf("Value at address %p is %x\n", pData, *pData);
return 0;
}
выдает результат, аналогичный
Value at address 00000023515FFC00 is 45
Value at address 00000023515FFC05 is ffffffab
Учитывая, что pData — это char *, я ожидал, что второе значение будет ab вместо ffffffab. Я считаю, что виноват спецификатор формата %x, но я не до конца его понимаю. Откуда берутся ведущие f?
char — это подписанный тип данных на вашем компьютере. Следовательно, 0xab — отрицательное число. При преобразовании в int он становится 0xffffffab. Вместо этого используйте unsiged char.
Должен быть обман....
Проблема в том, что char имеет подпись, определяемую реализацией, см. связанный дубликат. В случае, если он подписан, расширение знака происходит всякий раз, когда этот char продвигается, поскольку 0xAB становится отрицательным представлением, когда char подписан. Куча ffff возникла из-за продвижения аргумента printf по умолчанию с (подписанного) char на int. Продвижение аргумента по умолчанию глупо и ничего не знает о спецификаторе формата %x, ожидающем беззнаковый параметр. (unsigned char)*pData дало бы ожидаемый результат, так как в ходе акции продление знака не происходит.
Э-э... и этот комментарий может быть более конкретным ответом, чем связанный дубликат. Может быть, я закрыл это преждевременно или, может быть, есть лучший дубликат, посвященный расширению знака?
Благодарю за ваш ответ. Ваш комментарий ответил на мой вопрос. Связанный дубликат аналогичен, но не совсем ответил на мой вопрос.
Честно говоря, вместо этого я снова открою и опубликую ответ. Хотя если кто-то найдет дубликат получше, я не против.
@Gerhardh Как насчет этого? stackoverflow.com/questions/3555791/…





char может быть подписанным или беззнаковым в зависимости от компилятора. Символ по умолчанию подписан или не подписан? В данном случае он выглядит подписанным.
На обычных компьютерах знак char может содержать только значения от -128 до 127. 0xAB на таком компьютере будет представлением дополнения до 2 десятичного отрицательного числа -85.
В C есть различные формы неявного повышения типов, которые происходят, когда в большинстве выражений используются небольшие типы, такие как char, или, как в этом случае, при передаче вариативной функции printf. Специальный набор неявных правил повышения для функций с переменным числом аргументов называется «повышением аргумента по умолчанию» и утверждает, что небольшие целочисленные типы повышаются до int независимо от знака.
Если у нас есть знак char со значением -85, то при повышении до int этот знак учитывается, что известно как расширение знака. Это означает, что значение по-прежнему равно -85, но двоичное представление расширенного int может быть 0xFFFFFFAB (при условии, что 32-битное целое число).
Однако если бы у нас был беззнаковый char со значением 0xAB/171, то при повышении до int значение просто сохраняется и никакого знака нет. Таким образом, мы могли бы избежать расширения знака, приведя: (unsigned char)*pData. Явное преобразование знака в беззнак четко определено.
Строка формата printf не имеет отношения к этой рекламной акции. %x ожидает параметр, который равен unsigned int, поэтому мы по сути лжем при печати, поскольку передаем char, повышенный до int, строго говоря, неопределенное поведение. Однако printf в этом случае просто считывает двоичное представление int и представляет его как 0xFFFFFFAB.
Еда на вынос:
char (или знаковых типов в целом) при работе с необработанными двоичными данными или аппаратном программировании — плохая идея. Используйте unsigned char или uint8_t.
Добро пожаловать в СО. Если вы хотите увидеть
ab, используйтеunsigned char. В вашей системеchar— это подписанный тип данных, знак которого расширяется доint, когда вы передаете егоprintf. Типcharповышается доintпри передаче функции без прототипа или функции с переменным числом аргументов.