Печать шестнадцатеричных символов в C - PullRequest
83 голосов
/ 09 ноября 2011

Я пытаюсь прочитать строку символов, а затем распечатать шестнадцатеричный эквивалент символов.

Например, если у меня есть строка "0xc0 0xc0 abc123", где первые 2 символа c0 в шестнадцатеричном формате, а остальные символы abc123 в ASCII, тогда я должен получить

c0 c0 61 62 63 31 32 33

Однако, printf с использованием %x дает мне

ffffffc0 ffffffc0 61 62 63 31 32 33

Как получить желаемый результат без "ffffff"? И почему только у c0 (и 80) есть ffffff, но нет других символов?

Ответы [ 7 ]

110 голосов
/ 09 ноября 2011

Вы видите ffffff, потому что char подписано в вашей системе.В C такие функции vararg, как printf, будут преобразовывать все целые числа, меньшие от int до int.Поскольку char является целым числом (8-разрядное целое число со знаком в вашем случае), ваши символы повышаются до int через расширение знака.

Поскольку c0 и 80 имеют ведущий 1-бит (и являются отрицательными как 8-разрядное целое число), они расширяются до знака, в то время как другие в вашем образце - нет.

char    int
c0 -> ffffffc0
80 -> ffffff80
61 -> 00000061

Вот решение:

char ch = 0xC0;
printf("%x", ch & 0xff);

Это замаскирует старшие биты и оставит только те младшие 8 битов, которые вам нужны.

55 голосов
/ 09 ноября 2011

Действительно, есть преобразование типов в int. Также вы можете принудительно указать тип с помощью спецификатора% hhx.

printf("%hhX", a);

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

printf("%02hhX", a);

ИСО / МЭК 9899: 201x гласит:

7 Модификаторы длины и их значения: hh Указывает, что следующий спецификатор преобразования d, i, o, u, x или X применяется к аргумент char со знаком или без знака (аргумент будет иметь был продвинут в соответствии с целочисленными акциями, но его значение должно быть перед печатью преобразуется в подписанный или неподписанный символ); или это следующее

26 голосов
/ 09 ноября 2011

Вы можете создать неподписанный символ:

unsigned char c = 0xc5;

Печать даст C5, а не ffffffc5.

Только символы больше 127 печатаются с ffffff, потому что они отрицательны (символ подписан).

Или вы можете разыграть char во время печати:

char c = 0xc5; 
printf("%x", (unsigned char)c);
11 голосов
/ 13 октября 2015

Вы можете использовать hh, чтобы сообщить printf, что аргумент является беззнаковым символом. Используйте 0, чтобы получить заполнение нулями, и 2, чтобы установить ширину 2. x или X для шестнадцатеричных символов в нижнем / верхнем регистре.

uint8_t a = 0x0a;
printf("%02hhX", a); // Prints "0A"
printf("0x%02hhx", a); // Prints "0x0a"

Редактировать : Если читатели обеспокоены утверждением 2501 о том, что это как-то не «правильный» спецификатор формата, я предлагаю снова прочитать printf ссылку . В частности:

Несмотря на то, что% c ожидает аргумент int, безопасно передавать символ из-за целочисленного преобразования, которое происходит при вызове функции с переменным числом.

Правильные спецификации преобразования для типов символов фиксированной ширины (int8_t и т. Д.) Определены в заголовке <cinttypes> (C ++) или <inttypes.h> (C) (хотя PRIdMAX, PRIuMAX и т. Д. Являются синонимами % jd,% ju и т. д.) .

Что касается его замечания о подписанном и неподписанном, то в этом случае это не имеет значения, поскольку значения всегда должны быть положительными и легко помещаться в подписанном int. В любом случае, в формате шестнадцатеричного формата со знаком нет подписи.

Редактировать 2 : (издание «когда признаешь, что ошибаешься»):

Если вы прочитаете фактический стандарт C11 на стр. 311 (329 из PDF), вы найдете:

чч: указывает, что следующий аргумент преобразования d, i, o, u, x или X применяется к аргументу signed char или unsigned char (аргумент будет повышен в соответствии с целочисленными повышениями, но его значение должно быть преобразовано в signed char или unsigned char перед печатью); или что следующий указатель преобразования n применяется к указателю на аргумент signed char.

11 голосов
/ 09 ноября 2011

Вы, вероятно, сохраняете значение 0xc0 в переменной char, которая, вероятно, является типом со знаком, а ваше значение является отрицательным (установлен старший значащий бит).Затем при печати он преобразуется в int, и для сохранения семантической эквивалентности компилятор дополняет байты 0xff, поэтому отрицательное значение int будет иметь такое же числовое значение, что и ваше отрицательное значение char.Чтобы это исправить, просто наберите unsigned char при печати:

printf("%x", (unsigned char)variable);
2 голосов
/ 09 ноября 2011

Вы, вероятно, печатаете из массива символов со знаком. Либо выведите из массива unsigned char, либо замаскируйте значение с помощью 0xff: например, ar [i] & 0xFF. Значения c0 расширяются со знаком, потому что установлен старший (знаковый) бит.

0 голосов
/ 09 ноября 2011

Попробуйте что-то вроде этого:

int main()
{
    printf("%x %x %x %x %x %x %x %x\n",
        0xC0, 0xC0, 0x61, 0x62, 0x63, 0x31, 0x32, 0x33);
}

Который производит это:

$ ./foo 
c0 c0 61 62 63 31 32 33
...