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.
#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:
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:
- Baris 7 (
double tanpa_cast = a / b;): pembagian7 / 2dilakukan dalamintmenghasilkan3, baru dikonversi implisit kedouble3.0saat disimpan ketanpa_cast. - Baris 11 (
double dengan_cast = (double)a / b;): cast(double)amengubah7menjadi7.0sebelum pembagian; operandbikut dipromosikan kedoublemelalui usual arithmetic conversions, sehingga pembagian berlangsung dalamdoublemenghasilkan3.5. - Baris 15 (
double terlambat = (double)(a / b);): tanda kurung di sekitara / bmemaksa pembagian integer dilakukan lebih dulu menjadi3; cast(double)3kemudian menghasilkan3.0. Inilah kesalahan umum: cast ditempatkan terlalu jauh dari operand yang ingin kita ubah. - 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 *.
#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:
int 0x41424344 sebagai 4 byte: 0x44 0x43 0x42 0x41
Via cast pointer: 1234
n (cast ke size_t) >= lenPada 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.
#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:
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 2Pada 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 dariintkeinttidak melakukan apa pun.mallocdi C mengembalikanvoid *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 kevoiddipakai 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.