C++ menyiapkan bukan satu melainkan tiga stream standar yang sudah terbuka begitu program dimulai. Selain std::cout untuk output biasa, ada std::cerr untuk pesan galat dan std::clog untuk pesan log — ketiganya dipakai dengan operator << yang sama, tetapi punya tujuan dan perilaku buffer berbeda. Memilih stream yang tepat terlihat sepele, padahal ia menentukan apakah pengguna dan alat otomasi bisa memisahkan hasil kerja program dari diagnostiknya. Pada Chapter ini kita membandingkan ketiganya, membuktikan dengan redirect nyata bahwa cerr mengalir lewat jalur yang berbeda di terminal, dan merumuskan kapan masing-masing sebaiknya dipakai.

Tiga Stream Standar #

Ketiga stream adalah objek global yang dibuat otomatis oleh pustaka standar; kita tidak perlu mendeklarasikan apa pun selain meng-include <iostream>. Perbedaannya terletak pada tujuan (destination) dan cara buffer bekerja:

StreamTujuanBufferPeruntukan lazim
std::coutstandard output (stdout)DibufferHasil kerja program, data keluaran
std::cerrstandard error (stderr)Tidak dibufferPesan galat yang harus segera terlihat
std::clogstandard error (stderr)DibufferPesan log dan diagnostik berjalan

Secara penulisan kode, ketiganya identik: std::cout << x, std::cerr << x, dan std::clog << x sama-sama menyisipkan nilai. Yang berbeda adalah jalur di sistem operasi: cout menuju stdout, sementara cerr dan clog sama-sama menuju stderr. Pemisahan ini bukan gaya-gayaan — Unix dan turunannya memang merancang dua jalur berbeda agar output bisnis dan pesan diagnostik bisa diolah terpisah. Program berikut memakai ketiganya sekaligus:

cpp
#include <iostream>
#include <string>

int main() {
    std::string namaFile = "data.txt";

    std::cout << "Memproses " << namaFile << "...\n";
    std::cerr << "GALAT: file " << namaFile << " tidak ditemukan\n";
    std::clog << "INFO : proses selesai dengan status gagal\n";
    return 0;
}

Output yang Dihasilkan:

text
Memproses data.txt...

Mengapa blok output di atas hanya berisi satu baris, padahal programnya mencetak tiga? Karena alat verifikasi menangkap standard output saja: baris GALAT dan INFO mengalir ke stderr sehingga tidak ikut terdata. Di terminal sungguhan, ketiga baris tetap tampil berurutan:

bash
$ g++ -std=c++23 -Wall demo-cerr.cpp -o demo-cerr
$ ./demo-cerr
Memproses data.txt...
GALAT: file data.txt tidak ditemukan
INFO : proses selesai dengan status gagal

Bedah singkat programnya: baris 7 menyisipkan string hasil gabungan ke cout; baris 8 melaporkan kegagalan lewat cerr sesuai fungsinya; baris 9 mencatat diagnostik lewat clog. Ketiganya memakai operator << dan syntax identik — yang membedakan hanya pilihan stream.

Redirect: Memisahkan Jalur di Terminal #

Shell memperlakukan setiap proses sebagai tiga aliran bernomor: standard input (fd 0), standard output (fd 1), dan standard error (fd 2). Simbol redirect > mengalihkan fd 1 ke file, sedangkan 2> mengalihkan fd 2. Inilah bukti nyata bahwa cout dan cerr benar-benar jalur berbeda. Pertama, kita alihkan stdout ke file:

bash
$ ./demo-cerr > hasil.txt
GALAT: file data.txt tidak ditemukan
INFO : proses selesai dengan status gagal

$ cat hasil.txt
Memproses data.txt...

Baris GALAT dan INFO tetap muncul di terminal (karena stderr tidak dialihkan), sedangkan hasil.txt hanya berisi baris cout. Sekarang kebalikannya — alihkan stderr saja:

bash
$ ./demo-cerr 2> galat.log
Memproses data.txt...

$ cat galat.log
GALAT: file data.txt tidak ditemukan
INFO : proses selesai dengan status gagal

Kini terminal hanya menampilkan hasil kerja program, dan pesan galat terkumpul rapi di galat.log — pola yang sangat berguna saat output program dipakai sebagai data oleh proses lain. Bila keduanya perlu digabung ke satu file, shell menyediakan 2>&1 (alihkan fd 2 ke tujuan fd 1):

bash
$ ./demo-cerr > semua.txt 2>&1

Arti “Tidak Dibuffer” pada cerr #

Buffer adalah wadah sementara di memori: data dikumpulkan dulu, lalu dikirim sekaligus agar efisien. std::cout dibuffer penuh — isi yang kita sisipkan bisa menunggu di memori sampai buffer penuh, sampai ada perintah flush (misalnya std::endl), atau sampai program selesai. std::cerr dirancang berbeda: stream ini berflag unit-buffered, artinya setiap penyisipan langsung memicu flush. Pesan galat dengan demikian tertulis secepat mungkin, tanpa menunggu antrian. std::clog memilih jalan tengah: dialirkan ke stderr seperti cerr, tetapi tetap dibuffer seperti cout karena pesan log jarang butuh ketersegeran.

Alasan desainnya mudah dipahami lewat skenario: program yang berhenti mendadak (misalnya karena exception tak tertangani) mungkin tidak sempat mengosongkan buffer cout — data yang menunggu di sana bisa hilang begitu saja. Pesan galat yang sudah di-flush lewat cerr tidak mengalami nasib itu; ia sampai ke pengguna justru pada saat paling dibutuhkan, yaitu ketika program sedang bermasalah.

Kapan Memakai yang Mana #

Aturan praktisnya bisa diringkas dalam tiga kalimat:

  • Data keluaran → std::cout. Hasil perhitungan, laporan, data yang dipakai proses lain — semuanya lewat stdout agar bisa dialihkan dan diolah.
  • Pesan galat → std::cerr. Kondisi yang membuat program tidak bisa melanjutkan tugasnya: file tidak ditemukan, input tidak valid, sumber daya habis.
  • Catatan berjalan → std::clog. Diagnostik seperti “memulai langkah 3” atau “terhubung ke server” yang berguna saat menelusuri, tetapi bukan bagian dari hasil kerja.

Kebiasaan memisahkan ini terasa berharganya begitu program kita dipakai di pipeline: skrip dapat mengalihkan > tanpa risiko pesan galat ikut tercampur menjadi data. Konvensi yang sama dipakai tool standar seperti g++ sendiri — pesan error dan warning kompilasi muncul lewat stderr, dan itulah sebabnya mereka tetap terlihat meski output perintah kita alihkan. Halaman std::cerr di cppreference merangkum detail ketiga stream ini beserta flag buffer-nya.

Kita kini menguasai seluruh jalur komunikasi dasar: masuk lewat cin, keluar lewat cout, melapor lewat cerr dan clog. Satu perkakas terakhir di section ini sering membuat pemula bingung pada string seperti "C:\\Users\\..." — Chapter berikutnya membedah escape sequences dan menjelaskan mengapa backslash ditulis dua kali di kode.