Bir biçim belirteci, printf'e bir değeri metne nasıl çevireceğini ve scanf'e metni bir değere nasıl çevireceğini söyler. C'nin, derleyicinin varsayılan olarak size yardım edemediği tek parçasıdır - argüman tipleri fonksiyona görünmezdir - dolayısıyla bu sayfa, karşılaştıracağınız tablodur.
Bir belirtimin genel biçimi şudur:
%[bayraklar][genislik][.hassasiyet][uzunluk]donusum
Yalnızca % ve dönüşüm harfi zorunludur. %-8.2lf beş parçanın hepsini kullanır.
Dönüşüm Tablosu
| Belirteç | Anlamı | printf argümanı | scanf argümanı |
|---|---|---|---|
%d | işaretli ondalık tam sayı | int | int * |
%i | işaretli tam sayı | int (%d ile aynı) | int * (ayrıca 0x, 0-sekizlik kabul eder) |
%u | işaretsiz ondalık | unsigned int | unsigned int * |
%f | ondalık kayan nokta | double | float * |
%F | %f gibi, büyük harf INF/NAN | double | - |
%e / %E | bilimsel gösterim | double | float * |
%g / %G | %f / %e'nin kısa olanı | double | float * |
%a / %A | on altılık kayan nokta | double | float * |
%c | tek karakter | int (yükseltilmiş char) | char * |
%s | metin | char * ('\0' sonlandırmalı) | char * (tampon) |
%p | gösterici adresi | void * | void ** |
%x / %X | işaretsiz on altılık | unsigned int | unsigned int * |
%o | işaretsiz sekizlik | unsigned int | unsigned int * |
%n | şimdiye dek yazılan karakter sayısını sakla | int * | int * |
%% | gerçek bir % | yok | gerçek bir % ile eşleşir |
%[...] | tarama kümesi (yalnızca scanf) | - | char * |
İki madde uyarıyı hak eder. %n yazdırmak yerine belleğe yazar ve biçim metni açığının arkasındaki mekanizmadır - kullanıcı metninin bir biçim metnine ulaşmasına asla izin vermeyin. Ve %p bir void * ister; doğrudan bir int * geçirmek, her gerçek derleyici yine de adresi yazdırsa bile tanımsızdır.
Uzunluk Değiştiricileri
Uzunluk değiştiricisi hassasiyet ile dönüşüm harfi arasında gelir ve argümanın gerçekte ne kadar büyük olduğunu söyler.
İşaretli dönüşümler (d i) ile işaretsiz olanlar (u x o) farklı tipler alır - %lu, long değil unsigned long ister; bunu karıştırmak kozmetik bir hata değil, tanımsız davranıştır.
| Değiştirici | d i ile (printf / scanf) | u x o ile (printf / scanf) |
|---|---|---|
hh | int (signed char'dan) / signed char * | unsigned int (unsigned char'dan) / unsigned char * |
h | int (short'tan) / short * | unsigned int (unsigned short'tan) / unsigned short * |
| (yok) | int / int * | unsigned int / unsigned int * |
l | long / long * | unsigned long / unsigned long * |
ll | long long / long long * | unsigned long long / unsigned long long * |
z | işaretli boyut tipi / ona gösterici | size_t / size_t * |
j | intmax_t / intmax_t * | uintmax_t / uintmax_t * |
t | ptrdiff_t / ptrdiff_t * | ptrdiff_t'nin işaretsiz karşılığı |
Kayan noktalı ve karakter aileleri için ise:
| Değiştirici | Şununla | printf tipi | scanf tipi |
|---|---|---|---|
| (yok) | f e g a | double | float * |
l | f e g a | double (kabul edilir, etkisiz) | double * |
L | f e g a | long double | long double * |
l | c s | wint_t / wchar_t * | wchar_t * |
Gerçekte yazacağınız birleşimler:
Ezberlenmesi gereken %zu'dur. sizeof, strlen ve her standart kütüphane boyutu size_t döndürür; bu, 64 bitlik bir sistemde 8 bayttır, int ise 4. printf("%d", strlen(s)), bazı platformlarda çalışıyor gibi görünen gerçek bir hatadır - %zu kullanın.
hh ve h değiştiricileri esas olarak, hedef boyutunun önemli olduğu scanf için vardır. printf'te bir short çağrıdan önce int'e yükseltilir, dolayısıyla %d onu zaten doğru yazdırırdı; %hd yalnızca niyeti belgeler.
Sabit Genişlikli Tipler
int32_t gibi <stdint.h> tipleri için taşınabilir belirteçler <inttypes.h>'deki makrolardır:
PRId32, platformunuz için doğru harflere genişler ve komşu metin sabitleri birleşir; biçimin üç parçaya bölünmesinin sebebi budur. Çirkindir; alternatif - long long'a dönüştürüp %lld kullanmak - çoğu zaman daha okunaklı ve her zaman doğrudur.
Bayraklar
Bayraklar hemen %'den sonra gelir ve sunumu değiştirir. Yalnızca printf için geçerlidirler.
| Bayrak | Etki | Örnek | Çıktı |
|---|---|---|---|
- | değerden önce değil sonra doldur | 42 için %-6d| | 42 | |
+ | işaretli değerler için her zaman işaret yazdır | 42 için %+d | +42 |
| boşluk | +'ın geleceği yere bir boşluk yazdır | 42 için % d | 42 |
0 | boşluk yerine sıfırla doldur | 42 için %06d | 000042 |
# | alternatif biçim: %x için 0x, %o için 0, %g için noktayı koru | 255 için %#x | 0xff |
Bayraklar birleşebilir: %-+8.2f, en az 8 genişlik, iki ondalık, her zaman işaretli, sayıdan sonra doldurulmuş demektir. İkisini de yazarsanız -, 0'ı yener.
Genişlik ve Hassasiyet
Genişlik en küçük alan boyutudur - çıktı ona ulaşacak şekilde doldurulur ve ona sığması için asla kırpılmaz. Hassasiyet (bir noktadan sonra) her aile için farklı bir şey ifade eder:
| Dönüşüm | Hassasiyet şu demektir |
|---|---|
%f %e | ondalık noktadan sonraki basamak sayısı (varsayılan 6) |
%g | toplam anlamlı basamak (varsayılan 6) |
%s | yazdırılacak en fazla karakter |
%d %i %u %x %o | en az basamak, sıfırla doldurulmuş |
Bu sayılardan birinin yerine konan bir *, onu değerin kendisinden önce okunan bir int argümandan alır. Sütun genişlikleri çalışma zamanında hesaplanan tabloları böyle kurarsınız.
scanf'te genişliğin farklı ve çok daha önemli bir işi vardır: bir dönüşümün ne kadar girdi tüketeceğini sınırlar ki bu, %s ile bir tampon taşması arasında duran tek şeydir.
printf ile scanf'in Ayrıştığı Yerler
Birbirlerine benzerler ve dört yerde ayrışırlar. Her biri canlı bir hata kaynağıdır:
| Durum | printf | scanf |
|---|---|---|
double değeri | %f (float, double'a yükseltilir) | %lf - %f bir float * demektir |
| Genişlik | en küçük alan boyutu, doldurur | tüketilen en fazla girdi, kırpar |
| Argümanlar | değerler | göstericiler - &x ya da bir dizi adı |
| Biçimdeki bir boşluk | bir boşluk yazdırır | art arda gelen boşlukları atlar |
2.5 merhaba deneyin. Onu okuyan biçim ile yazdıran biçim aynı double için farklı harfler kullanır ve %15s'teki 15, girdiyi 15 karakter artı sonlandırıcıyla sınırlar - tam olarak word'ün boyutu.
scanf Tarama Kümesi
%[...], boşlukla ayrılmış bir kelime yerine bir karakter kümesi okur. scanf ile bütün bir satır okumanın yoludur, ama fgets hâlâ daha iyi araçtır:
[^\n], "satır sonu dışındaki her karakter" demektir. %[0-9] yalnızca rakamları, %[abc] yalnızca o üç harfi okurdu. %s'nin aksine bir tarama kümesi baştaki boşlukları atlamaz, dolayısıyla tampondaki başıboş bir satır sonu hiçbir şeyle eşleşmemesine yol açar.
Yanlış Yapmak Tanımsız Davranıştır
printf, argümanınızın gerçek tipini göremez. Belirtecin ima ettiği kadar baytı, belirtecin söylediği gibi yorumlayarak okur. Dolayısıyla bir uyumsuzluk bir biçimlendirme aksaklığı değil - belleği yanlış şey olarak okumaktır:
printf("%d\n", 3.5); /* çöp yazdırır: bir double'ın bitlerini int olarak okur */
printf("%s\n", 42); /* 42'yi bir adres sayar - çökme */
printf("%d\n", strlen(s)); /* size_t, int olarak okunur - 64 bitte yanlış */
printf("%f\n", 3); /* int, double olarak okunur - çöp */
Savunma tek bir bayrağa mal olur. -Wall ile derleyin ve GCC her sabit biçim metnini argümanlarıyla karşılaştırır:
gcc -Wall -Wextra program.c -o program
Yukarıdaki dört satırın her birini derleme zamanında bildirir. Bu, C'deki tek en değerli uyarıdır ve yukarıdaki editörde varsayılan olarak açıktır.
Bunları kullanmanın pratik tarafı için - hizalı tablolar, float'ları okunaklı yazdırmak, klasik satır sonu hatası olmadan girdi okumak - bkz. printf ve scanf.
Sıkça Sorulan Sorular
C'de %d ne demek?
%d, işaretli ondalık tam sayı - yani bir int - için dönüşüm belirtecidir. printf'te argümanı 10 tabanlı rakamlar olarak yazdırır; scanf'te 10 tabanlı rakamları bir int * içine okur. printf'te %i aynı şeyi ifade eder, ama scanf'te %i ayrıca 0x on altılık ve baştaki sıfırlı sekizlik kabul eder.
C'de %f ile %lf arasındaki fark nedir?
printf'te pratik bir fark yoktur: float argümanlar double'a yükseltilir, dolayısıyla %f ikisini de yazdırır ve C99 %lf'yi eşanlamlı olarak kabul eder. scanf'te fark kritiktir - %f bir float * (4 bayt), %lf ise bir double * (8 bayt) içine yazar. Bir double için %f kullanmak belleği bozar.
C'de bir size_t ya da long long nasıl yazdırılır?
size_t için %zu (sizeof ve strlen'in döndürdüğü) ve long long / unsigned long long için %lld / %llu. Bunlar için %d kullanmak tanımsız davranıştır - 32 bitlik sistemlerde çalışıyor gibi görünür ve 64 bitlik olanlarda bozulur; hatanın bu kadar uzun yaşamasının sebebi budur.
C'de yanlış biçim belirteci kullanırsanız ne olur?
Bu bir yuvarlama hatası değil, tanımsız davranıştır. printf, argümanı belirteç ne iddia ediyorsa o şekilde okur, dolayısıyla printf("%d", 3.5) çöp yazdırır ve printf("%s", 42), 42 sayısını bir adres sayıp genellikle çöker. GCC'nin biçim metinlerini argümanlarla karşılaştırmasını sağlayan -Wall ile derleyin.