Setelah bisa menyalin, menggabungkan, dan membandingkan string, operasi berikutnya yang sering diperlukan adalah pencarian: mencari karakter tertentu (misalnya pemisah path /) atau substring tertentu (misalnya ekstensi file .txt) di dalam string. Standard library C menyediakan strchr untuk mencari karakter tunggal dan strstr untuk mencari substring. Keduanya mengembalikan pointer ke posisi pertama yang cocok, atau NULL bila tidak ditemukan. Karena yang dikembalikan adalah pointer (bukan indeks), kita dapat langsung memakai hasilnya sebagai substring baru—teknik yang disebut pointer into string. Pada chapter ini kita akan menelusuri kedua fungsi ini, melihat bagaimana memanfaatkan pointer yang dikembalikan, dan menerapkan keduanya dalam kasus praktis.

strchr: Mencari Karakter #

strchr(s, c) mencari kemunculan pertama karakter c di string s. Fungsi mengembalikan pointer ke posisi karakter tersebut, atau NULL bila tidak ditemukan. Varian strrchr mencari dari belakang.

c
#include <stdio.h>
#include <string.h>

int main(void) {
    char s[] = "Belajar C menyenangkan";
    char *p;

    /* strchr: cari karakter pertama yang cocok */
    p = strchr(s, 'C');
    if (p != NULL) {
        printf("Ditemukan 'C' pada indeks %zu\n", (size_t)(p - s));
        printf("Substring mulai dari 'C': %s\n", p);
    }

    /* Mencari karakter yang tidak ada */
    p = strchr(s, 'z');
    printf("Hasil cari 'z': %s\n", (p == NULL) ? "NULL" : "ketemu");

    /* strchr juga bisa cari null terminator */
    p = strchr(s, '\0');
    printf("Posisi null terminator: indeks %zu\n", (size_t)(p - s));

    /* strrchr: cari dari belakang */
    char *q = strrchr(s, 'a');
    printf("'a' terakhir di indeks %zu\n", (size_t)(q - s));
    return 0;
}

Output yang Dihasilkan:

text
Ditemukan 'C' pada indeks 8
Substring mulai dari 'C': C menyenangkan
Hasil cari 'z': NULL
Posisi null terminator: indeks 22
'a' terakhir di indeks 20

Mari kita telaah baris-baris kode di atas:

  1. Baris 9 (p = strchr(s, 'C');): mencari karakter 'C' di string s. Hasilnya pointer ke posisi byte 'C', yaitu s + 8. Bila karakter tidak ditemukan, strchr mengembalikan NULL.
  2. Baris 10-13 (if (p != NULL) { ... }): kita selalu memeriksa p != NULL sebelum menggunakan hasil. Ekspresi (size_t)(p - s) menghitung indeks sebagai selisih alamat pointer p dan s. Untuk mendapatkan substring mulai dari posisi tersebut, kita dapat langsung memakai p sebagai argumen printf("%s", p)—printf akan mencetak byte mulai dari p sampai null terminator.
  3. Baris 16-17 (p = strchr(s, 'z');): karakter 'z' tidak ada di s, jadi strchr mengembalikan NULL. Kita mencetak "NULL" untuk membuat output terlihat jelas.
  4. Baris 20-21 (p = strchr(s, '\0');): menariknya, strchr dapat dipakai untuk mencari null terminator itu sendiri. Hasilnya adalah pointer ke byte null terminator—pada contoh, indeks 22 (panjang string "Belajar C menyenangkan" = 22 karakter).
  5. Baris 24-25 (q = strrchr(s, 'a');): strrchr mencari kemunculan terakhir karakter 'a' dari belakang. Ada beberapa 'a' di s (di indeks 4, 7, dan 20), dan strrchr mengembalikan yang terakhir (indeks 20).

strstr: Mencari Substring #

strstr(s, sub) mencari kemunculan pertama substring sub di string s. Sama seperti strchr, fungsi ini mengembalikan pointer ke posisi pertama atau NULL bila tidak ditemukan.

c
#include <stdio.h>
#include <string.h>

int main(void) {
    char s[] = "Halo Budi, selamat datang Budi!";
    char *p;

    /* strstr: cari substring pertama yang cocok */
    p = strstr(s, "Budi");
    if (p != NULL) {
        printf("Substring \"Budi\" di indeks %zu\n", (size_t)(p - s));
        printf("Mulai dari posisi tersebut: %s\n", p);
    }

    /* Cari substring yang tidak ada */
    p = strstr(s, "Siti");
    printf("Cari \"Siti\": %s\n", (p == NULL) ? "NULL" : "ketemu");

    /* Menghitung jumlah kemunculan substring */
    int count = 0;
    const char *cari = "Budi";
    const char *t = s;
    while ((t = strstr(t, cari)) != NULL) {
        count++;
        t += strlen(cari);   /* lanjutkan pencarian setelah match */
    }
    printf("Jumlah kemunculan \"Budi\": %d\n", count);

    /* Substring kosong mengembalikan string asli */
    p = strstr(s, "");
    printf("Cari \"\": %s\n", (p == s) ? "kembali ke awal" : "berbeda");
    return 0;
}

Output yang Dihasilkan:

text
Substring "Budi" di indeks 5
Mulai dari posisi tersebut: Budi, selamat datang Budi!
Cari "Siti": NULL
Jumlah kemunculan "Budi": 2
Cari "": kembali ke awal

Mari kita telaah baris-baris kode di atas:

  1. Baris 9 (p = strstr(s, "Budi");): mencari substring "Budi" di s. Hasilnya pointer ke byte pertama kemunculan pertama (s + 5).
  2. Baris 14 (p = strstr(s, "Siti");): substring tidak ditemukan, return NULL.
  3. Baris 18-23 (loop menghitung jumlah kemunculan): kita memakai loop yang setiap iterasi memanggil strstr untuk mencari kemunculan berikutnya. Setelah ditemukan, kita menggeser pointer t sejauh strlen(cari) byte agar pencarian berikutnya dimulai setelah posisi match saat ini. Tanpa penggeseran ini, loop akan berjalan tanpa henti karena strstr terus menemukan kemunculan yang sama. Hasil: substring "Budi" muncul 2 kali dalam s.
  4. Baris 26-27 (p = strstr(s, "");): substring kosong "" dianggap cocok di awal string. strstr mengembalikan pointer ke s[0], yang sama dengan s sendiri. Inilah mengapa p == s bernilai true.

Penerapan Praktis: Membership Test dan Splitting #

strchr adalah building block yang sering dipakai untuk membership test (apakah karakter termasuk himpunan tertentu) atau splitting (memecah string berdasarkan delimiter karakter).

c
#include <stdio.h>
#include <string.h>

/* Menggunakan strchr untuk memeriksa keanggotaan karakter */
int ada_dalam(const char *set, char c) {
    return strchr(set, c) != NULL;
}

int main(void) {
    const char *vokal = "aeiouAEIOU";

    char teks[] = "Belajar C";
    int v = 0, k = 0;
    for (size_t i = 0; teks[i] != '\0'; i++) {
        if (ada_dalam(vokal, teks[i])) {
            v++;
        } else if ((teks[i] >= 'a' && teks[i] <= 'z') ||
                   (teks[i] >= 'A' && teks[i] <= 'Z')) {
            k++;
        }
    }
    printf("Teks: %s\n", teks);
    printf("Jumlah vokal: %d\n", v);
    printf("Jumlah konsonan: %d\n", k);

    /* strchr untuk split sederhana */
    char path[] = "/usr/local/bin/program";
    char *slash = strchr(path, '/');
    if (slash != NULL) {
        *slash = '\0';   /* potong di slash pertama */
        printf("Sebelum slash pertama: \"%s\"\n", path);
        printf("Setelah slash pertama:  \"%s\"\n", slash + 1);
    }
    return 0;
}

Output yang Dihasilkan:

text
Teks: Belajar C
Jumlah vokal: 3
Jumlah konsonan: 5
Sebelum slash pertama: ""
Setelah slash pertama:  "usr/local/bin/program"

Pada contoh pertama, fungsi ada_dalam(set, c) memakai strchr untuk mengecek apakah karakter c ada di string set. Bila ada, strchr mengembalikan pointer non-NULL sehingga kondisi != NULL bernilai true. Pola ini lebih ringkas daripada loop for yang membandingkan c dengan setiap karakter di set. Pada contoh kedua, kita memakai strchr untuk menemukan slash pertama di path, lalu meng-overwrite byte tersebut dengan '\0' sehingga string asli terpotong menjadi dua bagian: path (sebelum slash) menjadi string kosong "" (karena slash ada di awal), dan slash + 1 (setelah slash) menjadi "usr/local/bin/program". Teknik in-place split seperti ini sering dipakai di parser sederhana.

Ringkasan Konsep #

strchr(s, c) mencari karakter tunggal dan mengembalikan pointer ke kemunculan pertama (atau NULL). strrchr(s, c) mencari dari belakang. strstr(s, sub) mencari substring dan mengembalikan pointer ke posisi pertama (atau NULL). Semua fungsi ini mengembalikan pointer into string—pointer ke byte di dalam string asli, bukan salinan. Untuk menghitung indeks, kurangkan pointer dengan alamat awal string: (size_t)(p - s). Untuk mendapatkan substring mulai dari posisi tersebut, gunakan p langsung sebagai string. Loop while ((t = strstr(t, sub)) != NULL) { ... t += strlen(sub); } adalah pola umum untuk menghitung atau memproses semua kemunculan substring. Pada chapter berikutnya kita akan membahas cara membaca string dari standard input dengan aman menggunakan fgets.