Pointer dan Memori Dinamis

Pertemuan 11

Dasar-Dasar Pemrograman

Outline Materi

  1. Konsep Pointer dan Alamat Memori
  2. Operator Pointer (& dan *)
  3. Pointer dan Array
  4. Memori Dinamis (new dan delete)
  5. Memory Management
  6. Best Practices

Apa itu Pointer?

Pointer adalah variabel yang menyimpan alamat memori dari variabel lain


int x = 42;           // Variabel biasa
int* ptr = &x;        // Pointer ke x
                    

Pointer "menunjuk" ke lokasi dimana data disimpan di memori

Mengapa Pointer Penting?

  • Manipulasi data efisien tanpa copy
  • Alokasi memori dinamis saat runtime
  • Fondasi untuk struktur data lanjutan
  • Membuat fungsi yang dapat memodifikasi variabel luar

Memori Komputer

Memori dapat dibayangkan sebagai deretan kotak dengan alamat unik


int x = 10;
// x disimpan di alamat: 0x7ffd5ba4e8bc
// Kita bisa akses alamat ini dengan pointer
                        

Visualisasi Pointer

Pointer Visualization

Operator Pointer

Address-of (&)

Mendapatkan alamat variabel


int x = 10;
int* ptr = &x;
                            

Dereference (*)

Akses nilai yang ditunjuk


cout << *ptr;  // 10
*ptr = 20;     // ubah x
                            

Contoh Lengkap


int nilai = 100;
int* ptr = &nilai;

cout << nilai;    // 100 (nilai variabel)
cout << &nilai;   // 0x... (alamat variabel)

cout << ptr;      // 0x... (alamat yang disimpan)
cout << *ptr;     // 100 (nilai yang ditunjuk)
                    

Modifikasi Melalui Pointer


int x = 10;
int* ptr = &x;

cout << x;      // 10
*ptr = 20;      // Ubah x melalui pointer

cout << x;      // 20
cout << *ptr;   // 20
                    

Pointer dan variabel asli berbagi lokasi memori yang sama!

Hubungan Pointer dan Array

Nama array adalah pointer konstan ke elemen pertama


int arr[5] = {10, 20, 30, 40, 50};
int* ptr = arr;  // arr = &arr[0]

cout << arr;     // Alamat arr[0]
cout << ptr;     // Sama dengan arr
                    

Akses Array dengan Pointer


int arr[5] = {10, 20, 30, 40, 50};
int* ptr = arr;

// Dua cara yang ekuivalen:
cout << arr[0];   // 10
cout << *ptr;     // 10

cout << arr[2];   // 30
cout << *(ptr+2); // 30
                    

Pointer Arithmetic

Operasi aritmatika pada pointer


int arr[5] = {10, 20, 30, 40, 50};

int* ptr = arr;      // Menunjuk ke arr[0]
ptr++;               // Menunjuk ke arr[1]

cout << *ptr;        // 20
cout << *(ptr + 2);  // 40 (arr[3])
                    

Traversal Array dengan Pointer


int arr[5] = {10, 20, 30, 40, 50};

// Menggunakan pointer
int* ptr = arr;
for (int i = 0; i < 5; i++) {
    cout << *ptr << " ";
    ptr++;
}
// Output: 10 20 30 40 50
                    

Array sebagai Parameter


void tampilkanArray(int* arr, int ukuran) {
    for (int i = 0; i < ukuran; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
}

int data[5] = {1, 2, 3, 4, 5};
tampilkanArray(data, 5);
                    

Stack vs Heap

Memory Layout

Alokasi Memori Statik vs Dinamis

Statik (Stack)


int x = 10;
int arr[100];
                            

✓ Otomatis dibersihkan
✓ Cepat
✗ Ukuran tetap

Dinamis (Heap)


int* x = new int(10);
int* arr = new int[n];
                            

✓ Ukuran fleksibel
✗ Manual management
✗ Lebih lambat

Operator new

Mengalokasikan memori di heap


// Single variable
int* ptr = new int(42);

// Dengan inisialisasi
double* pi = new double(3.14159);

// Array dinamis
int ukuran = 100;
int* arr = new int[ukuran];
arr[0] = 10;  // Gunakan seperti array biasa
                    

Operator delete

Setiap new harus memiliki delete!


// Untuk single variable
int* ptr = new int(42);
// ... gunakan ptr ...
delete ptr;
ptr = nullptr;  // Good practice

// Untuk array
int* arr = new int[100];
// ... gunakan arr ...
delete[] arr;   // Perhatikan []
arr = nullptr;
                    

Contoh Lengkap


int ukuran;
cin >> ukuran;

// Alokasi
int* arr = new int[ukuran];

// Isi array
for (int i = 0; i < ukuran; i++) {
    arr[i] = i * 10;
}

// Gunakan array
for (int i = 0; i < ukuran; i++) {
    cout << arr[i] << " ";
}

// PENTING: Dealokasi
delete[] arr;
arr = nullptr;
                    

Memory Leak

Bahaya: Memori tidak pernah di-dealokasi!


void fungsi() {
    int* ptr = new int[1000];
    // Gunakan ptr...
    // LUPA delete[] ptr; ← MEMORY LEAK!
}
                    

Seiring waktu, program kehabisan memori!

Cara Benar


void fungsi() {
    int* ptr = new int[1000];
    // Gunakan ptr...
    delete[] ptr;     // PENTING!
    ptr = nullptr;    // Good practice
}
                    

Selalu pasangkan new dengan delete!

Dangling Pointer

Pointer yang menunjuk ke memori yang sudah dihapus


int* ptr = new int(100);
cout << *ptr;  // OK: 100

delete ptr;    // Memory freed
// ptr sekarang DANGLING!
cout << *ptr;  // DANGER: Undefined behavior!
                    

Solusi: nullptr


int* ptr = new int(100);
// ... gunakan ptr ...

delete ptr;
ptr = nullptr;  // Set ke null!

// Sekarang aman untuk cek
if (ptr != nullptr) {
    cout << *ptr;
} else {
    cout << "Pointer null" << endl;
}
                    

Common Errors


// ERROR 1: Uninitialized pointer
int* ptr;
cout << *ptr;  // DANGER!

// ERROR 2: Double delete
int* p = new int(42);
delete p;
delete p;  // CRASH!

// ERROR 3: delete vs delete[]
int* arr = new int[10];
delete arr;     // WRONG!
delete[] arr;   // CORRECT!

// ERROR 4: Memory leak
for (int i = 0; i < 1000; i++) {
    int* leak = new int[100];
    // No delete[]!
}
                        

Pointer ke Pointer

Pointer yang menyimpan alamat pointer lain


int nilai = 42;
int* ptr1 = &nilai;     // Pointer
int** ptr2 = &ptr1;     // Pointer ke pointer

cout << nilai;   // 42
cout << *ptr1;   // 42
cout << **ptr2;  // 42
                    

Matrix Dinamis


int baris = 3;
int kolom = 4;

// Alokasi matrix
int** matrix = new int*[baris];
for (int i = 0; i < baris; i++) {
    matrix[i] = new int[kolom];
}

// Isi matrix
for (int i = 0; i < baris; i++) {
    for (int j = 0; j < kolom; j++) {
        matrix[i][j] = i * kolom + j;
    }
}

// Dealokasi (PENTING: urutan!)
for (int i = 0; i < baris; i++) {
    delete[] matrix[i];
}
delete[] matrix;
                    

Const Pointer

Pointer to Const


const int* ptr;

// Bisa ubah pointer
ptr = &x;
ptr = &y;

// Tidak bisa ubah nilai
*ptr = 10;  // ERROR
                            

Const Pointer


int* const ptr = &x;

// Bisa ubah nilai
*ptr = 10;  // OK

// Tidak bisa ubah pointer
ptr = &y;  // ERROR
                            

Pointer sebagai Parameter


void swap(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main() {
    int x = 5, y = 10;
    swap(&x, &y);  // Pass alamat
    cout << x << " " << y;  // 10 5
}
                    

Best Practices

  1. Selalu inisialisasi pointer
  2. Cek nullptr sebelum dereference
  3. Set nullptr setelah delete
  4. Pasangkan new dengan delete
  5. Gunakan delete[] untuk array

Template Best Practice


int* ptr = nullptr;  // 1. Inisialisasi

if (kondisi) {
    ptr = new int[100];  // 2. Alokasi
    
    // 3. Gunakan ptr
    ptr[0] = 10;
    
    delete[] ptr;  // 4. Dealokasi
    ptr = nullptr; // 5. Reset
}

// 6. Cek sebelum gunakan
if (ptr != nullptr) {
    cout << *ptr;
}
                    

Debugging Tools

  • Valgrind (Linux): Memory leak detector
  • AddressSanitizer: Compiler flag -fsanitize=address
  • Visual Studio: Built-in memory leak detection
  • GDB: Print pointer values

Aplikasi Praktis

  • Dynamic arrays dengan resize
  • Linked lists
  • Trees dan graphs
  • String manipulation
  • Matrix operations

Dynamic Array Resizing


int* resizeArray(int* old, int oldSize, int newSize) {
    int* newArr = new int[newSize];
    
    // Copy data lama
    int copySize = min(oldSize, newSize);
    for (int i = 0; i < copySize; i++) {
        newArr[i] = old[i];
    }
    
    // Inisialisasi elemen baru
    for (int i = oldSize; i < newSize; i++) {
        newArr[i] = 0;
    }
    
    delete[] old;  // Hapus array lama
    return newArr;
}
                    

Linked List Node


struct Node {
    int data;
    Node* next;  // Pointer ke node berikutnya
};

void insertAwal(Node** head, int nilai) {
    Node* newNode = new Node();
    newNode->data = nilai;
    newNode->next = *head;
    *head = newNode;
}
                    

Key Takeaways

  • Pointer menyimpan alamat memori
  • Operator & = address-of, * = dereference
  • Array dan pointer sangat erat terkait
  • new/delete untuk memori dinamis
  • Hindari memory leak dan dangling pointer
  • Selalu inisialisasi dan cleanup dengan benar

Operator Cheat Sheet


int x = 10;
int* ptr = &x;

x           // Nilai x (10)
&x          // Alamat x
ptr         // Alamat yang disimpan di ptr (sama dengan &x)
*ptr        // Nilai di alamat yang ditunjuk ptr (10)
&ptr        // Alamat pointer ptr sendiri

// Modifikasi
*ptr = 20;  // Ubah x menjadi 20
ptr++;      // Pindah ke memori berikutnya
                        

Memory Management Rules

Alokasi Dealokasi
new Type delete ptr
new Type[size] delete[] ptr
malloc(size) free(ptr)

Jangan mix-and-match! (new dengan free, malloc dengan delete)

Latihan

  1. Buat fungsi swap menggunakan pointer
  2. Implementasi dynamic array dengan resize
  3. Buat linked list sederhana
  4. Alokasi matrix 2D dinamis
  5. Fix memory leaks dalam kode yang diberikan

Terima Kasih!

Pertanyaan?


Sub-CPMK 3.3: Mahasiswa mampu menerapkan konsep pointer dan penggunaannya dalam program C++