Konversi tipe secara implisit (implicit type conversion) adalah salah satu perilaku paling penting namun sering diabaikan di C. Setiap kali operator bekerja pada dua operand bertipe berbeda, atau setiap kali sebuah nilai disimpan ke variabel bertipe berbeda, compiler C melakukan konversi otomatis untuk menyamakan tipe—tanpa meminta kita menulis cast eksplisit. Pada Chapter ini kita akan menelaah tiga konteks utama konversi implisit: konversi pada assignment, konversi pada arithmetic operand (yang melibatkan integer promotions dan usual arithmetic conversions—akan diuraikan tuntas di dua Chapter berikutnya), serta default argument promotions pada pemanggilan fungsi variadic seperti printf. Kita juga akan menyoroti jebakan klasik: 7 / 2 yang menghasilkan 3 meski disimpan ke double.
Konversi pada Assignment #
Saat nilai bertipe T1 ditugaskan ke variabel bertipe T2, compiler mengonversi nilai tersebut ke tipe T2 terlebih dahulu sebelum menyimpannya. Jika T2 lebih lebar dari T1 (misalnya int ke long atau float ke double), konversi bersifat aman tanpa kehilangan data. Jika T2 lebih sempit (misalnya double ke int), pecahan dipotong (truncation) dan nilai bisa meluap (overflow).
#include <stdio.h>
int main(void) {
int i = 5;
double d = 2.5;
/* int + double -> double */
double hasil1 = i + d;
printf("%d + %.2f = %.2f (tipe: double)\n", i, d, hasil1);
/* int / int -> int (truncated), lalu disimpan ke double */
double hasil2 = 7 / 2;
printf("7 / 2 disimpan ke double = %.2f (bukan 3.5!)\n", hasil2);
/* Untuk mendapat 3.5, salah satu operand harus floating */
double hasil3 = 7.0 / 2;
printf("7.0 / 2 = %.2f\n", hasil3);
/* Konversi pada assignment: double ke int memotong pecahan */
int bulat = 3.99;
printf("3.99 disimpan ke int = %d\n", bulat);
/* Konversi char ke int (integer promotion) */
char c = 'A';
int ascii = c + 1;
printf("'%c' + 1 = %d (karakter '%c')\n", c, ascii, (char)ascii);
return 0;
}Output yang Dihasilkan:
5 + 2.50 = 7.50 (tipe: double)
7 / 2 disimpan ke double = 3.00 (bukan 3.5!)
7.0 / 2 = 3.50
3.99 disimpan ke int = 3
'A' + 1 = 66 (karakter 'B')Mari kita telaah baris-baris kode di atas:
- Baris 8 (
double hasil1 = i + d;): ekspresii + dmenggabungkanintdandouble;idikonversi kedoublemenjadi5.0, lalu dijumlahkan dengan2.5menjadi7.5; hasildoubledisimpan kehasil1. - Baris 13 (
double hasil2 = 7 / 2;): operand7dan2keduanyaint, sehingga pembagian7 / 2dievaluasi dalamintmenjadi3(pecahan dibuang); nilai3inilah yang dikonversi kedoublemenjadi3.0dan disimpan kehasil2. Inilah jebakan paling umum di C—pembagianintsudah selesai sebelum konversi kedoubleterjadi. - Baris 17 (
double hasil3 = 7.0 / 2;): operand7.0bertipedouble, sehingga operand2dikonversi menjadi2.0; pembagian berlangsung dalamdoublemenghasilkan3.5. - Baris 21 (
int bulat = 3.99;): konversidoublekeintmemotong pecahan ke arah nol; hasil3meskipun nilai asal3.99sangat dekat ke4. - Baris 25 (
int ascii = c + 1;): variabelcbertipechardipromosikan keintmelalui integer promotions sebelum dijumlahkan dengan1; hasil66(kode ASCII'B').
Demosi Tipe dan Hilangnya Data #
Ketika nilai disimpan ke variabel bertipe lebih sempit, compiler tetap melakukan konversi namun data bisa hilang. Demosi int ke short membuang bit atas; demosi double ke int memotong pecahan; demosi unsigned ke signed (atau sebaliknya dengan ukuran sama) tidak mengubah pola bit namun mengubah interpretasi nilai.
#include <stdio.h>
int main(void) {
/* Konversi saat assignment: demosi tipe */
int i = 1000000; /* nilai di dalam rentang int */
short s = i; /* int -> short: potong bit */
printf("int -> short : %d -> %d\n", i, s);
/* Demosi double ke int */
double pi = 3.14159265;
int truncated = pi;
printf("double -> int: %.8f -> %d\n", pi, truncated);
/* Konversi unsigned-signed */
unsigned int u = 4294967295U; /* UINT_MAX di 32-bit */
int signed_val = u;
printf("unsigned %u -> int %d\n", u, signed_val);
/* Konversi float -> double */
float f = 1.5f;
double d = f; /* float -> double: aman */
printf("float %.2f -> double %.2f\n", f, d);
return 0;
}Output yang Dihasilkan:
int -> short : 1000000 -> 16960
double -> int: 3.14159265 -> 3
unsigned 4294967295 -> int -1
float 1.50 -> double 1.50Pada baris ke-6, nilai 1000000 (biner 0000 0000 0000 1111 0100 0010 0100 0000) disalin ke short (16 bit) sehingga 16 bit atas hilang; sisa 16 bit bawah 0100 0010 0100 0000 merepresentasikan 16960. Pada baris ke-13, 4294967295U (semua bit 1 pada 32-bit) memiliki pola yang sama ketika diinterpretasikan sebagai int bersifat signed, yaitu -1 dalam representasi two’s complement. Konversi float ke double pada baris ke-18 bersifat aman karena double memiliki presisi yang lebih besar; tidak ada data yang hilang.
Default Argument Promotions #
Saat memanggil fungsi tanpa prototype (jarang di C modern) atau memanggil fungsi variadic seperti printf, compiler melakukan default argument promotions pada setiap argumen:
- Tipe
chardanshortdipromosikan keint(atauunsigned intjikainttidak cukup menampung). - Tipe
floatdipromosikan kedouble.
Akibatnya, format specifier %f pada printf menerima double, bukan float; %d menerima int, bukan char atau short. Inilah mengapa kita bisa melewatkan variabel char dan short langsung ke printf dengan %d tanpa cast eksplisit.
#include <stdio.h>
int main(void) {
/* Konversi di argumen fungsi variadic (default argument promotions) */
char c = 'X';
short s = 100;
float f = 3.14f;
/* default argument promotions:
- char/short -> int
- float -> double
*/
printf("char via %%d : %d\n", c);
printf("short via %%d : %d\n", s);
printf("float via %%f : %f\n", f);
/* Promosi di ekspresi campuran */
int a = 100;
long b = 2000000000L;
long sum = a + b; /* a dipromosi ke long */
printf("\nint + long = %ld (long)\n", sum);
/* Konversi tanda (signed/unsigned) */
int neg = -5;
unsigned int un = 10u;
/* neg dikonversi ke unsigned: (UINT_MAX - 4) + 10 = 5 (wrap-around mod 2^32) */
printf("neg + un via %%u : %u\n", neg + un);
return 0;
}Output yang Dihasilkan:
char via %d : 88
short via %d : 100
float via %f : 3.140000
int + long = 2000000100 (long)
neg + un via %u : 5Pada baris ke-14, variabel c bertipe char dipromosikan ke int sebelum diteruskan ke printf; nilai 'X' (kode ASCII 88) dicetak dengan %d. Pada baris ke-21, variabel a bertipe int dipromosikan ke long agar ukurannya sama dengan b; penjumlahan berlangsung dalam long menghasilkan 2000000100. Pada baris ke-27, ekspresi neg + un menggabungkan int dan unsigned int dengan rank sama; nilai neg = -5 dikonversi ke unsigned int menjadi UINT_MAX - 4 = 4294967291; ditambah 10 dan dimodulo 2^32 menghasilkan 5.
Konversi Aman dengan Cast Eksplisit #
Konversi implisit kadang tidak diinginkan atau membingungkan. Pada Chapter type casting berikutnya, kita akan melihat bagaimana operator cast (tipe) ekspresi memungkinkan kita melakukan konversi secara sadar dan terdokumentasi. Sebelum sampai di sana, perlu dipahami bahwa konversi implisit selalu terjadi—satu-satunya pertanyaan adalah apakah kita sengaja mengendalikannya atau membiarkan compiler memilih.
Konversi implisit adalah pondasi mengapa C bisa tetap ringkas walaupun memiliki banyak tipe data. Dengan memahami kapan konversi terjadi dan bagaimana data bisa hilang, kita dapat menulis kode yang aman terhadap kehilangan presisi maupun overflow.