Próbuję przeczytać wiersz znaków, a następnie wydrukować szesnastkowy odpowiednik tych znaków.
Na przykład, jeśli mam ciąg, czyli "0xc0 0xc0 abc123"
gdzie pierwsze 2 znaki są c0
w szesnastkowym, a pozostałe abc123
w ASCII, to powinienem otrzymać
c0 c0 61 62 63 31 32 33
Jednak printf
użycie %x
daje mi
ffffffc0 ffffffc0 61 62 63 31 32 33
Jak uzyskać żądane wyniki bez "ffffff"
? I dlaczego tylko c0 (i 80) ma ffffff
, a pozostałe znaki nie mają?
"\xc0\xc0abc123"
Odpowiedzi:
Widzisz komunikat,
ffffff
ponieważchar
jest podpisany w Twoim systemie. W języku C funkcje vararg, takie jakprintf
promują wszystkie liczby całkowite mniejsze odint
doint
. Ponieważchar
jest to liczba całkowita (w twoim przypadku 8-bitowa liczba całkowita zeint
znakiem ), Twoje znaki są promowane za pomocą rozszerzenia ze znakiem.Ponieważ
c0
i80
mają początkowy 1-bitowy (i są ujemne jako 8-bitowa liczba całkowita), są one rozszerzane przez znak, podczas gdy inne w twojej próbce nie.Oto rozwiązanie:
To zamaskuje górne bity i zachowa tylko dolne 8 bitów, które chcesz.
źródło
unsigned char
to jedna instrukcja mniejsza w gcc4.6 dla x86-64 ...x
wymaga typu bez znaku, ale ch jest promowane do int. Poprawny kod po prostu rzucić ch do niepodpisane lub użyć oddanych do unsigned char i specyfikacją:hhx
.printf("%x", 0)
, nic nie jest drukowane.printf("%.2x", 0);
zwiększyć minimalną liczbę rysowanych znaków do 2. Aby ustawić maksimum, wstaw przedrostek. z liczbą. Na przykład możesz wymusić narysowanie tylko 2 znaków, wykonującprintf("%2.2x", 0);
printf("%x", ch & 0xff)
powinna być lepsza niż tuż przy użyciuprintf("%02hhX", a)
jako użytkownika @ brutal_lobster w odpowiedzi ?Rzeczywiście, istnieje konwersja typów na int. Możesz także wymusić char, używając specyfikatora% hhx.
W większości przypadków będziesz chciał ustawić również minimalną długość, aby wypełnić drugi znak zerami:
ISO / IEC 9899: 201x mówi:
źródło
Możesz utworzyć niepodpisany znak:
Wydruk da
C5
i nie daffffffc5
.Tylko znaki większe niż 127 są drukowane ze znakiem,
ffffff
ponieważ są ujemne (znak jest podpisany).Lub możesz przesłać
char
podczas drukowania:źródło
Prawdopodobnie przechowujesz wartość 0xc0 w
char
zmiennej, która prawdopodobnie jest typem ze znakiem, a twoja wartość jest ujemna (najbardziej znaczący zestaw bitów). Następnie, podczas drukowania, jest konwertowany naint
i aby zachować semantyczną równoważność, kompilator dopełnia dodatkowe bajty wartością 0xff, więc negatywint
będzie miał tę samą wartość liczbową co negatywchar
. Aby to naprawić, po prostu prześlij dounsigned char
podczas drukowania:źródło
Możesz użyć,
hh
aby powiedzieć,printf
że argument jest bez znaku. Użyj,0
aby uzyskać dopełnienie zerowe i2
ustawić szerokość na 2.x
lubX
dla małych / wielkich liter szesnastkowych.Edycja : Jeśli czytelnicy są zaniepokojeni stwierdzeniem 2501, że nie jest to w jakiś sposób „poprawne” specyfikatory formatu, sugeruję, aby ponownie przeczytali
printf
łącze . Konkretnie:Jeśli chodzi o jego punkt widzenia ze znakiem i bez znaku, w tym przypadku nie ma to znaczenia, ponieważ wartości zawsze muszą być dodatnie i łatwo zmieścić się w int ze znakiem. W każdym razie nie ma podpisanego specyfikatora formatu szesnastkowego.
Edytuj 2 : (wydanie „kiedy-przyznać-się mylisz”):
Jeśli przeczytasz aktualny standard C11 na stronie 311 (329 pliku PDF), znajdziesz:
źródło
inttypes.h
char
iunsigned char
są promowane doint
) [ en.cppreference.com/w/cpp/language/variadic_arguments] . Będziesz musiał używać specyfikatorów PRI tylko do rzeczy, które nie pasują do twojej platformyint
- npunsigned int
.%x
jest poprawne dla unsigned int not int. Typy char i unsigned char są promowane do int. Ponadto nie ma gwarancji, że uint8_t jest zdefiniowany jako znak bez znaku.Prawdopodobnie drukujesz z tablicy znaków ze znakiem. Wydrukuj z tablicy bez znaku lub zamaskuj wartość za pomocą 0xff: np. Ar [i] & 0xFF. Wartości c0 są rozszerzane ze znakiem, ponieważ ustawiony jest bit wysokiego (znaku).
źródło
Spróbuj czegoś takiego:
Który to produkuje:
źródło