Друк шістнадцяткових знаків на С


103

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

Наприклад, якщо у мене є рядок, тобто "0xc0 0xc0 abc123"перші два символи знаходяться 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, а не інші символи?


Рядок, що відповідає вашому масиву байтів, був би ..."\xc0\xc0abc123"
burito

Відповіді:


132

Ви бачите, 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 нижніх бітів, які ви хочете.


15
Моє рішення з використанням unsigned charатрибута на
lvella

1
Можливо, я можу допомогти. Це (технічно) невизначена поведінка, оскільки специфікатору xпотрібен непідписаний тип, але ch просувається до int. Правильний код буде просто відкидати ч до знака, або використовувати приведення до беззнаковим символам і специфікатор: hhx.
2501,

1
Якщо я маю printf("%x", 0), нічого не друкується.
Густаво Мейра

Він нічого не друкує, оскільки мінімум встановлено на 0. Щоб виправити це, спробуйте printf("%.2x", 0);збільшити мінімальний символ, намальований на 2. Щоб встановити максимум, додайте значення. з числом. Наприклад, ви можете примусити лише 2 символи, намальовані тим самимprintf("%2.2x", 0);
user2262111

Будь-яка причина , чому printf("%x", ch & 0xff)повинна бути краще , ніж просто використовувати printf("%02hhX", a)як в @ brutal_lobster - х відповіді ?
maxschlepzig

62

Дійсно, є перетворення типів до int. Крім того, ви можете примусити тип до знаку за допомогою специфікатора% hhx.

printf("%hhX", a);

У більшості випадків вам потрібно встановити мінімальну довжину, а також заповнити другий символ нулями:

printf("%02hhX", a);

ISO / IEC 9899: 201x говорить:

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


30

Ви можете створити неподписаний знак:

unsigned char c = 0xc5;

Друк він дасть, C5а ні ffffffc5.

Друкуються лише символи розміром більше 127, ffffffоскільки вони негативні (знак підписано).

Або ви можете передавати charчас друку:

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

3
+1 справжня найкраща відповідь, чітко вводячи якомога ближче до декларації даних (але не ближче).
Боб Штейн

13

Ви, ймовірно, зберігаєте значення 0xc0 у charзмінній, що, ймовірно, є підписаним типом, а ваше значення є негативним (найзначніший набір бітів). Потім під час друку він перетворюється на int, і щоб зберегти семантичну еквівалентність, компілятор додає додаткові байти з 0xff, тому мінус intматиме однакове числове значення вашого мінуса char. Щоб виправити це, просто зверніть увагу на unsigned charдрук:

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

13

Ви можете використовувати, 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аргумент.


Специфікатори невірні для типу uint8_t. Типи фіксованої ширини використовують спеціальні специфікатори друку. Дивіться:inttypes.h
2501

Так, але всі цілі числа varargs неявно просуваються до int.
Timmmm

Це може бути, але, наскільки визначено C, поведінка не визначена, якщо ви не використовуєте правильний специфікатор.
2501,

Але% x - правильний специфікатор. ( charі unsigned charпросуваються до int) [ en.cppreference.com/w/cpp/language/variadic_arguments] . Вам потрібно буде використовувати специфікатори PRI лише для речей, які не відповідають вашій платформі int- наприклад unsigned int.
Тиммммм

%xє правильним для неподписаного int not int. Типи char та неподписані char просуваються до int. Крім того, немає гарантії, що uint8_t визначається як неподписаний знак.
2501

2

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


-1

Спробуйте щось подібне:

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.