Array di C bukan sekadar kumpulan nilai—array adalah blok memori yang menempati alamat berurutan. Memahami bagaimana compiler menyusun array di memori, bagaimana pointer arithmetic bekerja, dan bagaimana padding memengaruhi tipe compound, akan membantu kita menulis kode yang efisien dan menghindari bug halus. Pada chapter ini kita akan menelusuri layout memori array, hubungan antara arr[i] dan *(arr + i), serta konsep alignment dan padding pada struct yang menyimpan array.
Layout Memori Array #
Saat kita mendeklarasikan int arr[5], compiler memesan satu blok memori sebesar 5 * sizeof(int) = 20 byte (pada sistem 64-bit dengan sizeof(int) = 4). Alamat elemen pertama arr[0] adalah alamat awal blok; elemen kedua arr[1] berada tepat 4 byte setelahnya, dan seterusnya. Karena elemen berurutan, kita bisa mengakses elemen mana pun dengan aritmetika pointer.
Alamat Elemen dan Pointer Arithmetic #
Contoh berikut mencetak alamat setiap elemen array, memperlihatkan jaraknya yang seragam, lalu mengakses elemen melalui pointer arithmetic.
#include <stdio.h>
int main(void) {
int arr[5] = {100, 200, 300, 400, 500};
/* Cetak alamat setiap elemen */
printf("Ukuran int: %zu byte\n", sizeof(int));
printf("Alamat tiap elemen:\n");
for (int i = 0; i < 5; i++) {
printf(" &arr[%d] = %p (jarak dari awal = %td)\n",
i, (void *)&arr[i], (char *)&arr[i] - (char *)&arr[0]);
}
/* Akses elemen via pointer aritmetika */
int *p = arr;
printf("\nAkses via pointer:\n");
for (int i = 0; i < 5; i++) {
printf(" *(p + %d) = %d\n", i, *(p + i));
}
/* Demonstrasi alignment: struct layout */
struct S {
char c;
int x;
};
printf("\nsizeof(char) = %zu\n", sizeof(char));
printf("sizeof(int) = %zu\n", sizeof(int));
printf("sizeof(struct S { char; int; }) = %zu (ada padding)\n", sizeof(struct S));
return 0;
}Output yang Dihasilkan:
Ukuran int: 4 byte
Alamat tiap elemen:
&arr[0] = 0x7ffc562c6650 (jarak dari awal = 0)
&arr[1] = 0x7ffc562c6654 (jarak dari awal = 4)
&arr[2] = 0x7ffc562c6658 (jarak dari awal = 8)
&arr[3] = 0x7ffc562c665c (jarak dari awal = 12)
&arr[4] = 0x7ffc562c6660 (jarak dari awal = 16)
Akses via pointer:
*(p + 0) = 100
*(p + 1) = 200
*(p + 2) = 300
*(p + 3) = 400
*(p + 4) = 500
sizeof(char) = 1
sizeof(int) = 4
sizeof(struct S { char; int; }) = 8 (ada padding)Mari kita telaah baris-baris kode di atas:
- Baris 4 (
int arr[5] = {...}): array 5 elemenint. Total memori = 5 × 4 = 20 byte. - Baris 7 (
printf("Ukuran int: %zu byte\n", sizeof(int));): mencetaksizeof(int)= 4 byte—menjadi dasar perhitungan offset alamat. - Baris 10 (
printf(" &arr[%d] = %p (jarak dari awal = %td)\n", i, (void *)&arr[i], (char *)&arr[i] - (char *)&arr[0]);):(void *)&arr[i]mencetak alamat memori elemen ke-idalam format heksadesimal pointer. Cast ke(char *)memungkinkan pengurangan alamat menghasilkan offset dalam byte (bukan dalam unit elemen). - Baris 14 (
int *p = arr;): pointerpmenunjuk ke elemen pertama array. Ekspresiarrdi sini meluruh menjadi pointer kearr[0]—sama dengan&arr[0]. - Baris 18 (
*(p + i)): pointer arithmetic.p + imenambah alamat sebanyaki * sizeof(int)byte, lalu*dereference alamat tersebut untuk membaca nilai. Hasilnya sama denganarr[i]. - Baris 22-26 (
struct S { char c; int x; };): struct dengan dua field. Walaupunsizeof(char) = 1dansizeof(int) = 4, totalsizeof(struct S) = 8—bukan5. Selisih 3 byte adalah padding yang ditambahkan compiler agarint xmulai pada alamat yang kelipatan 4 (alignment).
Pointer Arithmetic: arr[i] Ekuivalen dengan *(arr + i) #
Karena array meluruh menjadi pointer dan aritmetika pointer bekerja dengan satuan elemen, ekspresi arr[i] dan *(arr + i) benar-benar identik di C. Bahkan lebih—ekspresi i[arr] juga valid (komutatif), walaupun tidak lazim dipakai. Contoh berikut membuktikan kesetaraan ini.
#include <stdio.h>
int main(void) {
int a[4] = {10, 20, 30, 40};
/* arr[i] identik dengan *(arr + i) */
int *p = a;
printf("a[0] = %d, *p = %d\n", a[0], *p);
printf("a[2] = %d, *(p + 2) = %d\n", a[2], *(p + 2));
/* a + 1 menambah sizeof(int) byte, bukan 1 byte */
printf("p = %p\n", (void *)p);
printf("p + 1 = %p (naik %zu byte)\n", (void *)(p + 1), sizeof(int));
/* Demonstrasi arr[i] == i[arr] (sintaks C合法 tapi jangan dipakai) */
printf("a[3] = %d, 3[a] = %d\n", a[3], 3[a]);
return 0;
}Output yang Dihasilkan:
a[0] = 10, *p = 10
a[2] = 30, *(p + 2) = 30
p = 0x7ffd05009850
p + 1 = 0x7ffd05009854 (naik 4 byte)
a[3] = 40, 3[a] = 40Pada contoh di atas, alamat p + 1 naik 4 byte dari p (bukan 1 byte), karena aritmetika pointer int * bergerak dalam satuan sizeof(int). Hasil 3[a] = a[3] = 40—kedua ekspresi diterjemahkan menjadi *(a + 3). Meskipun valid, menulis 3[a] membingungkan pembaca; selalu gunakan a[3] untuk kejelasan.