Sebelum std::string mengambil alih, teks di C++ (dan di C) disimpan dengan cara yang sangat mekanis: sebuah array char yang diakhiri karakter khusus '\0'. Format ini disebut C-style string dan hingga hari ini masih menjadi bahasa bersama antara C++ dan dunia luar — fungsi sistem operasi, pustaka C, dan banyak API lama menerima serta mengembalikan string dalam bentuk ini. Memahaminya bukan sekadar arkeologi kode: dari format inilah kita melihat dengan jelas apa yang sebenarnya dilakukan std::string untuk kita, dan mengapa “string” terlihat seperti tipe istimewa padahal hanya array dengan aturan penutup. Pada Chapter ini kita membedah format '\0', memakai fungsi dari header <cstring>, dan menghadapi kelemahan bersejarahnya: buffer overflow.
Aturan Penutup ‘\0’ #
Aturan satu-satunya format ini: string berakhir pada karakter '\0' (karakter nol, bernilai 0, bukan digit ‘0’). Fungsi-fungsi teks tidak pernah mengetahui panjang array — ia membaca karakter satu per satu sampai menemukan '\0'. Konsekuensinya, array char[16] yang berisi "Budi" sebenarnya menyimpan lima karakter terpakai: B, u, d, i, '\0', dan sebelas slot sisa yang tidak berarti apa-apa. Program berikut memperagakan fungsi-fungsi inti dari header <cstring>:
#include <cstring>
#include <iostream>
int main() {
char nama[16] = "Budi"; // 'B','u','d','i','\0' + 11 slot cadangan
// strlen: panjang string sampai karakter '\0' (tidak menghitung '\0')
std::cout << "isi : " << nama << "\n";
std::cout << "strlen: " << std::strlen(nama) << " karakter\n";
std::cout << "sizeof: " << sizeof(nama) << " byte (kapasitas buffer)\n";
// strcpy menyalin sumber + '\0'; aman karena tujuan cukup besar
char salinan[16];
std::strcpy(salinan, nama);
std::cout << "salinan: " << salinan << "\n";
// strncpy membatasi jumlah karakter yang disalin — pola yang lebih aman
char pendek[4];
std::strncpy(pendek, nama, sizeof(pendek) - 1);
pendek[sizeof(pendek) - 1] = '\0'; // pastikan tetap ditutup '\0'
std::cout << "pendek : " << pendek << "\n";
// strcmp membandingkan isi; hasil 0 berarti identik
char tebakan[16] = "Budi";
if (std::strcmp(nama, tebakan) == 0) {
std::cout << "strcmp : '" << nama << "' == '" << tebakan << "'\n";
} else {
std::cout << "strcmp : berbeda\n";
}
// menulis '\0' memotong string di posisi mana pun
nama[2] = '\0';
std::cout << "terpotong: " << nama << " (strlen kini "
<< std::strlen(nama) << ")\n";
return 0;
}Output yang Dihasilkan:
isi : Budi
strlen: 4 karakter
sizeof: 16 byte (kapasitas buffer)
salinan: Budi
pendek : Bud
strcmp : 'Budi' == 'Budi'
terpotong: Bu (strlen kini 2)Mari kita bedah fungsi-fungsi yang terpakai:
- Baris 5 (
char nama[16] = "Budi";): string literal otomatis menyertakan'\0', sehingga inisialisasi ini menyalin 5 karakter ke dalam buffer 16 slot. Perhatikanstrlenmelaporkan 4 sedangkansizeofmelaporkan 16 — panjang isi versus kapasitas wadah adalah dua angka berbeda. - Baris 13 (
std::strcpy(salinan, nama)): menyalin isi termasuk penutup'\0'ke tujuan. Fungsi ini aman hanya jika kita yang menjamin tujuan cukup besar — strcpy tidak memeriksanya. - Baris 16-19 (
std::strncpy): versi terkendali yang menyalin paling banyaknkarakter. Kita mengisisizeof(pendek) - 1slot lalu menempatkan'\0'di posisi terakhir secara manual; pola ini memastikan buffer pendek tidak pernah bocor melewati kapasitasnya. - Baris 22-27 (
std::strcmp): membandingkan dua string karakter demi karakter. Hasilnya0bila identik — pembanding==pada arraycharjustru membandingkan alamat, bukan isi, sehingga perbandingan isi wajib lewatstrcmp. - Baris 30 (
nama[2] = '\0';): menaruh penutup di tengah memotong string menjadi"Bu"tanpa menghapus byte setelahnya.strlenkini membaca hanya sampai penutup baru.
Bahaya Buffer Overflow #
Kombinasi “fungsi teks yang tidak memeriksa batas” dan “string yang panjangnya ditentukan '\0'” menghasilkan kelalaian klasik C/C++: buffer overflow. Penulisan ke array char melebihi kapasitasnya sama seperti akses di luar batas pada Chapter Mengakses Elemen Array — undefined behavior yang bisa menimpa data tetangga. Kode berikut bermasalah dan kita tampilkan sebagai contoh tanpa klaim hasil eksekusi:
#include <cstring>
#include <iostream>
int main() {
char nama[4] = "Bud";
std::strcpy(nama, "Budi"); // butuh 5 slot, tersedia hanya 4
std::cout << nama << "\n";
return 0;
}Kasus di atas kebetulan terlihat oleh compiler karena ukuran buffer dan isi salinan sama-sama diketahui saat kompilasi. Dengan flag -Wall, GCC 14 memberi peringatan yang merinci angkanya:
warning: 'void* __builtin_memcpy(void*, const void*, long unsigned int)'
writing 5 bytes into a region of size 4 overflows the destination
[-Wstringop-overflow=]
6 | std::strcpy(nama, "Budi"); // butuh 5 slot, tersedia hanya 4
| ~~~~~~~~~~~^~~~~~~~~~~~~~
note: destination object 'nama' of size 4Peringatan seperti ini adalah jaring pengaman berbagi pengetahuan compiler — tetapi hanya bekerja bila panjang sumber diketahui saat kompilasi. Ketika teks berasal dari input pengguna, ukurannya baru terlihat saat runtime, dan compiler tidak bisa membantu. Itulah mengapa aturan emasnya tetap disiplin sendiri: buffer tujuan harus dihitung sebesar isi terpanjang yang mungkin ditambah satu slot untuk '\0', dan fungsi berbatas (strncpy, snprintf) dipilih di atas fungsi tak berbatas (strcpy, strcat). Kombinasi header <cstdio> dan format snprintf dibahas pada Section Input-Output dan File.
Kapan C-style String Masih Ditemui #
Meskipun std::string (Chapter berikutnya) adalah pilihan utama kode modern, format char berakhir '\0' tetap muncul di beberapa tempat yang perlu kita kenali. API pustaka C seperti fungsi sistem operasi (open, getenv, dan ribuan lainnya) menerima const char*, sehingga kode C++ modern tetap sering “membongkar” std::string menjadi const char* di batas pemanggilan API tersebut. Argument baris perintah argv dari fungsi main berupa array dari char*. Sumber belajar lama, kode industri berusia puluhan tahun, dan papan mikrokontroler tanpa pustaka standar juga masih memakainya.
Untuk interaksi dengan API tersebut, std::string menyediakan jembatan resmi: anggota .c_str() mengembalikan const char* berakhir '\0' yang menunjuk isi string. Pola umumnya satu baris — fopen(namaFile.c_str(), "r") — dan aman selama string aslinya tetap hidup. Dua dunia ini koeksistensi dengan baik selama kita tahu batasnya: format '\0' untuk lalu lintas dengan C, std::string untuk seluruh logika program.
Chapter berikutnya memperkenalkan std::string secara tuntas: teks yang tumbuh mengikuti isi, dibandingkan dengan ==, dan digabung dengan + tanpa menghitung kapasitas buffer.
Rujukan resmi: cppreference — strlen , cppreference — strcpy/strncpy , cppreference — strcmp .