Topik ini membahas variabel dan tipe fundamental C++ beserta batas perilakunya. Materinya mencakup cara mendeklarasikan variabel bertipe int, double, char, dan bool, pengukuran ukuran tipe dengan sizeof, serta overflow pada tipe bilangan bulat. Setiap perilaku diperagakan dengan program kecil yang dapat dijalankan, sehingga klaim pada prosa selalu bisa diuji ulang di terminal.
Mendeklarasikan variabel dan menginisialisasinya#
Variabel adalah nama yang dipasangkan dengan tipe dan lokasi penyimpanan untuk satu nilai. Deklarasinya ditulis dengan pola tipe nama = nilai awal, dan pemberian nilai awal saat deklarasi disebut inisialisasi. Program berikut mendeklarasikan satu variabel untuk setiap tipe fundamental, lalu mencetak semuanya.
#include <iostream>
int main() {
int jumlahApel = 12;
double hargaSatuan = 3500.5;
char inisial = 'A';
bool stokTersedia = true;
std::cout << "jumlahApel = " << jumlahApel << "\n";
std::cout << "hargaSatuan = " << hargaSatuan << "\n";
std::cout << "inisial = " << inisial << "\n";
std::cout << "stokTersedia = " << stokTersedia << "\n";
return 0;
}jumlahApel = 12
hargaSatuan = 3500.5
inisial = A
stokTersedia = 1Tipe int menyimpan bilangan bulat, tipe double menyimpan bilangan pecahan berpresisi ganda, tipe char menyimpan satu karakter seperti 'A', dan tipe bool hanya menerima true atau false. Perhatikan bahwa nilai true tercetak sebagai 1, karena stream mengonversi bool menjadi bilangan sebelum dicetak. Variabel yang dideklarasikan tanpa inisialisasi berisi nilai tak tentu, sehingga inisialisasi saat deklarasi patut dijadikan kebiasaan.
Tiga bentuk inisialisasi#
C++ menyediakan tiga bentuk inisialisasi yang setara untuk kasus sederhana, yaitu inisialisasi salin int a = 5;, inisialisasi langsung int a(5);, dan inisialisasi kurawal int a{5};. Bentuk kurawal menawarkan jaminan tambahan, yakni menolak konversi yang menyempitkan nilai (narrowing conversion). Deklarasi int x = 3.9; sah menurut compiler dan menghasilkan 3, sedangkan int x{3.9}; ditolak sebelum program dihasilkan. Percobaan berikut memeragakan penolakan tersebut.
# berkas dengan inisialisasi kurawal yang menyempitkan nilai
cat > /tmp/penyempitan.cpp << 'EOF'
int main() {
int nilai{3.9};
return 0;
}
EOF
# compiler menolaknya saat kompilasi
g++ /tmp/penyempitan.cpp -o /tmp/penyempitan/tmp/penyempitan.cpp: In function ‘int main()’:
/tmp/penyempitan.cpp:2:15: error: narrowing conversion of ‘3.8999999999999999e+0’ from ‘double’ to ‘int’ [-Wnarrowing]
2 | int nilai{3.9};
| ^~~Karena penolakan terjadi saat kompilasi, kehilangan data tertangkap jauh sebelum program dijalankan. Bentuk kurawal juga melayani inisialisasi tanpa nilai, misalnya int hitungan{}; yang diisi nol, sehingga variabel tidak berisi nilai tak tentu. Demi jaminan tersebut, inisialisasi kurawal patut dijadikan bentuk bawaan, sedangkan dua bentuk lainnya dikenali agar kode pustaka lama tetap terbaca.
Litera angka dan akhirannya#
Nilai yang ditulis langsung di dalam kode disebut litera (literal), dan setiap litera memiliki tipe yang ditentukan oleh bentuk penulisannya. Tanda ' dapat diselipkan pada litera bilangan sebagai pemisah digit tanpa mengubah nilainya, sedangkan akhiran LL menaikkan tipe menjadi long long, akhiran u menandai unsigned, dan akhiran f menandai float. Program berikut membandingkan beberapa litera beserta akhirannya.
#include <iostream>
int main() {
int penduduk = 275'000'000;
long long jarak = 299'792'458LL;
unsigned int sisi = 24u;
float suhu = 36.5f;
std::cout << "penduduk = " << penduduk << "\n";
std::cout << "jarak = " << jarak << "\n";
std::cout << "sisi = " << sisi << "\n";
std::cout << "suhu = " << suhu << "\n";
return 0;
}penduduk = 275000000
jarak = 299792458
sisi = 24
suhu = 36.5Akhiran berguna ketika nilai berisiko melebihi rentang int pada perhitungan lanjutannya, atau ketika fungsi yang dipanggil menuntut tipe argumen yang persis. Pemisah digit tidak mengubah program sama sekali dan hanya memudahkan pembacaan bilangan yang panjang. Tanpa pemisah itu, menghitung jumlah nol pada 275000000 dengan mata telanjang rawan keliru.
Ukuran tipe dan operator sizeof#
Setiap tipe menempati sejumlah byte memori, dan operator sizeof melaporkan jumlahnya dalam satuan byte. Nilai yang dilaporkan bisa berbeda antarplatform, tetapi pada platform umum seperti Linux x86-64 ukurannya konsisten. Program berikut mencetak ukuran lima tipe yang sering dipakai.
#include <iostream>
int main() {
std::cout << "Ukuran int : " << sizeof(int) << " byte\n";
std::cout << "Ukuran long long : " << sizeof(long long) << " byte\n";
std::cout << "Ukuran double : " << sizeof(double) << " byte\n";
std::cout << "Ukuran char : " << sizeof(char) << " byte\n";
std::cout << "Ukuran bool : " << sizeof(bool) << " byte\n";
return 0;
}Ukuran int : 4 byte
Ukuran long long : 8 byte
Ukuran double : 8 byte
Ukuran char : 1 byte
Ukuran bool : 1 bytesizeof adalah operator, bukan fungsi biasa, sehingga operator ini dapat menerima nama tipe maupun ekspresi. Hasilnya diperoleh saat kompilasi untuk ekspresi yang bertipe statis, dan tipe hasilnya adalah std::size_t, yaitu tipe bilangan bulat tak bertanda yang dirancang untuk ukuran memori. Ukuran inilah yang menentukan rentang nilai sebuah tipe, dan topik overflow di bawah menunjukkan akibatnya.
Overflow pada tipe unsigned#
Tipe bilangan bulat tak bertanda seperti unsigned int berperilaku aritmetika modulo. Apabila hasil perhitungan melebihi batas atas, nilainya menggulung ke nilai terkecil, dan sebaliknya. Perilaku ini terdefinisi dengan baik oleh standar, sehingga hasilnya dapat diprediksi di semua platform.
#include <iostream>
int main() {
unsigned int stok = 0;
stok = stok - 1; // menggulung ke nilai terbesar unsigned int
std::cout << "stok sekarang: " << stok << "\n";
std::cout << "stok + 1 : " << stok + 1 << "\n";
return 0;
}stok sekarang: 4294967295
stok + 1 : 0unsigned int berukuran 4 byte atau 32 bit, sehingga nilai terbesarnya adalah 4294967295. Mengurangi 0 dengan 1 menggulung ke nilai terbesar tersebut, sedangkan menambahkan 1 kembali menggulung ke 0. Inilah alasan deklarasi unsigned int stok = 0; untuk nilai yang tidak seharusnya negatif berisiko: satu pengurangan yang salah menghasilkan stok yang tampak sangat besar.
Signed overflow adalah undefined behavior#
Perilaku pada tipe bertanda seperti int berbeda sepenuhnya. Apabila perhitungan bertipe signed melebihi rentangnya, yang terjadi adalah undefined behavior, yaitu keadaan yang tidak ditetapkan perilakunya oleh standar. Compiler diperbolehkan berasumsi kejadian itu tidak pernah terjadi, sehingga hasilnya bisa berupa nilai yang menggulung, nilai yang terpotong, cabang program yang dihilangkan, atau perilaku lain yang berubah ketika level optimasi berbeda.
// tidak-dijalankan (signed overflow adalah undefined behavior, hasilnya tidak dapat dijamin)
#include <iostream>
int main() {
int stok = 2147483647;
stok = stok + 1; // undefined behavior, bukan sekadar menggulung
std::cout << stok << "\n";
return 0;
}Nilai 2147483647 adalah batas atas int pada platform 32 bit atau lebih. Contoh di atas sengaja tidak dijalankan karena keluarannya tidak wajib sama antarcompiler, antarlevel optimasi, maupun antarpeluncuran. Cara yang benar untuk menghindarinya adalah memilih tipe dengan rentang memadai, misalnya long long, atau memeriksa batas sebelum berhitung.
Memilih tipe untuk suatu kebutuhan#
Pemilihan tipe mengikuti isi data yang akan disimpan, bukan selera. Tabel berikut merangkum kegunaan umum setiap tipe fundamental pada topik ini.
| Tipe | Isi yang disarankan | Contoh nilai |
|---|---|---|
int | Bilangan bulat umum | 12, -3, 1000 |
long long | Bilangan bulat dengan rentang besar | 9000000000 |
unsigned int | Pola bit atau aritmetika modulo yang disengaja | 4294967295 |
double | Bilangan pecahan | 3500.5 |
char | Satu karakter | ‘A’ |
bool | Keadaan benar atau salah | true |
Sebagai aturan praktis, int menjadi pilihan awal untuk bilangan bulat dan double untuk bilangan pecahan. Tipe unsigned dipakai hanya ketika perilaku modulo memang diinginkan, karena rentang lebih besarnya tidak sebanding dengan risiko penggulungan yang mudah terlewat.
Kesalahan umum#
Tiga kekeliruan berikut sering muncul saat variabel bertipe fundamental mulai digunakan.
- Menyimpan karakter dengan tanda kutip ganda, misalnya
char inisial = "A";. Litera"A"bertipeconst char*, sehingga compiler melaporkanerror: invalid conversion from 'const char*' to 'char'. Perbaikannya menulis'A'dengan tanda kutip tunggal. - Membaca variabel yang belum diinisialisasi. Nilainya tak tentu dan dapat berubah antar peluncuran, sehingga hasil program tidak dapat diandalkan. Perbaikannya mengisi nilai awal pada deklarasi, misalnya
int hitungan = 0;atauint hitungan{};. - Mengira
boolmenyimpan angka. Penugasanbool b = 42;sah dan nilainya langsung diterjemahkan menjaditrue, sehingga angka sebenarnya hilang pada saat disimpan. Perbaikannya menyimpan angka pada tipe bilangan dan menyimpan keadaan padabool.
Ringkasan#
- Variabel dideklarasikan dengan pola
tipe nama = nilai awal, dan inisialisasi saat deklarasi mencegah pembacaan nilai tak tentu. - Tipe fundamental yang paling sering dipakai adalah
intuntuk bilangan bulat,doubleuntuk pecahan,charuntuk satu karakter, danbooluntuk nilai benar atau salah. - Operator
sizeofmelaporkan ukuran tipe dalam byte dan hasilnya diperoleh saat kompilasi. - Overflow pada tipe unsigned terdefinisi dan menggulung mengikuti aritmetika modulo, sedangkan overflow pada tipe signed merupakan undefined behavior yang hasilnya tidak dapat dijamin.
Referensi#
- Fundamental types — cppreference — Daftar tipe fundamental beserta ukuran dan rentang nilainya menurut standar.
- sizeof operator — cppreference — Aturan lengkap operator sizeof dan tipe hasilnya.
- Literals — cppreference — Daftar litera C++ beserta akhiran tipe yang menyertainya.
- List initialization — cppreference — Aturan inisialisasi kurawal, termasuk larangan konversi yang menyempitkan.
- Undefined behavior — cppreference — Klasifikasi perilaku yang tidak ditetapkan standar, termasuk signed overflow.
- Object sizes and the sizeof operator — LearnCpp — Penjelasan ukuran objek dan pemakaian sizeof secara bertahap.