Setiap algoritma yang kita tulis pada akhirnya beroperasi pada data: angka, karakter, kebenaran suatu kondisi. C++ adalah bahasa yang statically typed, artinya compiler harus mengetahui tipe setiap variabel pada saat kompilasi. Pemilihan tipe yang tepat menentukan seberapa cepat dan seberapa aman kode kita berjalan. Pada Chapter ini kita akan mempelajari tipe fundamental C++ yang paling sering dipakai di DSA, cara menginisialisasi variabel, serta cara compiler melakukan konversi antar tipe.

Tipe Fundamental untuk DSA #

C++ menyediakan banyak tipe bawaan, namun hanya sebagian kecil yang sering kita pakai saat menulis struktur data dan algoritma. Pemilihan tipe menentukan rentang nilai yang bisa ditampung sekaligus konsumsi memori. Tabel berikut merangkum tipe yang paling relevan:

TipeUkuran tipikalRentangPenggunaan umum di DSA
int4 byte±2,1 miliarCounter, indeks kecil
long long8 byte±9,2 × 10¹⁸Hasil perhitungan besar, kombinasi
std::size_t8 byte0 .. 1,8 × 10¹⁹Ukuran container, indeks array
char1 bytekarakter ASCIIPemrosesan string
bool1 bytetrue / falseFlag, kondisi
double8 byte~15 digit desimalGeometri, statistik

Mekanisme: Variabel adalah Slot Memori #

Ketika kita mendeklarasikan int jumlah_node = 1'000;, compiler memesan slot memori sebesar sizeof(int) byte, menempelkan nama jumlah_node ke alamat slot tersebut, dan menulis nilai 1000 ke dalamnya. Nama variabel hanyalah label yang dipakai compiler untuk menerjemahkan kode menjadi akses memori.

Kode: Deklarasi dan Inisialisasi #

cpp
#include <iostream>
#include <limits>

int main() {
    int jumlah_node = 1'000;
    long long kombinasi = 9'000'000'000'000LL;
    std::size_t n = 42;
    char inisial = 'G';
    bool selesai = false;

    std::cout << "jumlah_node = " << jumlah_node << '\n';
    std::cout << "kombinasi   = " << kombinasi << '\n';
    std::cout << "n           = " << n << '\n';
    std::cout << "inisial     = " << inisial << '\n';
    std::cout << "selesai     = " << std::boolalpha << selesai << '\n';

    std::cout << "int max     = " << std::numeric_limits<int>::max() << '\n';
    std::cout << "long long max= " << std::numeric_limits<long long>::max() << '\n';
    return 0;
}

Output yang Dihasilkan:

text
jumlah_node = 1000
kombinasi   = 9000000000000
n           = 42
inisial     = G
selesai     = false
int max     = 2147483647
long long max= 9223372036854775807

Mari kita telaah baris-baris kode di atas:

  1. Baris int jumlah_node = 1'000;: compiler memesan 4 byte, menulis 1000. Tanda ' adalah digit separator yang hanya memperjelas angka besar; nilainya tetap 1000.
  2. Baris long long kombinasi = 9'000'000'000'000LL;: sufiks LL memaksa literal menjadi long long karena angka sebesar itu melebihi int max.
  3. Baris std::size_t n = 42;: size_t adalah alias unsigned 64-bit. Kita pakai untuk panjang container dan indeks array.
  4. Baris std::boolalpha: manipulator yang membuat bool dicetak sebagai true/false alih-alih 1/0.
  5. Baris std::numeric_limits<int>::max(): cara portabel memperoleh batas atas tipe.

Inisialisasi: Brace vs Equals #

C++ modern menyediakan tiga gaya inisialisasi yang sering membingungkan pemula. Pilihannya bukan sekadar selera gaya — masing-masing memiliki makna teknis berbeda.

GayaSintaksKarakteristik
Copy initializationint a = 10;Warisan dari C; mengizinkan narrowing implisit
Direct list initint b{20};Modern; menolak narrowing
Value initializationint c{};Mengisi nol untuk tipe fundamental

Kode: Tiga Gaya Inisialisasi #

cpp
#include <iostream>

int main() {
    int a = 10;      // copy initialization
    int b{20};       // direct list initialization
    int c{};         // value initialization -> 0
    std::cout << "a,b,c = " << a << ',' << b << ',' << c << '\n';

    // int bad{3.14}; // ERROR: narrowing conversion dari double ke int

    double pi = 3.14;
    int bulat = pi;            // OK secara sintaks, tapi memotong 0.14
    std::cout << "bulat  = " << bulat << '\n';
    return 0;
}

Output yang Dihasilkan:

text
a,b,c = 10,20,0
bulat  = 3
  1. Baris int b{20};: brace {} memaksa pemeriksaan narrowing. Compiler akan menolak int bad{3.14} karena 3.14 tidak muat sebagai int tanpa kehilangan presisi.
  2. Baris int c{};: tanda kurung kurawal kosong melakukan value initialization. Untuk tipe fundamental, hasilnya 0. Untuk std::string, hasilnya string kosong.
  3. Baris int bulat = pi;: copy initialization mengizinkan konversi implisit double → int yang memotong bagian pecahan. Operasi ini sah, tetapi kehilangan presisi sering menjadi sumber bug DSA yang sulit dilacak.

auto dan decltype #

Menulis tipe yang panjang seperti std::vector<int>::iterator berulang-ulang melelahkan. C++ menyediakan auto agar compiler menyimpulkan tipe dari ekspresi inisialisasi, dan decltype untuk memperoleh tipe dari ekspresi yang sudah ada.

Kode: Penyimpulan Tipe Otomatis #

cpp
#include <iostream>
#include <vector>

int main() {
    std::vector<int> data{10, 20, 30};

    auto ukuran = data.size();        // std::size_t
    auto total = data[0] + data[1];   // int
    decltype(total) cadangan = 0;     // int (mengikuti tipe total)

    std::cout << "ukuran    = " << ukuran << '\n';
    std::cout << "total     = " << total << '\n';
    std::cout << "cadangan  = " << cadangan << '\n';
    return 0;
}

Output yang Dihasilkan:

text
ukuran    = 3
total     = 30
cadangan  = 0
  1. Baris auto ukuran = data.size();: size() mengembalikan std::size_t, jadi ukuran juga bertipe std::size_t. Menggunakan auto menghindari kesalahan tipe yang sering muncul bila kita menulis int.
  2. Baris auto total = data[0] + data[1];: penjumlahan dua int menghasilkan int, sehingga total bertipe int.
  3. Baris decltype(total) cadangan = 0;: decltype mengambil tipe variabel total (yaitu int) tanpa mengevaluasi nilainya, lalu mendeklarasikan cadangan dengan tipe yang sama.

Konversi Tipe: Eksplisit vs Implisit #

Konversi tipe terjadi ketika dua tipe berbeda berinteraksi dalam satu ekspresi. C++ melakukan implicit conversion untuk tipe yang kompatibel, namun untuk konversi yang berisiko kita perlu menyatakannya secara eksplisit.

Mekanisme: Promotion dan Narrowing #

Saat kita menulis int + double, compiler mempromosikan int menjadi double agar operasi berlangsung tanpa kehilangan presisi. Sebaliknya, double → int adalah narrowing yang membuang bagian pecahan. Untuk konversi yang sengaja dilakukan, gunakan static_cast<Tipe>(nilai).

Kode: Rasio dan Overflow #

cpp
#include <iostream>
#include <limits>

int main() {
    int jumlah_node = 1000;
    double rasio = static_cast<double>(jumlah_node) / 7;
    std::cout << "rasio = " << rasio << '\n';

    int big = std::numeric_limits<int>::max();
    std::cout << "big      = " << big << '\n';
    std::cout << "big + 1  = " << big + 1 << '\n';            // overflow
    long long safe = static_cast<long long>(big) + 1;
    std::cout << "safe     = " << safe << '\n';
    return 0;
}

Output yang Dihasilkan:

text
rasio = 142.857
big      = 2147483647
big + 1  = -2147483648
safe     = 2147483648
  1. Baris static_cast<double>(jumlah_node) / 7: kita memaksa jumlah_node menjadi double lebih dulu, sehingga pembagian dilakukan dalam presisi pecahan. Tanpa cast, 1000 / 7 menghasilkan 142 (integer division).
  2. Baris big + 1: big berisi INT_MAX. Menambahkan 1 menyebabkan signed integer overflow, yang dalam C++ adalah undefined behavior. Pada platform umum hasilnya membungkus ke nilai negatif terbesar.
  3. Baris static_cast<long long>(big) + 1: cast ke long long lebih dulu memperluas rentang, sehingga penambahan menjadi aman.

Ringkasan #

  • Pilih tipe sesuai kebutuhan: int untuk counter kecil, long long untuk hasil besar, std::size_t untuk ukuran/panjang.
  • Gunakan brace initialization int x{value}; agar compiler menjaga dari narrowing.
  • Manfaatkan auto dan decltype untuk kode yang ringkas tanpa kehilangan ketepatan tipe.
  • Gunakan static_cast<Tipe>(nilai) untuk konversi yang sengaja dilakukan, dan jangan abaikan kemungkinan overflow.