Banyak program perlu menyimpan daftar string: nama hari, daftar kode mata kuliah, daftar menu, atau daftar argumen baris perintah. C tidak memiliki tipe String bawaan—string adalah array char yang diakhiri karakter nol ('\0'). Untuk menyimpan banyak string, kita punya dua pilihan utama: array 2D char strs[N][M] (setiap string menempati buffer tetap) atau array of pointer char *strs[N] (setiap elemen menunjuk ke string yang panjangnya berbeda-beda). Pada chapter ini kita akan membandingkan keduanya dari sisi memori, fleksibilitas, dan keamanan modifikasi.
Dua Model Array of Strings #
Model 1: char strs[N][M] — matriks karakter N baris × M kolom. Setiap baris menyimpan satu string dengan kapasitas maksimum M-1 karakter (1 byte untuk '\0'). Memori yang dipakai = N * M byte, terlepas dari panjang string aktual. String bisa dimodifikasi karena buffer adalah milik array sendiri.
Model 2: char *strs[N] — array berisi N pointer ke char. Setiap elemen adalah pointer yang menunjuk ke string di tempat lain (biasanya string literal yang berada di read-only memory). Memori yang dipakai = N * sizeof(char *) = 8N byte pada 64-bit, plus memori yang dialokasikan untuk string aktual. String yang ditunjuk biasanya tidak boleh dimodifikasi (karena string literal adalah read-only).
Perbandingan Kedua Model #
Contoh berikut mendeklarasikan kedua model dengan data yang sama, mencetak isinya, membandingkan sizeof, lalu memodifikasi isi model 1 (yang aman) dan menunjukkan bahwa model 2 berisi string literal yang tidak boleh dimodifikasi.
#include <stdio.h>
#include <string.h>
int main(void) {
/* Array of strings model 1: array 2D char */
char nama1[3][10] = {
"Budi",
"Siti",
"Andi"
};
/* Array of strings model 2: array of pointers */
const char *nama2[3] = {
"Budi",
"Siti",
"Andi"
};
printf("== char nama1[3][10] ==\n");
for (int i = 0; i < 3; i++) {
printf(" %s (len=%zu)\n", nama1[i], strlen(nama1[i]));
}
printf(" sizeof(nama1) = %zu (fixed)\n", sizeof(nama1));
printf(" sizeof(nama1[0]) = %zu (per baris)\n", sizeof(nama1[0]));
printf("\n== const char *nama2[3] ==\n");
for (int i = 0; i < 3; i++) {
printf(" %s (len=%zu)\n", nama2[i], strlen(nama2[i]));
}
printf(" sizeof(nama2) = %zu (3 pointer)\n", sizeof(nama2));
printf(" sizeof(nama2[0]) = %zu (satu pointer)\n", sizeof(nama2[0]));
/* Memodifikasi isi array 2D (bisa, memori lokal) */
strcpy(nama1[0], "Doni");
printf("\nSetelah strcpy(nama1[0], \"Doni\"):\n");
printf(" nama1[0] = %s\n", nama1[0]);
/* Memodifikasi string literal -> UNDEFINED. JANGAN dilakukan. */
/* nama2[0][0] = 'X'; */ /* bila di-uncomment: segfault */
return 0;
}Output yang Dihasilkan:
== char nama1[3][10] ==
Budi (len=4)
Siti (len=4)
Andi (len=4)
sizeof(nama1) = 30 (fixed)
sizeof(nama1[0]) = 10 (per baris)
== const char *nama2[3] ==
Budi (len=4)
Siti (len=4)
Andi (len=4)
sizeof(nama2) = 24 (3 pointer)
sizeof(nama2[0]) = 8 (satu pointer)
Setelah strcpy(nama1[0], "Doni"):
nama1[0] = DoniMari kita telaah baris-baris kode di atas:
- Baris 8 (
char nama1[3][10] = {"Budi", "Siti", "Andi"};): array 2D berukuran 3 baris × 10 kolom. Setiap baris menampung satu string dengan kapasitas maksimum 9 karakter + 1 byte'\0'. Memori total = 30 byte, terlepas dari panjang aktual string (4 karakter). - Baris 14 (
const char *nama2[3] = {...};): array 3 pointerconst char *. Setiap pointer menunjuk ke string literal yang berada di read-only memory. Memori total = 3 × 8 = 24 byte, plus memori untuk string aktual (5 byte per literal termasuk'\0'). - Baris 25 (
sizeof(nama1) = 30): ukuran total array 2D = 3 × 10 = 30 byte. - Baris 26 (
sizeof(nama1[0]) = 10):nama1[0]adalah baris pertama yang tipenyachar[10], berukuran 10 byte. - Baris 32 (
sizeof(nama2) = 24): 3 pointer × 8 byte = 24 byte. - Baris 33 (
sizeof(nama2[0]) = 8):nama2[0]bertipeconst char *, berukuran 8 byte pada 64-bit. - Baris 36 (
strcpy(nama1[0], "Doni");): menyalin string"Doni"ke buffernama1[0]. Aman karenanama1[0]adalah buffer lokal berkapasitas 10 byte, dan"Doni"hanya 5 byte (termasuk'\0'). - Baris 40-41 (
/* nama2[0][0] = 'X'; */): komentar yang sengaja tidak diaktifkan. Mengubah string literal adalah undefined behavior—biasanya memicu segmentation fault karena string literal disimpan di read-only memory.
Daftar Hari dengan Model 2D #
Contoh berikut menampilkan penggunaan praktis model 2D char[N][M]: menyimpan daftar nama hari. Setiap string bisa dimodifikasi dengan aman, dan sizeof memberi ukuran total yang terprediksi.
#include <stdio.h>
#include <string.h>
int main(void) {
/* Matriks char: array of fixed-size strings */
char hari[7][10] = {
"Senin", "Selasa", "Rabu", "Kamis",
"Jumat", "Sabtu", "Minggu"
};
printf("Daftar hari:\n");
for (int i = 0; i < 7; i++) {
printf(" %d: %-8s (len=%zu)\n", i, hari[i], strlen(hari[i]));
}
/* Mengubah isi (aman karena buffer lokal) */
strcpy(hari[0], "Monday");
printf("Setelah ganti hari[0]: %s\n", hari[0]);
/* Bandingkan memori yang dipakai */
printf("sizeof(hari) = %zu byte\n", sizeof(hari)); /* 7 * 10 = 70 */
return 0;
}Output yang Dihasilkan:
Daftar hari:
0: Senin (len=5)
1: Selasa (len=6)
2: Rabu (len=4)
3: Kamis (len=5)
4: Jumat (len=5)
5: Sabtu (len=5)
6: Minggu (len=6)
Setelah ganti hari[0]: Monday
sizeof(hari) = 70 bytePada contoh di atas, array hari berukuran 70 byte (7 × 10). Format %-8s mencetak string rata kiri dengan lebar minimal 8 karakter—berguna untuk membuat tabel yang rapi. Pengubahan hari[0] dari "Senin" ke "Monday" aman karena "Monday" hanya 7 byte (termasuk '\0'), masih muat dalam kapasitas 10 byte per baris.