Saat array diteruskan ke fungsi, C tidak menyalin seluruh elemen—sangat tidak efisien bila array berukuran besar. Sebagai gantinya, compiler menyederhanakan tipe parameter int arr[] menjadi int *arr, sebuah pointer ke elemen pertama. Proses ini disebut array-to-pointer decay. Konsekuensinya signifikan: di dalam fungsi, sizeof(arr) mengembalikan ukuran pointer (8 byte pada sistem 64-bit), bukan ukuran array asli. Pada chapter ini kita akan menelusuri mekanisme decay, implikasinya terhadap sizeof, dan pola aman untuk melewatkan array ke fungsi.
Mekanisme Array-to-Pointer Decay #
Ketika kita menulis parameter int arr[] pada deklarasi fungsi, compiler mengubahnya menjadi int *arr. Kedua bentuk ini ekuivalen—pilih salah satu sesuai selera. Akibatnya, fungsi tidak lagi memiliki informasi ukuran array; ia hanya menerima alamat elemen pertama. Inilah mengapa setiap fungsi yang menerima array juga biasanya menerima parameter tambahan int n untuk jumlah elemen.
Dampak praktis decay:
- Tidak ada informasi ukuran —
sizeof(arr)di dalam fungsi adalahsizeof(int *), biasanya 8 byte pada sistem 64-bit. - Modifikasi elemen mempengaruhi array asli — karena fungsi menerima alamat elemen asli (bukan salinan), perubahan pada
arr[i]di dalam fungsi langsung terlihat di pemanggil. - Pointer arithmetic berlaku —
arr + idi dalam fungsi menghitung alamat elemen ke-idengan benar.
Demonstrasi Decay dengan sizeof #
Contoh berikut secara eksplisit membandingkan sizeof array di main dan di dalam fungsi yang menerima array sebagai parameter.
#include <stdio.h>
/* Parameter arr[] meluruh (decay) menjadi int *arr */
void cetak_array(int arr[], int n) {
printf("Di dalam fungsi: sizeof(arr) = %zu\n", sizeof(arr));
for (int i = 0; i < n; i++) {
printf(" arr[%d] = %d\n", i, arr[i]);
}
}
/* Versi eksplisit sebagai pointer - perilaku identik */
void kali_dua(int *arr, int n) {
for (int i = 0; i < n; i++) {
arr[i] = arr[i] * 2;
}
}
int main(void) {
int data[5] = {10, 20, 30, 40, 50};
printf("Di dalam main : sizeof(data) = %zu\n", sizeof(data));
cetak_array(data, 5);
kali_dua(data, 5);
printf("Setelah kali_dua:\n");
for (int i = 0; i < 5; i++) {
printf(" data[%d] = %d\n", i, data[i]);
}
return 0;
}Output yang Dihasilkan:
Di dalam main : sizeof(data) = 20
Di dalam fungsi: sizeof(arr) = 8
arr[0] = 10
arr[1] = 20
arr[2] = 30
arr[3] = 40
arr[4] = 50
Setelah kali_dua:
data[0] = 20
data[1] = 40
data[2] = 60
data[3] = 80
data[4] = 100Mari kita telaah baris-baris kode di atas:
- Baris 4 (
void cetak_array(int arr[], int n)): parameterarr[]sebenarnya adalahint *arrsetelah decay. Parameternwajib karena fungsi tidak bisa menentukan panjang sendiri. - Baris 5 (
printf("Di dalam fungsi: sizeof(arr) = %zu\n", sizeof(arr));):sizeof(arr)di sini mengembalikan ukuran pointerint *(8 byte pada 64-bit), bukan ukuran array asli (20 byte). Compiler memberi peringatan-Wsizeof-array-argumentuntuk mengingatkan kita. - Baris 11 (
void kali_dua(int *arr, int n)): bentuk parameterint *arrsecara eksplisit. Perilakunya identik denganint arr[]—decay terjadi sama. - Baris 12-14 (
arr[i] = arr[i] * 2;): memodifikasi elemen melalui pointer. Karenaarrmenunjuk ke elemen asli di arraydatamilikmain, perubahan langsung terlihat di pemanggil. - Baris 18 (
int data[5] = {10, 20, 30, 40, 50};): arraydataberukuran 5 elemen × 4 byte = 20 byte. - Baris 20 (
printf("Di dalam main : sizeof(data) = %zu\n", sizeof(data));): dimain,datamasih berupa array (belum decay), sehinggasizeof(data)= 20 byte. Bandingkan dengansizeof(arr) = 8di dalam fungsi. - Baris 21 (
cetak_array(data, 5);): pemanggilan dengan arraydatadan ukuran5. Hanya alamat elemen pertama yang diteruskan; tidak ada salinan elemen. - Baris 23 (
kali_dua(data, 5);): setelah pemanggilan ini, setiap elemendatamenjadi dua kali lipat. Modifikasi langsung terlihat tanpa perlu return value—ini ciri khas pass-by-pointer.
Parameter Array 2D dan Pointer Eksplisit #
Array 2D yang diteruskan ke fungsi juga meluruh, tetapi hanya dimensi pertama yang menjadi pointer. Dimensi kedua harus ditentukan secara eksplisit. Alternatif modern (C99) memakai parameter VLA int m[][kolom] dengan kolom sebagai parameter pertama.
#include <stdio.h>
/* Mengubah parameter dari array menjadi pointer eksplisit */
void skala(int *arr, int n, int faktor) {
for (int i = 0; i < n; i++) {
arr[i] *= faktor;
}
}
/* Menerima array 2D: parameter kolom wajib ditentukan */
void cetak_2d(int baris, int kolom, int m[][kolom]) {
for (int i = 0; i < baris; i++) {
for (int j = 0; j < kolom; j++) {
printf("%d ", m[i][j]);
}
printf("\n");
}
}
int main(void) {
int data[4] = {1, 2, 3, 4};
skala(data, 4, 10);
for (int i = 0; i < 4; i++) {
printf("%d ", data[i]);
}
printf("\n");
int grid[2][3] = {{1, 2, 3}, {4, 5, 6}};
cetak_2d(2, 3, grid);
return 0;
}Output yang Dihasilkan:
10 20 30 40
1 2 3
4 5 6Pada contoh di atas, fungsi skala menerima pointer eksplisit int *arr. Pola arr[i] *= faktor identik dengan arr[i] = arr[i] * faktor. Untuk array 2D, parameter int m[][kolom] menggunakan variable-length array parameter (C99). Compiler perlu tahu jumlah kolom untuk menghitung offset baris—tanpa informasi kolom, ekspresi m[i][j] tidak bisa dihitung alamatnya.