Menu

C'de Biçim Belirteçleri: Eksiksiz printf ve scanf Başvurusu

Tek yerde her C biçim belirteci - %d, %s, %c, %f, %p ve gerisi ne anlama gelir, l, ll, h ve z uzunluk değiştiricileri, genişlik, hassasiyet ve bayraklar ve hangilerinin printf ile scanf arasında farklılaştığı.

Bu sayfada çalıştırılabilir editörler var - düzenle, çalıştır ve sonucu anında gör.

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ı
%dişaretli ondalık tam sayıintint *
%iişaretli tam sayıint (%d ile aynı)int * (ayrıca 0x, 0-sekizlik kabul eder)
%uişaretsiz ondalıkunsigned intunsigned int *
%fondalık kayan noktadoublefloat *
%F%f gibi, büyük harf INF/NANdouble-
%e / %Ebilimsel gösterimdoublefloat *
%g / %G%f / %e'nin kısa olanıdoublefloat *
%a / %Aon altılık kayan noktadoublefloat *
%ctek karakterint (yükseltilmiş char)char *
%smetinchar * ('\0' sonlandırmalı)char * (tampon)
%pgösterici adresivoid *void **
%x / %Xişaretsiz on altılıkunsigned intunsigned int *
%oişaretsiz sekizlikunsigned intunsigned int *
%nşimdiye dek yazılan karakter sayısını saklaint *int *
%%gerçek bir %yokgerç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ştiricid i ile (printf / scanf)u x o ile (printf / scanf)
hhint (signed char'dan) / signed char *unsigned int (unsigned char'dan) / unsigned char *
hint (short'tan) / short *unsigned int (unsigned short'tan) / unsigned short *
(yok)int / int *unsigned int / unsigned int *
llong / long *unsigned long / unsigned long *
lllong long / long long *unsigned long long / unsigned long long *
zişaretli boyut tipi / ona göstericisize_t / size_t *
jintmax_t / intmax_t *uintmax_t / uintmax_t *
tptrdiff_t / ptrdiff_t *ptrdiff_t'nin işaretsiz karşılığı

Kayan noktalı ve karakter aileleri için ise:

DeğiştiriciŞununlaprintf tipiscanf tipi
(yok)f e g adoublefloat *
lf e g adouble (kabul edilir, etkisiz)double *
Lf e g along doublelong double *
lc swint_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.

BayrakEtkiÖrnekÇıktı
-değerden önce değil sonra doldur42 için %-6d|42 |
+işaretli değerler için her zaman işaret yazdır42 için %+d+42
boşluk+'ın geleceği yere bir boşluk yazdır42 için % d 42
0boşluk yerine sıfırla doldur42 için %06d000042
#alternatif biçim: %x için 0x, %o için 0, %g için noktayı koru255 için %#x0xff

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üşümHassasiyet şu demektir
%f %eondalık noktadan sonraki basamak sayısı (varsayılan 6)
%gtoplam anlamlı basamak (varsayılan 6)
%syazdırılacak en fazla karakter
%d %i %u %x %oen 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:

Durumprintfscanf
double değeri%f (float, double'a yükseltilir)%lf - %f bir float * demektir
Genişliken küçük alan boyutu, doldururtüketilen en fazla girdi, kırpar
Argümanlardeğerlergöstericiler - &x ya da bir dizi adı
Biçimdeki bir boşlukbir boşluk yazdırırart 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.

Coddy programming languages illustration

Coddy ile kodlamayı öğren

BAŞLA