C'de onaltılık karakterleri yazdırma


104

Bir satır karakter okumaya çalışıyorum, sonra karakterlerin onaltılık eşdeğerini yazdırıyorum.

Örneğin "0xc0 0xc0 abc123", ilk 2 karakterin c0onaltılık olduğu ve kalan karakterlerin abc123ASCII olduğu bir dizem varsa, o zaman şunu almalıyım

c0 c0 61 62 63 31 32 33

Ancak printfkullanmak %xbana

ffffffc0 ffffffc0 61 62 63 31 32 33

Olmadan istediğim çıktıyı nasıl elde ederim "ffffff"? Ve neden sadece c0 (ve 80) 'de var ffffffama diğer karakterler yok?


Bayt "\xc0\xc0abc123"
dizinizle

Yanıtlar:


133

ffffffÇünkü charsisteminizde imzalanmış olduğunu görüyorsunuz . C gibi vararg fonksiyonları printfdaha bütün tamsayılar küçük teşvik edecektir intetmek int. Yana charbir tamsayı (sizin durumda 8 bitlik işaretli tamsayı) 'dir, sizin karakter terfi ediliyor intoturum uzantısı üzerinden.

Yana c0ve 80öncü 1-bit (ve bir 8 bitlik tamsayı olarak negatif) sizin örnekteki diğerleri yapmadığı halde, onlar oturum genişletilmiş ediliyor.

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

İşte bir çözüm:

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

Bu, üst bitleri maskeleyecek ve yalnızca istediğiniz alt 8 biti tutacaktır.


15
Bir döküm kullanarak Çözümümün unsigned charx86-64 için gcc4.6 bir talimat küçüktür ...
lvella

1
Belki yardım edebilirim. Bu (teknik olarak) tanımsız bir davranıştır çünkü belirteç xişaretsiz bir tür gerektirir, ancak ch, int'e yükseltilir. Doğru kod basitçe imzasız için ch döküm veya imzasız char ve belirtici bir döküm kullanırsınız: hhx.
2501

1
Varsa printf("%x", 0), hiçbir şey yazdırılmaz.
Gustavo Meira

Minimum 0 olarak ayarlandığından hiçbir şey yazdırmaz. Bunu düzeltmek için, printf("%.2x", 0);çizilen minimum karakterleri 2'ye yükseltmeyi deneyin . Bir maks. Ayarlamak için, başlığına. bir numara ile. Örneğin, yaparak çizilen yalnızca 2 karakteri zorlayabilirsinizprintf("%2.2x", 0);
user2262111

Herhangi bir neden printf("%x", ch & 0xff)sadece kullanmaktan daha iyi olmalıdır printf("%02hhX", a)@ brutal_lobster en olduğu gibi cevap ?
maxschlepzig

62

Aslında, int türüne tür dönüşümü vardır. Ayrıca% hhx belirleyicisini kullanarak yazıyı karaktere zorlayabilirsiniz.

printf("%hhX", a);

Çoğu durumda, ikinci karakteri sıfırlarla doldurmak için minimum uzunluğu da ayarlamak isteyeceksiniz:

printf("%02hhX", a);

ISO / IEC 9899: 201x diyor ki:

7 Uzunluk değiştiriciler ve anlamları şöyledir: hh Aşağıdaki d, i, o, u, x veya X dönüşüm tanımlayıcısının işaretli bir karakter veya işaretsiz karakter bağımsız değişkenine uygulandığını belirtir (bağımsız değişken, tamsayı yükseltmelerine göre yükseltilecektir, ancak değeri, yazdırılmadan önce imzalı karaktere veya imzasız karaktere dönüştürülür); ya da aşağıdaki


30

İmzasız bir karakter oluşturabilirsiniz:

unsigned char c = 0xc5;

Basmak verecek C5ve vermeyecek ffffffc5.

ffffffNegatif oldukları için yalnızca 127'den büyük karakterler yazdırılır (karakter imzalanır).

Veya charbaskı sırasında yayınlayabilirsiniz:

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

3
Gerçek en iyi yanıtı + 1'leyin, veri bildirimine olabildiğince yakın açık yazarak (ancak daha yakın değil).
Bob Stein

13

Muhtemelen 0xc0 değerini bir chardeğişkende depoluyorsunuz , bu büyük olasılıkla işaretli türdür ve değeriniz negatiftir (en önemli bit kümesi). Ardından, yazdırırken, dönüştürülür intve anlamsal eşdeğerliği korumak için, derleyici ekstra baytları 0xff ile doldurur, böylece negatif, negatifinizle intaynı sayısal değere sahip olur char. Bunu düzeltmek için unsigned char, yazdırırken şunu yazın:

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

13

Argümanın işaretsiz bir karakter olduğunu hhsöylemek için kullanabilirsiniz printf. Kullanım 0sıfır doldurma almak ve 22'ye genişliğini ayarlamak için xveya Xdaha düşük / büyük harf heks karakterler için.

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

Düzenleme : Okuyucular, 2501'in bunun bir şekilde 'doğru' biçim tanımlayıcıları olmadığına dair iddiasından endişe duyuyorlarsa, printfbağlantıyı yeniden okumalarını öneririm . Özellikle:

% C, int bağımsız değişkenini beklese de, bir variadic işlev çağrıldığında gerçekleşen tamsayı yükseltmesi nedeniyle bir char iletmek güvenlidir.

Sabit genişlikli karakter türleri (int8_t, vb.) İçin doğru dönüştürme özellikleri, başlıkta <cinttypes>(C ++) veya <inttypes.h>(C) tanımlanır (PRIdMAX, PRIuMAX vb.% Jd,% ju, vb . İle eşanlamlıdır) .

İşaretli ve işaretsiz hakkındaki düşüncesine gelince, bu durumda önemli değildir çünkü değerler her zaman pozitif olmalı ve işaretli bir int'e kolayca sığmalıdır. Yine de imzalı onaltılık biçim belirticisi yok.

Düzenleme 2 : ("ne zaman-yanılıyorsunuz?" Baskısı):

Gerçek C11 standardını 311. sayfada okursanız (PDF'nin 329'u) şunları bulursunuz:

ss: belirtir, takip eden bir bu d, i, o, u, x, veya Xdönüşüm belirteci bir geçerli olduğu signed charya da unsigned charbağımsız değişken (değeri bir tamsayı promosyonlara göre teşvik edilmiş olacaktır, ama değeri çevrilir signed charveya unsigned charbaskı öncesi); veya aşağıdaki ndönüşüm belirticisinin bir signed charbağımsız değişkene işaretçi için geçerli olduğu .


Belirteçler, uint8_t türü için doğru değil. Sabit genişlik türleri özel yazdırma belirticileri kullanır. Bkz:inttypes.h
2501

Evet ama tüm varargs tamsayıları dolaylı olarak int'e yükseltilir.
Timmmm

Olabilir, ancak C tanımlandığı kadarıyla, doğru belirticiyi kullanmazsanız davranış tanımsızdır.
2501

Ama% x olan doğru belirteci. ( charve unsigned charyükseltilir int) [ en.cppreference.com/w/cpp/language/variadic_arguments] . PRI belirticilerini yalnızca platformunuza uymayan şeyler için kullanmanız gerekir int- örn unsigned int.
Timmmm

%xişaretsiz int için doğrudur int değil. Char ve unsigned char türleri int'e yükseltilir. Ek olarak, uint8_t'nin işaretsiz karakter olarak tanımlandığına dair bir garanti yoktur.
2501

2

Muhtemelen imzalı bir karakter dizisinden yazdırıyorsunuz. Ya bir işaretsiz karakter dizisinden yazdırın ya da değeri 0xff ile maskeleyin: örneğin ar [i] ve 0xFF. Yüksek (işaret) biti ayarlandığından c0 değerleri işaret genişletiliyor.


-1

Bunun gibi bir şey dene:

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

Bunu üreten:

$ ./foo 
c0 c0 61 62 63 31 32 33
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.