String di C pada dasarnya adalah array char yang diakhiri dengan byte '\0'. Karena array dan pointer berkaitan erat, kita juga bisa memperlakukan string sebagai char * dan menelusurinya byte per byte dengan pointer arithmetic. Banyak fungsi standar di <string.h> (seperti strlen, strcpy, strcat) bekerja dengan cara persis seperti itu—loop pointer hingga menemui null terminator. Pada chapter ini kita akan menulis ulang beberapa fungsi string standar memakai pointer, sekaligus memahami mengapa pointer ke const char adalah tipe yang tepat untuk parameter string read-only.
Menelusuri String dengan Pointer #
String literal seperti "Budi" disimpan sebagai array char berukuran 5 byte (4 huruf + 1 null terminator) di memori hanya-baca. Nama string literal bisa decay menjadi const char *—pointer ke byte pertama. Karena string berakhir dengan '\0', kita bisa menelusurinya dengan loop while (*s != '\0') { ...; s++; } tanpa perlu tahu panjangnya di depan. Inilah idiom paling khas C untuk iterasi string.
Mekanisme kerjanya: saat compiler melihat const char *nama = "Budi";, compiler menyimpan alamat byte pertama huruf 'B' ke variabel nama. Ekspresi nama + 1 maju 1 byte (karena sizeof(char) == 1) ke huruf 'u', dan seterusnya hingga nama + 4 yang menunjuk ke null terminator '\0'. Operator dereference *nama membaca 1 byte char pada alamat tersebut.
#include <stdio.h>
/* Implementasi strlen dengan pointer arithmetic */
size_t panjang_string(const char *s) {
const char *mulai = s;
while (*s != '\0') {
s++;
}
return (size_t)(s - mulai);
}
int main(void) {
const char *nama = "Budi";
printf("String: \"%s\"\n", nama);
printf("Panjang (manual) : %zu\n", panjang_string(nama));
printf("Alamat byte 0 : %p -> '%c'\n", (void*)nama, *nama);
printf("Alamat byte 1 : %p -> '%c'\n", (void*)(nama + 1), *(nama + 1));
printf("Alamat byte 4 : %p -> '\\0' (null terminator)\n",
(void*)(nama + 4));
return 0;
}Output yang Dihasilkan:
String: "Budi"
Panjang (manual) : 4
Alamat byte 0 : 0x5596ff3dc008 -> 'B'
Alamat byte 1 : 0x5596ff3dc009 -> 'u'
Alamat byte 4 : 0x5596ff3dc00c -> '\0' (null terminator)Fungsi panjang_string menyimpan posisi awal mulai, lalu menggerakkan pointer s maju hingga menemui '\0'. Selisih s - mulai adalah jumlah byte (sama dengan jumlah elemen, karena sizeof(char) == 1) antara mulai dan posisi null terminator—inilah panjang string. Alamat byte bertambah 1 per huruf, persis seperti yang diharapkan dari pointer arithmetic pada char *.
Mari kita telaah baris-baris kode di atas:
- Baris 4-10 (
size_t panjang_string(const char *s) { ... }): fungsi menerimaconst char *(pointer kecharyang tidak boleh dimodifikasi melalui pointer ini). Loop majus++hingga*s == '\0', lalu mengembalikan selisih pointer. - Baris 13 (
const char *nama = "Budi";): string literal"Budi"sebenarnya adalahconst char[5](termasuk'\0') yang diletakkan di section memori hanya-baca. Deklarasiconst char *menandai bahwa kita berjanji tidak akan memodifikasi string melalui pointer ini. - Baris 16 (
printf("Panjang: %zu", panjang_string(nama));): panggil fungsi. Hasilnya4—panjang string tanpa null terminator. - Baris 17-19 (
printf("Alamat byte 0: %p -> '%c'", (void*)nama, *nama);): cetak alamat dan isi byte pertama. Setiap byte berisi satu karakter ASCII. - Baris 20-21 (
printf("Alamat byte 4: %p -> '\\0' ...", (void*)(nama + 4));):nama + 4menunjuk ke byte kelima—null terminator. Karakter ini tidak terlihat saat dicetak dengan%skarena%sberhenti di'\0'.
Operasi String dengan Pointer #
Banyak operasi string standar bisa ditulis ulang dengan pointer secara elegan. Contoh berikut menunjukkan implementasi strcpy-like dan strupr-like (mengubah huruf kecil menjadi besar). Perhatikan bagaimana pointer dst dan src bergerak maju bersamaan—idiom yang sangat khas di C.
#include <stdio.h>
/* Salin string src ke dst dengan pointer arithmetic */
void salin_string(char *dst, const char *src) {
while (*src != '\0') {
*dst = *src;
dst++;
src++;
}
*dst = '\0'; /* jangan lupa null terminator */
}
/* Ubah huruf kecil menjadi besar (in-place) */
void huruf_besar(char *s) {
while (*s != '\0') {
if (*s >= 'a' && *s <= 'z') {
*s = *s - 'a' + 'A';
}
s++;
}
}
int main(void) {
char buffer[20];
salin_string(buffer, "halo dunia");
printf("Hasil salin : %s\n", buffer);
huruf_besar(buffer);
printf("Setelah huruf_besar: %s\n", buffer);
return 0;
}Output yang Dihasilkan:
Hasil salin : halo dunia
Setelah huruf_besar: HALO DUNIAFungsi salin_string menyalin byte demi byte dari src ke dst dengan menggerakkan kedua pointer maju bersamaan. Setelah loop selesai, kita harus secara eksplisit menambah '\0' di akhir dst—karena loop berhenti saat *src == '\0' sebelum sempat menyalin terminator. Fungsi huruf_besar memodifikasi string in-place melalui pointer s—di sinilah parameter bertipe char * (bukan const char *) diperlukan agar compiler mengizinkan penugasan *s = ....