Integer promotions adalah konversi otomatis yang dilakukan compiler C pada tipe bertipe kecil—char, short, dan tipe enumerasi—menjadi tipe int (atau unsigned int dalam kasus khusus) sebelum operator apa pun diterapkan. Banyak pemula terkejut mengetahui bahwa ekspresi short x = 30000; short y = 30000; short hasil = x + y; sebenarnya tidak menjumlahkan dua short secara langsung; kedua operand dipromosikan ke int lebih dulu, dijumlahkan dalam int, lalu hasil int dikonversi kembali ke short saat disimpan. Pada Chapter ini kita akan menelaah aturan integer promotions secara mendalam, melihat bagaimana promosi ini mencegah overflow yang seharusnya tidak terjadi, serta bagaimana promosi memengaruhi operator unary seperti ~ dan -.
Aturan Integer Promotions #
ISO/IEC 9899 §6.3.1.1 mengatur bahwa tipe dengan integer conversion rank lebih rendah dari int (yaitu char, signed char, unsigned char, short, unsigned short, dan tipe enumerasi) secara otomatis dipromosikan ke int pada konteks berikut:
- Operand operator aritmetika (
+,-,*,/,%). - Operand operator bitwise (
&,|,^,~,<<,>>). - Operand operator unary
+,-,~. - Operand operator relasional dan logika.
- Operand pertama dari operator conditional
? :. - Argumen fungsi variadic (sebagai bagian dari default argument promotions).
Pemilihan tipe tujuan: jika int dapat merepresentasikan semua nilai tipe asal, promosi ke int; jika tidak (misalnya unsigned short pada platform 16-bit di mana int juga 16-bit), promosi ke unsigned int.
#include <stdio.h>
int main(void) {
char a = 200; /* rentang char hanya -128..127 (signed) atau 0..255 (unsigned) */
char b = 100;
/* Ekspresi a + b: keduanya dipromosikan ke int */
int sum = a + b;
printf("char a = %d, b = %d\n", (int)a, (int)b);
printf("a + b (dipromosikan ke int) = %d\n", sum);
/* short + short di int */
short x = 30000;
short y = 30000;
int hasil = x + y;
printf("\nshort x = %d, y = %d\n", (int)x, (int)y);
printf("x + y (di int) = %d (tidak overflow int!)\n", hasil);
/* Jika hasil disimpan kembali ke short, terjadi overflow */
short hasil_short = x + y; /* int -> short, mod 2^16 */
printf("disimpan ke short = %d\n", (int)hasil_short);
return 0;
}Output yang Dihasilkan:
char a = -56, b = 100
a + b (dipromosikan ke int) = 44
short x = 30000, y = 30000
x + y (di int) = 60000 (tidak overflow int!)
disimpan ke short = -5536Mari kita telaah baris-baris kode di atas:
- Baris 4 (
char a = 200;): literal200melebihi rentangsigned char(-128..127); compiler mengonversi200ke representasicharyang menjadi-56(karena two’s complement). - Baris 8 (
int sum = a + b;): kedua operandadanbdipromosikan daricharkeintsebelum dijumlahkan;(-56) + 100 = 44; hasilint44disimpan kesum. - Baris 14 (
int hasil = x + y;): keduashortdipromosikan keint;30000 + 30000 = 60000dijumlahkan dalamint(yang memiliki rentang hingga sekitar 2 miliar). Hasil60000tidak meluapint. - Baris 18 (
short hasil_short = x + y;): walaupun penjumlahan dilakukan dalamintmenghasilkan60000, hasil ini disimpan kembali keshort(rentang-32768..32767); nilai60000melampaui rentangshort, sehingga disimpan sebagai-5536(modulo2^16diinterpretasikan sebagai signed).
Promosi signed dan unsigned char #
Perilaku promosi berbeda untuk signed char versus unsigned char. Karena int cukup besar untuk menampung semua nilai keduanya, keduanya dipromosikan ke int. Namun nilai hasil promosi berbeda: signed char negatif tetap negatif sebagai int, sedangkan unsigned char positif tetap positif sebagai int.
#include <stdio.h>
int main(void) {
/* char sebagai signed: rentang -128..127 */
signed char sc = 200; /* terpotong ke -56 */
/* char sebagai unsigned: rentang 0..255 */
unsigned char uc = 200;
printf("signed char sc = 200 -> %d\n", (int)sc);
printf("unsigned char uc = 200 -> %d\n", (int)uc);
/* Hasil promosi ke int */
int promo_sc = sc;
int promo_uc = uc;
printf("promosi signed char -> int %d\n", promo_sc);
printf("promosi unsigned char -> int %d\n", promo_uc);
/* Promosi pada unary operator ~ */
unsigned char mask = 0x0F; /* 0000 1111 */
unsigned int inverted = ~mask;
printf("\nunsigned char 0x%02X di-invert:\n", mask);
printf(" ~mask sebagai unsigned int = 0x%08X\n", inverted);
printf(" (lihat bit atas semua 1 karena promosi)\n");
return 0;
}Output yang Dihasilkan:
signed char sc = 200 -> -56
unsigned char uc = 200 -> 200
promosi signed char -> int -56
promosi unsigned char -> int 200
unsigned char 0x0F di-invert:
~mask sebagai unsigned int = 0xFFFFFFF0
(lihat bit atas semua 1 karena promosi)Pada baris ke-19, ekspresi ~mask terlihat seolah-olah membalik 8 bit 0x0F menjadi 0xF0. Namun karena mask bertipe unsigned char, ia dipromosikan ke int terlebih dahulu (32-bit) menjadi 0x0000000F, baru di-invert menjadi 0xFFFFFFF0. Inilah sebabnya operator ~ pada tipe kecil selalu menghasilkan bit 1 di seluruh bit atas—efek yang sering mengejutkan pemula. Untuk mendapatkan 0xF0 saja, kita perlu cast eksplisit: (unsigned char)~mask.
Promosi pada Operator Unary #
Operator unary -, +, dan ~ juga memicu integer promotions. Operator negasi - pada unsigned short menghasilkan int, bukan unsigned short. Inilah mengapa -us di mana us bertipe unsigned short tidak otomatis berubah menjadi 0xFFFF atau nilai negatif; promosi ke int membuat hasilnya 0 (jika us == 0) atau nilai negatif dalam int.
#include <stdio.h>
#include <limits.h>
int main(void) {
/* Demonstrasikan integer promotion pada unary minus */
unsigned short us = 0;
int neg = -us; /* us dipromosikan ke int (karena short) lalu di-negate */
printf("unsigned short 0, -us = %d (int)\n", neg);
/* Perbandingan: short vs int constant */
short s = -1;
int i = -1;
if (s == -1) {
printf("short -1 == int -1 (setelah promosi)\n");
}
/* Promosi pada bitwise shift */
unsigned char c = 1;
unsigned long shifted = ((unsigned int)c) << 24; /* eksplisit promosi */
printf("1 << 24 = 0x%lX\n", shifted);
/* Tanpa promosi (cast ke unsigned char), shift akan terpotong */
unsigned char trunc = (unsigned char)(c << 24);
printf("(unsigned char)(1 << 24) = 0x%02X (terpotong ke 0)\n", trunc);
return 0;
}Output yang Dihasilkan:
unsigned short 0, -us = 0 (int)
short -1 == int -1 (setelah promosi)
1 << 24 = 0x1000000
(unsigned char)(1 << 24) = 0x00 (terpotong ke 0)Pada baris ke-7, ekspresi -us dengan us = 0 menghasilkan 0; karena us dipromosikan ke int lebih dulu menjadi 0, lalu di-negate menjadi 0 (negasi nol tetap nol). Pada baris ke-13, perbandingan s == -1 memicu promosi s ke int; karena s = -1 dan -1 (sebagai literal int) sama, perbandingan menghasilkan 1. Pada baris ke-18, cast eksplisit (unsigned int)c memaksa promosi sebelum shift; hasil 1 << 24 adalah 16777216 (0x1000000) yang muat dalam unsigned long. Pada baris ke-22, hasil c << 24 (yang sebenarnya 16777216 di int) di-cast kembali ke unsigned char sehingga 24 bit atas terpotong, menyisakan 0x00 di byte terendah.
Mengapa Integer Promotions Ada #
Aturan integer promotions bukan keputusan arbitrer; aturan ini lahir dari pertimbangan praktis:
- Efisiensi historis: CPU 32-bit dan 64-bit modern bekerja paling efisien pada operand berukuran
int. Pemrosesan tipe 8-bit atau 16-bit sering kali membutuhkan instruksi tambahan untuk ekstensi tanda dan masking. - Konsistensi: hasil operator seperti
~,-, dan+pada tipe kecil yang sama selalu menghasilkan tipe yang konsisten (int), terlepas dari arsitektur target. - Pencegahan overflow tak terduga: tanpa promosi,
char(127) + char(1)akan meluap menjadi-128segera setelah penjumlahan; promosi keintmenghasilkan128yang valid.
Dengan pemahaman integer promotions, kita siap melangkah ke topik berikutnya: usual arithmetic conversions yang menentukan tipe akhir ketika dua operand bertipe berbeda (misalnya int + long atau int + double).