Type casting adalah konversi tipe yang kita lakukan secara eksplisit menggunakan operator cast (tipe) ekspresi. Berbeda dari konversi implisit yang terjadi diam-diam, cast menyatakan niat: “saya tahu tipe awal dan tipe tujuan, dan saya bertanggung jawab atas konversi ini.” Pada Chapter ini kita akan menelaah kapan cast diperlukan, kapan cast hanya menyembunyikan masalah, dan bagaimana cast berinteraksi dengan precedence operator. Kita juga akan melihat tiga pola cast paling lazim: cast untuk memperbaiki pembagian int menjadi double, cast pointer untuk membaca byte mentah atau mengakses memori yang dialokasikan malloc, dan cast untuk menekan warning perbandingan signed/unsigned.

Sintaks Cast #

Operator cast memiliki bentuk (tipe) ekspresi. Operator ini bersifat unary dengan precedence tinggi (tingkat 2 dalam tabel precedence), sehingga (double)a / b di-parse sebagai ((double)a) / b. Jika kita menulis (double)(a / b), cast terjadi setelah pembagian integer selesai—hasil yang sudah dibulatkan tetap dibulatkan.

c
#include <stdio.h>

int main(void) {
    int a = 7;
    int b = 2;

    /* Tanpa cast: pembagian integer */
    double tanpa_cast = a / b;
    printf("a / b           = %.4f (integer division)\n", tanpa_cast);

    /* Cast salah satu operand ke double */
    double dengan_cast = (double)a / b;
    printf("(double)a / b   = %.4f\n", dengan_cast);

    /* Cast keduanya */
    double dua_cast = (double)a / (double)b;
    printf("(double)a / (double)b = %.4f\n", dua_cast);

    /* Cast pada hasil sudah terlambat */
    double terlambat = (double)(a / b);
    printf("(double)(a / b) = %.4f (sudah dibulatkan dulu!)\n", terlambat);
    return 0;
}

Output yang Dihasilkan:

text
a / b           = 3.0000 (integer division)
(double)a / b   = 3.5000
(double)a / (double)b = 3.5000
(double)(a / b) = 3.0000 (sudah dibulatkan dulu!)

Mari kita telaah baris-baris kode di atas:

  1. Baris 7 (double tanpa_cast = a / b;): pembagian 7 / 2 dilakukan dalam int menghasilkan 3, baru dikonversi implisit ke double 3.0 saat disimpan ke tanpa_cast.
  2. Baris 11 (double dengan_cast = (double)a / b;): cast (double)a mengubah 7 menjadi 7.0 sebelum pembagian; operand b ikut dipromosikan ke double melalui usual arithmetic conversions, sehingga pembagian berlangsung dalam double menghasilkan 3.5.
  3. Baris 15 (double terlambat = (double)(a / b);): tanda kurung di sekitar a / b memaksa pembagian integer dilakukan lebih dulu menjadi 3; cast (double)3 kemudian menghasilkan 3.0. Inilah kesalahan umum: cast ditempatkan terlalu jauh dari operand yang ingin kita ubah.
  4. Cast pada salah satu operand saja sudah cukup untuk memaksa seluruh pembagian berlangsung dalam double; cast kedua operand tidak menambah presisi namun kadang dipakai untuk kejelasan dokumentasi.

Cast pada Pointer #

Cast pointer adalah salah satu pemakaian paling kuat namun berbahaya dari operator cast. Kita dapat mengubah cara compiler menafsirkan memori yang sama dengan mengubah tipe pointer. Pola umum: membaca byte mentah dari sebuah int dengan cast (unsigned char *)&nilai, atau mengakses memori yang dikembalikan malloc sebagai int *.

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

int main(void) {
    /* Cast pointer: membaca byte mentah */
    int nilai = 0x41424344;   /* 'ABCD' di little-endian */
    unsigned char *byte = (unsigned char *)&nilai;
    printf("int 0x%X sebagai 4 byte: ", nilai);
    for (int i = 0; i < 4; i++) {
        printf("0x%02X ", byte[i]);
    }
    printf("\n");

    /* Cast void* dari malloc */
    void *mem = malloc(4);
    int *ip = (int *)mem;
    *ip = 1234;
    printf("Via cast pointer: %d\n", *ip);
    free(mem);

    /* Cast untuk suppress warning */
    int n = -5;
    size_t len = 10;
    if ((size_t)n < len) {   /* cast eksplisit */
        printf("n (cast ke size_t) < len\n");
    } else {
        printf("n (cast ke size_t) >= len\n");
    }
    return 0;
}

Output yang Dihasilkan:

text
int 0x41424344 sebagai 4 byte: 0x44 0x43 0x42 0x41 
Via cast pointer: 1234
n (cast ke size_t) >= len

Pada baris ke-7, cast (unsigned char *)&nilai mengubah pointer to int menjadi pointer to unsigned char. Karena unsigned char berukuran 1 byte, kita dapat mengakses byte-byte individual dari nilai. Pada mesin little-endian, byte paling rendah disimpan lebih dulu, sehingga output menampilkan 0x44 0x43 0x42 0x41 (kebalikan urutan dari notasi heksadesimal 0x41424344). Pada baris ke-15, malloc(4) mengembalikan void *; cast (int *)mem diperlukan untuk mengakses memori sebagai int (meskipun di C, konversi void * ke pointer bertipe sebenarnya implisit, cast eksplisit sering dipakai untuk kejelasan). Pada baris ke-21, cast (size_t)n mengubah -5 menjadi nilai size_t yang sangat besar (sekitar 18446744073709551611 pada 64-bit), sehingga perbandingan dengan len = 10 menghasilkan false—cast tidak memperbaiki logika, hanya menyembunyikan warning.

Cast untuk Pembulatan dan Enum #

Cast sering dipakai untuk membulatkan bilangan floating-point ke int. Karena cast double ke int memotong pecahan ke arah nol, kita dapat menambah offset sebelum cast untuk mendapatkan berbagai jenis pembulatan.

c
#include <stdio.h>

/* Demo: cast untuk round bilangan positif */
int pembulatan_ke_atas(double x) {
    return (int)(x + 0.999999);
}

int main(void) {
    double harga[] = {17500.0, 17500.4, 17500.6, 17500.99};
    int n = sizeof(harga) / sizeof(harga[0]);

    for (int i = 0; i < n; i++) {
        int bulat_bawah = (int)harga[i];          /* truncate */
        int bulat_atas = pembulatan_ke_atas(harga[i]);
        int bulat_normal = (int)(harga[i] + 0.5);  /* round half up */
        printf("%.2f -> bawah=%d, atas=%d, normal=%d\n",
               harga[i], bulat_bawah, bulat_atas, bulat_normal);
    }

    /* Cast pada enum ke int */
    enum Warna { MERAH, HIJAU, BIRU };
    enum Warna w = BIRU;
    int indeks = (int)w;
    printf("\nenum BIRU -> int %d\n", indeks);
    return 0;
}

Output yang Dihasilkan:

text
17500.00 -> bawah=17500, atas=17500, normal=17500
17500.40 -> bawah=17500, atas=17501, normal=17500
17500.60 -> bawah=17500, atas=17501, normal=17501
17500.99 -> bawah=17500, atas=17501, normal=17501

enum BIRU -> int 2

Pada baris ke-5, fungsi pembulatan_ke_atas menambahkan 0.999999 sebelum cast ke int; karena 0.999999 sedikit kurang dari 1.0, nilai yang sudah bulat seperti 17500.0 tidak akan naik, sedangkan nilai dengan pecahan apapun akan dibulatkan ke atas. Pada baris ke-15, cast (int)harga[i] membuang pecahan (truncate ke bawah). Pada baris ke-17, cast (int)(harga[i] + 0.5) menggeser batas pembulatan ke 0.5, sehingga pecahan 0.5 ke atas dibulatkan ke bilangan bulat berikutnya—pola “round half up” yang paling umum.

Kapan Cast Tidak Diperlukan #

Banyak situasi di mana pemula menulis cast yang sebenarnya tidak diperlukan atau bahkan berbahaya:

  • int x = (int)5; — cast dari int ke int tidak melakukan apa pun.
  • malloc di C mengembalikan void * yang otomatis dikonversi ke tipe pointer tujuan; cast eksplisit (int *)malloc(4) tidak diperlukan dan dapat menyembunyikan bug jika #include <stdlib.h> tertinggal (di C++ cast wajib, di C tidak).
  • (void)x; — cast ke void dipakai untuk menekan warning variabel tidak terpakai, namun lebih baik menandai dengan __attribute__((unused)) atau (void)x; di akhir deklarasi.

Cast eksplisit adalah alat untuk berkomunikasi dengan pembaca kode: “saya sengaja melakukan konversi ini”. Gunakan cast ketika konversi memang diperlukan dan ketika niat perlu ditegaskan. Hindari cast yang hanya menyembunyikan warning tanpa memperbaiki akar masalah.