Печать шестнадцатеричных символов в C

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

Например, если у меня есть строка "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, но не другие символы?

Ответ 1

Вы видите 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 бит.

Ответ 2

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

printf("%hhX", a);

Ответ 3

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

unsigned char c = 0xc5;

Печать будет давать C5, а не ffffffc5.

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

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

char c = 0xc5; 
printf("%x", (unsigned char)c);

Ответ 4

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

printf("%x", (unsigned char)variable);

Ответ 5

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

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

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

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

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

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

Изменить 2: ( "когда-то-признать-вы ошибаетесь" ):

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

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

Ответ 6

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

Ответ 7

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

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