Salah satu kegunaan utama pointer adalah memungkinkan fungsi mengubah variabel milik pemanggil. Secara default, parameter fungsi di C dilewatkan by value—fungsi menerima salinan nilai, bukan variabel aslinya. Setiap modifikasi parameter di dalam fungsi tidak terlihat oleh pemanggil. Untuk mengatasi keterbatasan ini, kita melewatkan alamat variabel (lewat operator &) ke fungsi yang menerima pointer; fungsi kemudian bisa memodifikasi variabel asli melalui dereference pointer tersebut. Pada chapter ini kita akan membandingkan pass-by-value dan pass-by-pointer secara langsung, lalu menerapkannya pada dua kasus klasik: fungsi tukar dan idiom out-parameter.
Pass-by-Value vs Pass-by-Pointer #
C murni menerapkan pass-by-value untuk semua parameter: argumen yang dilewatkan ke fungsi disalin ke parameter lokal fungsi tersebut. Modifikasi parameter tidak memengaruhi variabel pemanggil. Untuk membuat fungsi bisa mengubah variabel pemanggil, kita melewatkan alamat variabel tersebut (menggunakan &) sebagai argumen, dan mendeklarasikan parameter sebagai pointer. Fungsi kemudian bisa menulis ke variabel asli melalui *param = ....
Mekanisme kerjanya: saat pemanggil menulis fungsi(&a), nilai &a (alamat a) disalin ke parameter fungsi int *x. Di dalam fungsi, ekspresi *x adalah lvalue yang menunjuk ke region memori a di stack frame pemanggil. Penugasan *x = 999 menulis ke region tersebut—perubahan langsung terlihat di variabel a di main. Tanpa pointer, perubahan hanya terjadi pada salinan lokal parameter dan hilang saat fungsi kembali.
#include <stdio.h>
/* Pass by value: fungsi menerima SALINAN nilai */
void ubah_nilai_value(int x) {
x = 999;
printf(" di dalam ubah_nilai_value: x = %d\n", x);
}
/* Pass by pointer: fungsi menerima ALAMAT variabel */
void ubah_nilai_ptr(int *x) {
*x = 999;
printf(" di dalam ubah_nilai_ptr : *x = %d\n", *x);
}
int main(void) {
int a = 10;
printf("Sebelum ubah_nilai_value: a = %d\n", a);
ubah_nilai_value(a);
printf("Setelah ubah_nilai_value: a = %d (tidak berubah)\n\n", a);
printf("Sebelum ubah_nilai_ptr : a = %d\n", a);
ubah_nilai_ptr(&a);
printf("Setelah ubah_nilai_ptr : a = %d (berubah!)\n", a);
return 0;
}Output yang Dihasilkan:
Sebelum ubah_nilai_value: a = 10
di dalam ubah_nilai_value: x = 999
Setelah ubah_nilai_value: a = 10 (tidak berubah)
Sebelum ubah_nilai_ptr : a = 10
di dalam ubah_nilai_ptr : *x = 999
Setelah ubah_nilai_ptr : a = 999 (berubah!)Pada pemanggilan ubah_nilai_value(a), nilai a (yaitu 10) disalin ke parameter lokal x. Penugasan x = 999 di dalam fungsi hanya mengubah salinan lokal; setelah fungsi kembali, variabel a di main tetap bernilai 10. Sebaliknya, pada ubah_nilai_ptr(&a), alamat a disalin ke parameter x bertipe int *. Penugasan *x = 999 menulis melalui pointer ke region memori a, sehingga a di main berubah menjadi 999.
Mari kita telaah baris-baris kode di atas:
- Baris 4-7 (
void ubah_nilai_value(int x)): parameterxadalah variabel lokal baru yang diinisialisasi dengan salinan nilai argumen. Modifikasix = 999tidak terlihat di luar fungsi. - Baris 10-13 (
void ubah_nilai_ptr(int *x)): parameterxbertipeint *. Penugasan*x = 999menulis melalui pointer ke region memori pemanggil. - Baris 18 (
ubah_nilai_value(a);): lewatkan nilaia(10). Tidak ada&karena kita ingin pass-by-value. - Baris 22 (
ubah_nilai_ptr(&a);): lewatkan alamatadengan&. Inilah yang membuat fungsi bisa mengubah variabel pemanggil. - Baris 23 (
printf("Setelah ... a = %d (berubah!)", a);): konfirmasi bahwaakini bernilai999—bukti modifikasi dari dalam fungsi.
Kasus Klasik: tukar dan Out-Parameter #
Dua idioms yang paling sering muncul: fungsi tukar yang menukar dua nilai, dan out-parameter untuk mengembalikan beberapa nilai sekaligus dari satu fungsi (C hanya mengizinkan satu return value per fungsi). Kedua idiom ini membutuhkan pointer karena fungsi harus memodifikasi lebih dari satu variabel pemanggil.
#include <stdio.h>
/* Tukar dua nilai - butuh pointer karena harus ubah keduanya */
void tukar(int *a, int *b) {
int tmp = *a;
*a = *b;
*b = tmp;
}
/* Idiom "out parameter" - kembalikan dua nilai sekaligus */
void min_max(const int *arr, int n, int *out_min, int *out_max) {
*out_min = arr[0];
*out_max = arr[0];
for (int i = 1; i < n; i++) {
if (arr[i] < *out_min) *out_min = arr[i];
if (arr[i] > *out_max) *out_max = arr[i];
}
}
int main(void) {
int x = 5, y = 9;
printf("Sebelum tukar: x=%d, y=%d\n", x, y);
tukar(&x, &y);
printf("Setelah tukar : x=%d, y=%d\n", x, y);
int data[6] = {3, 8, 1, 9, 4, 6};
int lo, hi;
min_max(data, 6, &lo, &hi);
printf("\nData: ");
for (int i = 0; i < 6; i++) printf("%d ", data[i]);
printf("\nMin: %d, Max: %d\n", lo, hi);
return 0;
}Output yang Dihasilkan:
Sebelum tukar: x=5, y=9
Setelah tukar : x=9, y=5
Data: 3 8 1 9 4 6
Min: 1, Max: 9Fungsi tukar membutuhkan pointer karena harus memodifikasi dua variabel sekaligus (x dan y di pemanggil). Tanpa pointer, modifikasi hanya terjadi pada salinan lokal. Fungsi min_max mengembalikan dua nilai—minimum dan maksimum array—melalui out-parameter out_min dan out_max. Pola ini begitu umum di C karena C hanya mengizinkan satu return value per fungsi; untuk “mengembalikan” beberapa nilai, kita gunakan out-parameter yang diisi melalui dereference pointer.