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

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

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

c0 c0 61 62 63 31 32 33
, printf используя %x дает мне
ffffffc0 ffffffc0 61 62 63 31 32 33

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

7 ответов


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

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

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

вот решение:

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

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


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

printf("%hhX", a);

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

unsigned char c = 0xc5;

печать это даст C5, а не ffffffc5.

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

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

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

можно использовать 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, безопасно передавать char из-за целочисленного продвижения, которое происходит при вызове переменной функции.

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

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

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

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

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


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

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

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


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

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