kuliah#pemrograman#bahasa c#array#pointer

Array dan Pointer di C: Bedanya, Hubungannya, Contoh Kode

Nama array meluruh jadi pointer begitu dikirim ke fungsi, jadi sizeof di dalam fungsi memberi 8 bukan ukuran arraynya. Lengkap dengan kode yang jalan.

Oleh Oscar Ryanda Putra· Diterbitkan 8 Okt 2026· 7 menit baca
Di halaman ini

Array dan pointer adalah dua hal berbeda, tapi nama array meluruh menjadi pointer ke elemen pertamanya begitu dikirim sebagai argumen fungsi. Itu sebabnya sizeof di dalam fungsi memberi 8, bukan ukuran array yang kamu kirim. Satu perilaku itu menjelaskan hampir semua kebingungan pemula tentang array dan pointer, dan jarang dijelaskan sampai akarnya di kelas Dasar Pemrograman.

Kalau arr[i] sama dengan *(arr+i), apa bedanya array dan pointer?

Bedanya di apa yang mereka pegang.

Array adalah sebuah blok memori. Deklarasi int data[5] langsung menyediakan ruang untuk lima bilangan, dan nama data terikat permanen ke blok itu.

Pointer adalah sebuah variabel yang isinya alamat. Deklarasi int *p menyediakan ruang untuk menyimpan satu alamat, dan isinya bisa diubah kapan saja.

Yang membuat keduanya terasa sama adalah aturan ini: di hampir semua ekspresi, nama array otomatis berubah menjadi alamat elemen pertamanya. Jadi data[2] dan *(data + 2) benar benar kompilasi menjadi hal yang sama, dan p[2] juga sah untuk pointer.

SifatArray int data[5]Pointer int *p
sizeof20, total byte seluruh elemen8, ukuran satu alamat di sistem 64 bit
Bisa di-assign ulangTidakYa
Memori elemenSudah dialokasikanBelum, hanya menyimpan alamat
Tipe dari &namaint (*)[5], pointer ke arrayint **, pointer ke pointer
Boleh nama++TidakYa

Peluruhan nama array ke pointer berlaku di hampir semua tempat, kecuali tiga pengecualian: ketika jadi operan sizeof, ketika jadi operan &, dan ketika string literal dipakai untuk menginisialisasi array karakter. Tiga pengecualian itu yang menyimpan seluruh cerita di bawah.

Kenapa sizeof di dalam fungsi memberi 8?

Jalankan kode ini apa adanya.

#include <stdio.h>

void cetak(int a[], int n) {
    printf("di fungsi : sizeof(a) = %zu\n", sizeof(a));
    for (int i = 0; i < n; i++) printf("%d ", a[i]);
    printf("\n");
}

int main(void) {
    int data[5] = {3, 1, 4, 1, 5};
    printf("di main   : sizeof(data) = %zu\n", sizeof(data));
    printf("jumlah elemen = %zu\n", sizeof(data) / sizeof(data[0]));
    cetak(data, 5);
    return 0;
}

Keluarannya di Linux 64 bit:

di main   : sizeof(data) = 20
jumlah elemen = 5
di fungsi : sizeof(a) = 8
3 1 4 1 5

Di main, data masih array, jadi sizeof memberi 5 dikali 4 byte sama dengan 20. Begitu dikirim ke cetak, yang benar benar berpindah hanyalah alamat elemen pertamanya. Parameter yang kamu tulis sebagai int a[] diterjemahkan oleh kompiler menjadi int *a, meskipun sintaksnya terlihat seperti array. Jadi sizeof(a) di dalam fungsi mengukur sebuah pointer, dan di sistem 64 bit sebuah pointer besarnya 8 byte.

Angka 8 itu bukan angka ajaib. Di sistem 32 bit nilainya 4, dan di mikrokontroler AVR seperti Arduino Uno nilainya 2. Yang konstan adalah faktanya: nilai itu mengukur pointer, bukan arraymu.

Kompiler sebenarnya sudah memperingatkan. Kompilasi dengan gcc -Wall dan kamu akan dapat pesan seperti warning: 'sizeof' on array function parameter 'a' will return size of 'int *'. Peringatan ini hampir selalu terlewat karena programnya tetap jalan dan tetap mencetak sesuatu.

Kenapa panjang array harus dikirim sebagai parameter terpisah?

Karena setelah peluruhan, tidak ada satu pun informasi panjang yang tersisa. Fungsi hanya menerima alamat awal, dan di C tidak ada metadata yang menyimpan berapa elemen yang ada di belakang alamat itu.

Lihat apa yang terjadi kalau trik sizeof yang benar di main dipakai di dalam fungsi.

void proses(int a[]) {
    size_t n = sizeof(a) / sizeof(a[0]);   /* 8 / 4 = 2 */
    for (size_t i = 0; i < n; i++) a[i] *= 2;
}

Loop itu memproses dua elemen, bukan lima. Programnya tidak crash, tidak ada pesan error saat dijalankan, dan tiga elemen terakhir diam diam tidak tersentuh. Bug seperti ini yang paling mahal, karena gejalanya cuma hasil yang salah sebagian, dan kamu akan mencari kesalahannya di logika, bukan di baris sizeof.

Jadi tanda tangan fungsi yang benar untuk array selalu membawa panjangnya.

void proses(int a[], size_t n);
void proses(int *a, size_t n);   /* identik dengan di atas */

Dua baris itu sama persis bagi kompiler. Menulis int a[5] pun tidak mengubah apa apa, karena angka 5 di parameter fungsi diabaikan dan tidak diperiksa. Kamu tetap bisa mengirim array berisi tiga elemen ke fungsi yang parameternya int a[5], dan tidak ada yang mengeluh.

Apa bedanya data, &data[0], dan &data?

Tiga ekspresi ini menunjuk ke alamat yang sama tapi tipenya berbeda, dan perbedaan tipe itu terlihat begitu kamu melakukan aritmatika.

int data[5] = {3, 1, 4, 1, 5};
printf("data      = %p\n", (void *)data);
printf("&data[0]  = %p\n", (void *)&data[0]);
printf("&data     = %p\n", (void *)&data);
printf("data + 1  = %p\n", (void *)(data + 1));
printf("&data + 1 = %p\n", (void *)(&data + 1));

Contoh keluarannya:

data      = 0x7ffd3c2a4a50
&data[0]  = 0x7ffd3c2a4a50
&data     = 0x7ffd3c2a4a50
data + 1  = 0x7ffd3c2a4a54
&data + 1 = 0x7ffd3c2a4a64

Tiga baris pertama identik. Baris keempat maju 4 byte, karena data bertipe int * dan satu langkah berarti satu int. Baris kelima maju 20 byte, karena &data bertipe int (*)[5] dan satu langkah berarti satu array utuh berisi lima int.

Di sinilah aritmatika pointer jadi jelas: penambahan satu selalu berarti satu elemen bertipe itu, bukan satu byte. Kalau kamu mengubah int menjadi double, lompatannya jadi 8 byte tanpa kamu mengubah apa pun di kodenya.

Apa saja yang tidak boleh dilakukan ke nama array?

Array punya batasan yang tidak dimiliki pointer, dan pesan errornya cukup khas untuk dihafal.

int a[5] = {1, 2, 3, 4, 5};
int b[5];

b = a;      /* error: assignment to expression with array type */
a++;        /* error: lvalue required as increment operand */

int *p = a; /* boleh, p menyimpan alamat a[0] */
p[2] = 99;  /* mengubah a[2] menjadi 99 */
p++;        /* boleh, p sekarang menunjuk a[1] */

Nama array bukan variabel yang bisa ditimpa, jadi menyalin array harus elemen per elemen atau dengan memcpy. Tapi lewat pointer, isi arraynya bisa diubah dengan bebas, dan itu alasan kenapa fungsi di C bisa mengubah array pemanggilnya meskipun C selalu mengirim argumen secara salinan nilai. Yang disalin adalah alamatnya, bukan isinya.

Bagaimana kalau arraynya dua dimensi?

Peluruhan hanya terjadi pada dimensi pertama. int m[3][4] meluruh menjadi int (*)[4], yaitu pointer ke array berisi 4 int, bukan int **.

void cetak_matriks(int m[][4], int baris) {
    for (int i = 0; i < baris; i++) {
        for (int j = 0; j < 4; j++) printf("%d ", m[i][j]);
        printf("\n");
    }
}

Angka 4 di parameter wajib ditulis, karena kompiler butuh tahu panjang satu baris untuk menghitung m[i][j]. Mengganti parameter itu menjadi int **m akan dikompilasi dengan peringatan tipe lalu crash saat dijalankan, karena int ** berarti pointer ke pointer, dan array dua dimensi sebenarnya satu blok memori berurutan, bukan kumpulan pointer.

Kesalahan yang sering terjadi

  1. Memakai sizeof(a) / sizeof(a[0]) di dalam fungsi. Hasilnya 2 untuk array int, dan loopmu memproses dua elemen saja tanpa error.
  2. Mengirim array dua dimensi sebagai int **.
  3. Mengembalikan pointer ke array lokal dari dalam fungsi. Array lokal hilang begitu fungsi selesai, dan pointer yang kamu kembalikan menunjuk ke memori yang sudah tidak berlaku.
  4. Memakai malloc tanpa menyimpan panjangnya di variabel terpisah. Memori hasil malloc juga tidak membawa informasi ukuran.
  5. Menyamakan sizeof dan strlen untuk string. Pada char s[] = "halo", sizeof(s) memberi 5 karena menghitung karakter penutup nol, sedangkan strlen(s) memberi 4.
  6. Mengubah isi string literal. char *s = "halo"; lalu s[0] = 'H'; biasanya crash, karena string literal disimpan di memori yang tidak boleh ditulis. Pakai char s[] = "halo"; kalau mau diubah.
  7. Mengakses a[5] pada array berukuran 5. C tidak memeriksa batas array sama sekali, jadi programnya mungkin tetap jalan dan mungkin merusak variabel lain. Yang tidak crash hari ini bisa crash setelah kodenya diubah sedikit.

Nomor satu dan nomor tujuh punya pola yang sama dan itu pola yang perlu kamu bawa keluar dari artikel ini: di C, kesalahan tentang panjang array tidak berbunyi. Kompilasinya hijau, programnya jalan, keluarannya salah sebagian. Kebiasaan yang menyelamatkan adalah selalu mengompilasi dengan gcc -Wall -Wextra dan membaca peringatannya, bukan hanya errornya.

Kalau ujian praktik atau responsi pemrograman sudah dekat, pola latihan terarah untuk materi semacam ini ada di strategi belajar UAS dan UTS mahasiswa. Untuk menyimpan potongan kode dan keluarannya supaya bisa dibaca ulang saat responsi, formatnya dibahas di cara membuat catatan kuliah efektif.

FAQ

Kalau parameternya kutulis int a[5], apakah ukurannya ikut terkirim?

Tidak. Angka di dalam kurung siku pada parameter fungsi diabaikan kompiler dan tidak diperiksa saat pemanggilan. Satu satunya fungsinya adalah dokumentasi untuk pembaca kodemu.

Adakah cara mengirim array ke fungsi tanpa meluruh?

Ada, dengan pointer ke array. Tulis parameternya void f(int (*a)[5]) lalu panggil dengan f(&data). Di dalam fungsi, sizeof(*a) memberi 20 dan (*a)[i] mengakses elemennya. Konsekuensinya fungsi itu hanya menerima array berukuran tepat 5, jadi cara ini jarang dipakai kecuali ukurannya memang bagian dari kontrak.

Kenapa 8 dan bukan 4?

Karena ukuran pointer mengikuti arsitektur, bukan tipe datanya. Di sistem 64 bit alamat memori butuh 8 byte. Pointer ke char dan pointer ke double sama sama 8 byte, meskipun yang ditunjuk berukuran 1 dan 8 byte.

Apa bedanya char s[] = "halo" dan char *s = "halo"?

Bentuk pertama membuat array berisi salinan lima karakter yang boleh kamu ubah. Bentuk kedua membuat pointer yang menunjuk ke string literal di memori hanya baca, jadi mengubahnya adalah perilaku tidak terdefinisi dan biasanya berakhir crash.

Apakah perilaku yang sama berlaku di C++?

Peluruhan array ke pointer identik di C++. Bedanya C++ punya std::array dan std::vector yang membawa ukurannya sendiri, dan std::span sejak C++20 untuk menerima array tanpa kehilangan panjangnya. Di C murni, panjang tetap harus kamu kirim sendiri.

Kenapa fungsi bisa mengubah isi array padahal C mengirim argumen sebagai salinan?

Yang disalin adalah alamatnya, bukan isinya. Fungsi punya variabel pointer sendiri yang nilainya salinan alamat arraymu, dan lewat alamat itu dia menulis ke memori yang sama. Itu juga alasan kenapa a[i] = 10 di dalam fungsi berpengaruh, sedangkan a = NULL di dalam fungsi tidak berpengaruh ke pemanggilnya.

Artikel Terkait

Siap taklukkan UTS, UAS, dan skripsi?

Gabung gratis di Pintarly. Ubah catatan kuliahmu jadi rangkuman, flashcards, dan kuis otomatis, plus AI tutor yang siap bantu kapan aja butuh.