Membaca angka dari input pengguna atau dari file konfigurasi selalu menghasilkan string. Agar bisa dipakai dalam operasi aritmetika, string tersebut harus dikonversi menjadi tipe numerik (int, long, double). Standard library C menyediakan dua keluarga fungsi: atoi/atol/atof yang sederhana tetapi tidak memiliki error handling, dan strtol/strtod/strtoul yang lengkap dengan mekanisme deteksi error melalui parameter endptr dan variabel global errno. Pemula sering memakai atoi karena lebih ringkas, tetapi atoi tidak bisa membedakan input "0" dari input invalid seperti "bukan angka"—keduanya mengembalikan 0. Pada chapter ini kita akan mendemonstrasikan keterbatasan atoi dan menunjukkan bagaimana strtol menyelesaikannya dengan mekanisme error yang proper.

atoi: Sederhana tapi Tanpa Error Handling #

atoi(s), atol(s), dan atof(s) mengkonversi string ke int, long, dan double secara langsung. Fungsi-fungsi ini mudah dipakai tetapi tidak memiliki mekanisme untuk mendeteksi input invalid atau overflow.

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

int main(void) {
    /* atoi/atol/atof: konversi cepat tanpa error handling */
    int  a = atoi("1234");
    long b = atol("9999999999");
    double c = atof("3.14");
    int  d = atoi("bukan angka");

    printf("atoi(\"1234\")        = %d\n", a);
    printf("atol(\"9999999999\")  = %ld\n", b);
    printf("atof(\"3.14\")        = %f\n", c);
    printf("atoi(\"bukan angka\") = %d  (tidak ada cara deteksi error!)\n", d);
    return 0;
}

Output yang Dihasilkan:

text
atoi("1234")        = 1234
atol("9999999999")  = 9999999999
atof("3.14")        = 3.140000
atoi("bukan angka") = 0  (tidak ada cara deteksi error!)

Pada contoh di atas, atoi("1234") = 1234 dan atol("9999999999") = 9999999999 bekerja normal pada input valid. atof("3.14") = 3.14 juga benar. Akan tetapi, atoi("bukan angka") = 0—tidak ada cara membedakan apakah pengguna benar-benar memasukkan 0 atau memasukkan teks yang tidak valid. C standard mengatakan: “If the value of the result cannot be represented, the behavior is undefined.” Artinya, atoi("999999999999999999999") yang melampaui batas int adalah undefined behavior—hasilnya tidak terprediksi. Untuk kode produksi, keterbatasan ini tidak dapat diterima.

strtol: Konversi dengan Error Handling #

strtol(s, &endptr, base) mengkonversi string ke long dengan tiga lapisan error handling: (1) parameter endptr memberi tahu pemanggil sampai posisi mana string berhasil dikonversi; (2) variabel global errno di-set ke ERANGE bila hasil melampaui batas long; (3) bila endptr == s, tidak ada karakter yang terkonversi sama sekali—artinya input bukan angka valid. Parameter base mengizinkan konversi bilangan berbasis 10 (desimal), 16 (heksadesimal), 8 (oktal), atau 0 (auto-detect dari prefix).

c
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>

int main(void) {
    const char *input[] = {"1234", "-99", "0x1F", "  42abc", "bukan angka", "999999999999999999999"};
    int n = 6;

    for (int i = 0; i < n; i++) {
        errno = 0;
        char *endptr = NULL;
        long val = strtol(input[i], &endptr, 10);

        printf("Input: %-25s -> ", input[i]);
        printf("val=%ld", val);

        /* Cek apakah ada karakter yang tidak terkonversi */
        if (*endptr != '\0') {
            printf("  sisa: \"%s\"", endptr);
        }
        /* Cek overflow */
        if (errno == ERANGE) {
            printf("  [OVERFLOW]");
        }
        /* Cek tidak ada konversi sama sekali */
        if (endptr == input[i]) {
            printf("  [TIDAK ADA ANGKA]");
        }
        printf("\n");
    }

    /* Base 16 untuk hex */
    errno = 0;
    char *e;
    long hex = strtol("0x1F", &e, 16);
    printf("strtol(\"0x1F\", base 16) = %ld\n", hex);

    return 0;
}

Output yang Dihasilkan:

text
Input: 1234                      -> val=1234
Input: -99                       -> val=-99
Input: 0x1F                      -> val=0  sisa: "x1F"
Input:   42abc                   -> val=42  sisa: "abc"
Input: bukan angka               -> val=0  sisa: "bukan angka"  [TIDAK ADA ANGKA]
Input: 999999999999999999999     -> val=9223372036854775807  [OVERFLOW]
strtol("0x1F", base 16) = 31

Mari kita telaah baris-baris kode di atas:

  1. Baris 12 (errno = 0;): kita mereset errno ke 0 sebelum setiap pemanggilan strtol. Ini wajib karena errno tidak pernah di-clear oleh library—bila tidak di-reset, kita bisa membaca nilai error dari pemanggilan sebelumnya.
  2. Baris 14 (long val = strtol(input[i], &endptr, 10);): argumen kedua adalah alamat pointer endptr. Setelah pemanggilan, endptr menunjuk ke karakter pertama yang tidak ikut dikonversi. Argumen ketiga 10 menyatakan base desimal.
  3. Baris 20-22 (if (*endptr != '\0') { ... }): bila endptr tidak menunjuk ke null terminator, ada karakter sisa yang tidak terkonversi. Pada input "0x1F" dengan base 10, hanya "0" yang terkonversi (memberi val=0), lalu "x1F" adalah sisa.
  4. Baris 26-28 (if (errno == ERANGE) { ... }): bila hasil melebihi LONG_MAX atau LONG_MIN, strtol mengembalikan nilai clamped (LONG_MAX atau LONG_MIN) dan men-set errno = ERANGE. Pada input "999999999999999999999" (21 digit), strtol mengembalikan 9223372036854775807 (= LONG_MAX di sistem 64-bit) dan set errno.
  5. Baris 31-33 (if (endptr == input[i]) { ... }): bila endptr masih menunjuk ke awal string, tidak ada karakter sama sekali yang terkonversi. Inilah cara membedakan input "0" (yang valid, val=0, endptr menunjuk ke null terminator) dari input "bukan angka" (val=0 juga, tetapi endptr tetap di awal).
  6. Baris 41 (strtol("0x1F", &e, 16)): dengan base 16, prefix "0x" diabaikan dan "1F" dikonversi sebagai heksadesimal. Hasil: 31 (= 1*16 + 15).

strtod: Konversi Bilangan Floating Point #

strtod(s, &endptr) adalah pasangan strtol untuk bilangan floating point. Selain mendukung notasi standar seperti "3.14", strtod juga mendukung notasi ilmiah "3.14e2" dan nilai khusus "inf" / "nan".

c
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

/* strtod: konversi double dengan error handling */
int main(void) {
    const char *s = "3.14159e2 adalah pi kali 100";
    char *endptr;
    errno = 0;
    double d = strtod(s, &endptr);

    printf("Hasil konversi: %f\n", d);
    printf("Sisa string  : \"%s\"\n", endptr);

    /* Konversi tidak valid: tidak ada digit sama sekali */
    errno = 0;
    double bad = strtod("abc-test", &endptr);
    printf("strtod(\"abc-test\") = %f, endptr == awal? %s\n",
           bad, (endptr[0] == 'a') ? "YA (tidak ada konversi)" : "TIDAK");

    return 0;
}

Output yang Dihasilkan:

text
Hasil konversi: 314.159000
Sisa string  : " adalah pi kali 100"
strtod("abc-test") = 0.000000, endptr == awal? YA (tidak ada konversi)

Pada contoh pertama, strtod("3.14159e2 adalah pi kali 100", &endptr) mengkonversi "3.14159e2" sebagai notasi ilmiah (3.14159 × 10² = 314.159). endptr menunjuk ke karakter spasi setelah "e2"—ini adalah sisa string yang tidak terkonversi. Pada contoh kedua, strtod("abc-test", &endptr) tidak menemukan digit sama sekali sehingga endptr tetap di awal string (endptr[0] == 'a'). Inilah cara mendeteksi input non-numerik—sesuatu yang tidak bisa dilakukan atof yang selalu mengembalikan 0.0 untuk input invalid.

Ringkasan Konsep #

Konversi string ke angka di C dapat dilakukan dengan dua keluarga fungsi. atoi/atol/atof sederhana tetapi tidak menyediakan cara membedakan input "0" yang sah dari input invalid yang juga mengembalikan 0. strtol/strtoul/strtod adalah pilihan yang proper: parameter endptr menunjukkan sampai mana string terkonversi (sehingga kita tahu apakah ada karakter sisa), variabel global errno di-set ke ERANGE bila overflow, dan endptr == s menandakan tidak ada konversi sama sekali. Untuk validasi ketat, periksa ketiga indikator: endptr != s (ada konversi), *endptr == '\0' (tidak ada karakter sisa), errno != ERANGE (tidak overflow). Selalu reset errno = 0 sebelum pemanggilan karena library tidak pernah meng-clear nilai ini. Pada chapter berikutnya kita akan membahas isu encoding—bagaimana karakter non-ASCII (misalnya é, kanji, emoji) direpresentasikan di C.