Tanpa format specifier, fungsi printf hanya bisa mencetak teks biasa. Kekuatan sebenarnya dari printf justru ada pada penanda seperti %d, %f, %s, dan %x yang memberitahu fungsi cara mengubah nilai variabel menjadi deretan karakter yang bisa dibaca manusia. Pada chapter ini kita akan mengenal format specifier dasar, modifier ukuran (%ld, %zu), flag (%-, %+, %0), serta cara mengatur lebar field dan presisi secara dinamis. Daftar lengkap format specifier dapat ditemukan di dokumentasi cppreference untuk <stdio.h>.

Format Specifier Dasar #

Setiap format specifier diawali tanda persen % dan diakhiri satu huruf conversion character yang menentukan bagaimana nilai akan direpresentasikan. Berikut adalah tabel penanda yang paling sering dipakai:

PenandaTipe ArgumenHasil
%dintBilangan desimal bertanda
%uunsigned intBilangan desimal tanpa tanda
%ounsigned intBilangan oktal
%xunsigned intBilangan heksadesimal (huruf kecil)
%Xunsigned intBilangan heksadesimal (huruf besar)
%cint (karakter)Satu karakter
%schar *String dengan null terminator
%fdoubleNotasi titik tetap
%edoubleNotasi ilmiah
%gdoubleNotasi terpendek antara %f atau %e
%%(tanpa argumen)Tanda persen literal

Mari kita lihat seluruh penanda di atas beraksi dalam satu program:

c
#include <stdio.h>

int main(void) {
    int i = 255;
    unsigned int u = 255u;
    char c = 'A';
    char s[] = "Belajar C";
    double d = 3.141592653589793;

    printf("int     (%%d) : %d\n", i);
    printf("unsigned(%%u) : %u\n", u);
    printf("octal   (%%o) : %o\n", i);
    printf("hex     (%%x) : %x\n", i);
    printf("HEX     (%%X) : %X\n", i);
    printf("char    (%%c) : %c\n", c);
    printf("string  (%%s) : %s\n", s);
    printf("double  (%%f) : %f\n", d);
    printf("double  (%%e) : %e\n", d);
    printf("double  (%%g) : %g\n", d);
    printf("percent(%%%%) : %%\n");
    return 0;
}

Output yang Dihasilkan:

text
int     (%d) : 255
unsigned(%u) : 255
octal   (%o) : 377
hex     (%x) : ff
HEX     (%X) : FF
char    (%c) : A
string  (%s) : Belajar C
double  (%f) : 3.141593
double  (%e) : 3.141593e+00
double  (%g) : 3.14159
percent(%%) : %

Mari kita telaah beberapa baris kunci di atas:

  1. Baris 10 (printf("octal (%%o) : %o\n", i);): argumen i bernilai 255 (desimal). Penanda %o menampilkan bilangan dalam basis 8, menghasilkan 377 karena 2*64 + 5*8 + 7 = 255.
  2. Baris 11 (printf("hex (%%x) : %x\n", i);): penanda %x menampilkan 255 dalam basis 16 dengan huruf kecil, yaitu ff.
  3. Baris 12 (printf("HEX (%%X) : %X\n", i);): penanda %X sama dengan %x tetapi memakai huruf besar, menghasilkan FF. Berguna jika kita ingin format yang seragam dengan notasi heksadesimal umum.
  4. Baris 17 (printf("double (%%g) : %g\n", d);): penanda %g memilih representasi terpendek antara %f dan %e. Untuk 3.141592653589793, hasilnya 3.14159 karena digit signifikan default 6 sudah cukup.
  5. Baris 19 (printf("percent(%%%%) : %%\n");): %% adalah penanda khusus untuk mencetak karakter persen literal. Tidak ada argumen yang dibutuhkan.

Modifier Ukuran #

Untuk tipe data yang ukurannya berbeda dari int atau double, kita memerlukan length modifier yang disisipkan di antara % dan huruf konversi. Modifier yang paling sering dipakai: h (short), l (long), ll (long long), z (size_t), dan L (long double).

c
#include <stdio.h>

int main(void) {
    long besar = 9223372036854775807L;
    unsigned long panjang = 18446744073709551615UL;
    size_t ukuran = sizeof(int);

    printf("long          : %ld\n", besar);
    printf("unsigned long : %lu\n", panjang);
    printf("size_t        : %zu\n", ukuran);
    printf("long hex      : %#lx\n", besar);

    /* Flag minus untuk rata kiri */
    printf("[%-10d]\n", 42);
    printf("[%10d]\n", 42);

    /* Flag plus dan nol */
    printf("[%+d]\n", 42);
    printf("[%05d]\n", 42);
    printf("[% d]\n", 42);
    return 0;
}

Output yang Dihasilkan:

text
long          : 9223372036854775807
unsigned long : 18446744073709551615
size_t        : 4
long hex      : 0x7fffffffffffffff
[42        ]
[        42]
[+42]
[00042]
[ 42]

Mari kita telaah baris-baris kode di atas:

  1. Baris 7 (printf("long ... : %ld\n", besar);): modifier l di %ld memberitahu printf bahwa argumen bertipe long. Tanpa l, printf akan membaca argumen seolah-olah int dan hasilnya terpotong.
  2. Baris 9 (printf("size_t ... : %zu\n", ukuran);): modifier z di %zu menandakan tipe size_t. Tipe size_t adalah unsigned sehingga tidak boleh dicetak dengan %d.
  3. Baris 10 (printf("long hex : %#lx\n", besar);): flag # menambahkan prefiks 0x pada output heksadesimal, sehingga pembaca tahu bahwa angka tersebut dalam basis 16.
  4. Baris 13 (printf("[%-10d]\n", 42);): flag - merata-kirikan angka dalam field selebar 10 kolom. Outputnya [42 ].
  5. Baris 14 (printf("[%10d]\n", 42);): tanpa flag, default adalah rata kanan, menghasilkan [ 42].
  6. Baris 17 (printf("[%05d]\n", 42);): flag 0 mengisi ruang kosong dengan angka nol, menghasilkan [00042]. Berguna untuk nomor invoice atau kode.
  7. Baris 18 (printf("[% d]\n", 42);): flag spasi menampilkan tanda plus sebagai spasi kosong, sehingga positif dan negatif tetap sejajar jika dicetak berdampingan.

Lebar Field dan Presisi Dinamis #

Pada chapter sebelumnya kita memakai lebar field berupa angka konstan seperti %5d. C juga mendukung lebar dan presisi yang nilainya ditentukan oleh argumen, dengan menulis * sebagai pengganti angka. Fitur ini berguna saat lebar kolom baru diketahui saat program berjalan.

c
#include <stdio.h>

int main(void) {
    double pi = 3.141592653589793;

    /* Presisi untuk float */
    printf("%.0f\n", pi);
    printf("%.2f\n", pi);
    printf("%.6f\n", pi);
    printf("%.15f\n", pi);

    /* Presisi untuk string: potong panjang */
    printf("%.3s\n", "Belajar C");

    /* Lebar dinamis dengan asterisk */
    int lebar = 8;
    printf("[%*d]\n", lebar, 42);

    int presisi = 3;
    printf("[%.*f]\n", presisi, pi);
    return 0;
}

Output yang Dihasilkan:

text
3
3.14
3.141593
3.141592653589793
Bel
[      42]
[3.142]

Mari kita telaah baris-baris kode di atas:

  1. Baris 6 (printf("%.0f\n", pi);): presisi 0 menghilangkan seluruh digit desimal dan membulatkan nilai, sehingga 3.14... menjadi 3.
  2. Baris 9 (printf("%.15f\n", pi);): presisi 15 memunculkan banyak digit. Pada tipe double IEEE 754, hanya sekitar 15-17 digit pertama yang akurat.
  3. Baris 12 (printf("%.3s\n", "Belajar C");): untuk tipe string, presisi membatasi jumlah karakter yang dicetak. Hanya tiga huruf pertama yang muncul: Bel.
  4. Baris 16 (printf("[%*d]\n", lebar, 42);): asterisk * mengambil nilai lebar dari argumen pertama (lebar = 8), sehingga ekivalen dengan [%8d].
  5. Baris 19 (printf("[%.*f]\n", presisi, pi);): presisi juga bisa diambil dari argumen. Hasilnya sama dengan [%.3f], yaitu [3.142] karena pembulatan.