Menu

Format Specifier di C: Referensi Lengkap printf dan scanf

Semua format specifier C di satu tempat: arti %d, %s, %c, %f, %p, dan lainnya, length modifier l, ll, h, dan z, lebar, presisi, dan flag, serta mana yang berbeda antara printf dan scanf.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

Format specifier memberi tahu printf cara mengubah nilai menjadi teks, dan memberi tahu scanf cara mengubah teks menjadi nilai. Inilah satu bagian C di mana compiler secara default tidak bisa membantumu, karena tipe argumen tidak terlihat oleh fungsinya. Jadi halaman ini adalah tabel untuk dijadikan pegangan.

Bentuk umum sebuah spesifikasi adalah:

%[flags][width][.precision][length]conversion

Hanya % dan huruf konversi yang wajib. %-8.2lf memakai kelima bagiannya.

Tabel Konversi

SpecifierArtiArgumen printfArgumen scanf
%dinteger desimal signedintint *
%iinteger signedint (sama dengan %d)int * (juga menerima 0x, oktal berawalan 0)
%udesimal unsignedunsigned intunsigned int *
%ffloating point desimaldoublefloat *
%Fseperti %f, INF/NAN huruf besardouble-
%e / %Enotasi ilmiahdoublefloat *
%g / %Gyang lebih pendek dari %f / %edoublefloat *
%a / %Afloating point heksadesimaldoublefloat *
%csatu karakterint (char yang dinaikkan)char *
%sstringchar * (diakhiri '\0')char * (buffer)
%palamat pointervoid *void **
%x / %Xheksadesimal unsignedunsigned intunsigned int *
%ooktal unsignedunsigned intunsigned int *
%nmenyimpan jumlah karakter yang sudah ditulisint *int *
%%tanda % apa adanyatidak adamencocokkan % apa adanya
%[...]scanset (hanya scanf)-char *

Dua entri perlu diberi peringatan. %n menulis ke memori alih-alih mencetak, dan merupakan mekanisme di balik celah keamanan format string: jangan pernah biarkan teks dari pengguna masuk ke format string. Lalu %p membutuhkan void *; memberikan int * langsung adalah undefined behavior, meski setiap compiler sungguhan tetap mencetak alamatnya.

Length Modifier

Length modifier ditaruh di antara presisi dan huruf konversi, dan menyatakan seberapa besar argumennya sebenarnya.

Konversi signed (d i) dan unsigned (u x o) menerima tipe yang berbeda: %lu menginginkan unsigned long, bukan long, dan salah memilihnya adalah undefined behavior, bukan sekadar kesalahan kosmetik.

ModifierDengan d i (printf / scanf)Dengan u x o (printf / scanf)
hhint (dari signed char) / signed char *unsigned int (dari unsigned char) / unsigned char *
hint (dari short) / short *unsigned int (dari unsigned short) / unsigned short *
(tidak ada)int / int *unsigned int / unsigned int *
llong / long *unsigned long / unsigned long *
lllong long / long long *unsigned long long / unsigned long long *
ztipe ukuran signed / pointer ke sanasize_t / size_t *
jintmax_t / intmax_t *uintmax_t / uintmax_t *
tptrdiff_t / ptrdiff_t *pasangan unsigned dari ptrdiff_t

Dan untuk keluarga floating-point dan karakter:

ModifierDenganTipe printfTipe scanf
(tidak ada)f e g adoublefloat *
lf e g adouble (diterima, tanpa efek)double *
Lf e g along doublelong double *
lc swint_t / wchar_t *wchar_t *

Kombinasi yang benar-benar akan kamu ketik:

%zu adalah yang wajib dihafal. sizeof, strlen, dan setiap ukuran dari library standar mengembalikan size_t, yang berukuran 8 byte di sistem 64-bit sementara int berukuran 4. printf("%d", strlen(s)) adalah bug sungguhan yang terlihat berfungsi di sebagian platform; gunakan %zu.

Modifier hh dan h terutama ada untuk scanf, di mana ukuran tujuan itu penting. Di printf, short dinaikkan menjadi int sebelum pemanggilan, jadi %d toh akan mencetaknya dengan benar; %hd hanya mendokumentasikan maksudnya.

Tipe dengan Lebar Pasti

Untuk tipe <stdint.h> seperti int32_t, specifier yang portabel adalah macro dari <inttypes.h>:

PRId32 dijabarkan menjadi huruf yang tepat untuk platformmu, dan string literal yang bersebelahan otomatis digabung, karena itulah format-nya dipecah menjadi tiga bagian. Bentuknya jelek; alternatifnya, melakukan cast ke long long lalu memakai %lld, sering kali lebih mudah dibaca dan selalu benar.

Flag

Flag ditaruh tepat setelah % dan mengubah tampilannya. Flag hanya berlaku untuk printf.

FlagEfekContohOutput
-padding di belakang nilai, bukan di depannya%-6d| pada 4242 |
+selalu cetak tanda untuk nilai signed%+d pada 42+42
spasicetak spasi di tempat + akan muncul% d pada 42 42
0padding dengan nol, bukan spasi%06d pada 42000042
#bentuk alternatif: 0x untuk %x, 0 untuk %o, pertahankan titik desimal untuk %g%#x pada 2550xff

Flag bisa digabung: %-+8.2f berarti lebar minimum 8, dua desimal, selalu bertanda, dengan padding di belakang angka. - mengalahkan 0 kalau kamu menulis keduanya.

Lebar dan Presisi

Lebar adalah ukuran field minimum: output diberi padding untuk mencapainya, dan tidak pernah dipotong agar muat. Presisi (setelah titik) punya arti berbeda untuk setiap keluarga:

KonversiArti presisi
%f %edigit setelah titik desimal (default 6)
%gtotal digit signifikan (default 6)
%sjumlah karakter maksimum yang dicetak
%d %i %u %x %ojumlah digit minimum, diberi padding nol

* di tempat salah satu angka itu mengambil nilainya dari argumen int, yang dibaca sebelum nilainya sendiri. Begitulah cara membuat tabel yang lebar kolomnya dihitung saat program berjalan.

Di scanf, lebar punya tugas yang berbeda dan jauh lebih penting: ia membatasi berapa banyak input yang dipakai sebuah konversi, satu-satunya hal yang menghalangi %s dari buffer overflow.

Di Mana printf dan scanf Berbeda

Keduanya terlihat mirip tetapi berbeda di empat hal. Masing-masing adalah sumber bug yang nyata:

Situasiprintfscanf
Nilai double%f (float dinaikkan menjadi double)%lf, karena %f berarti float *
Lebarukuran field minimum, diberi paddinginput maksimum yang dipakai, dipotong
Argumennilaipointer: &x, atau nama array
Spasi di formatmencetak spasimelewati deretan whitespace apa pun

Coba 2.5 hello. Format yang membacanya dan format yang mencetaknya memakai huruf berbeda untuk double yang sama, dan 15 di %15s membatasi input sampai 15 karakter ditambah terminator, persis seukuran word.

Scanset di scanf

%[...] membaca sekumpulan karakter, bukan kata yang dibatasi whitespace. Inilah cara membaca satu baris penuh dengan scanf, meski fgets tetap alat yang lebih baik:

[^\n] berarti "karakter apa pun kecuali baris baru". %[0-9] hanya akan membaca digit, %[abc] hanya tiga huruf itu. Berbeda dengan %s, scanset tidak melewati whitespace di depan, jadi satu baris baru yang tersisa di buffer membuatnya sama sekali tidak cocok dengan apa pun.

Salah Memilih Adalah Undefined Behavior

printf tidak bisa melihat tipe asli argumenmu. Ia membaca byte sebanyak yang diisyaratkan specifier, dan menafsirkannya sesuai kata specifier itu. Jadi ketidakcocokan bukan sekadar gangguan format, melainkan membaca memori sebagai sesuatu yang salah:

printf("%d\n", 3.5);        /* prints garbage: reads a double's bits as int */
printf("%s\n", 42);         /* treats 42 as an address - crash */
printf("%d\n", strlen(s));  /* size_t read as int - wrong on 64-bit */
printf("%f\n", 3);          /* int read as double - garbage */

Pertahanannya hanya butuh satu flag. Kompilasi dengan -Wall dan GCC memeriksa setiap format string literal terhadap argumennya:

gcc -Wall -Wextra program.c -o program

Compiler melaporkan keempat baris di atas saat kompilasi. Ini peringatan paling berharga di C, dan sudah menyala secara default di editor di atas.

Untuk sisi praktis memakai semua ini, seperti tabel yang rata, mencetak float agar mudah dibaca, dan membaca input tanpa bug baris baru yang klasik, lihat printf dan scanf.

Pertanyaan yang sering diajukan

Apa arti %d di C?

%d adalah conversion specifier untuk integer desimal signed, yaitu int. Di printf, ia mencetak argumen sebagai digit basis 10; di scanf, ia membaca digit basis 10 ke dalam int *. %i artinya sama di printf, tetapi di scanf %i juga menerima heksadesimal 0x dan oktal yang diawali nol.

Apa perbedaan %f dan %lf di C?

Di printf praktis tidak ada perbedaan: argumen float dinaikkan menjadi double, jadi %f mencetak keduanya, dan C99 menerima %lf sebagai sinonimnya. Di scanf perbedaannya sangat penting: %f menulis ke float * (4 byte) dan %lf ke double * (8 byte). Memakai %f untuk double merusak memori.

Bagaimana cara mencetak size_t atau long long di C?

%zu untuk size_t (yang dikembalikan sizeof dan strlen) dan %lld / %llu untuk long long / unsigned long long. Memakai %d untuk tipe-tipe ini adalah undefined behavior: kebetulan berfungsi di sistem 32-bit dan bermasalah di sistem 64-bit, karena itulah bug ini bisa bertahan begitu lama.

Apa yang terjadi kalau memakai format specifier yang salah di C?

Itu undefined behavior, bukan sekadar kesalahan pembulatan. printf membaca argumen sesuai klaim specifier-nya, jadi printf("%d", 3.5) mencetak nilai sampah dan printf("%s", 42) mengikuti angka 42 sebagai alamat dan biasanya crash. Kompilasi dengan -Wall, yang membuat GCC memeriksa format string terhadap argumennya.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai