Pointer arithmetic adalah salah satu kemampuan paling khas C yang membuat bahasa ini begitu ekspresif untuk operasi level memori. Berbeda dengan aritmetika integer biasa, menambahkan 1 pada sebuah pointer tidak selalu menambahkan 1 byte ke alamat—melainkan menambahkan sizeof(tipe_yang_ditunjuk) byte. Artinya int *p; p + 1 maju 4 byte, sedangkan char *c; c + 1 maju hanya 1 byte. Inilah mengapa kita bisa menelusuri elemen array dengan pointer tanpa peduli ukuran elemen. Pada chapter ini kita akan membandingkan aritmetika pada int* dan char*, serta mempraktikkan iterasi array dengan pointer sebagai alternatif loop berbasis indeks.
Cara Kerja Pointer Arithmetic #
Pada tipe pointer T *p, ekspresi p + n menghasilkan pointer baru yang menunjuk ke region memori n * sizeof(T) byte setelah p. Kebalikannya, p - n menghasilkan pointer n * sizeof(T) byte sebelum p. Dua pointer dengan tipe yang sama dapat dikurangkan (p2 - p1) untuk memperoleh jumlah elemen di antara keduanya—bukan jumlah byte. Standar C (ISO/IEC 9899 §6.5.6) juga mengizinkan perbandingan pointer dengan <, >, <=, >=, ==, != selama kedua pointer menunjuk ke elemen dalam array yang sama atau satu-past-the-end.
Mekanisme di balik layar: saat compiler melihat pi + 1 dengan pi bertipe int *, compiler menerjemahkan ekspresi ini menjadi (int*)((char*)pi + 1 * sizeof(int))—yaitu alamat pi digeser maju sizeof(int) byte. Komputasi ini gratis bagi programmer: compiler mengalikan increment dengan ukuran elemen secara otomatis. Jika kita ingin maju 1 byte saja, kita harus cast pointer ke char * terlebih dahulu—seperti yang akan kita lihat pada contoh.
Visualisasi Konsep #
Diagram berikut memperlihatkan bagaimana pointer arithmetic bekerja secara berbeda untuk int* (4 byte per elemen) dan char* (1 byte per elemen):
Pada diagram di atas, array int melompat 4 byte setiap kali pointer bertambah 1, sedangkan array char melompat hanya 1 byte. Compiler secara otomatis mengalikan offset dengan sizeof(T) sesuai tipe pointer—programmer tidak perlu menyebut ukuran eksplisit. Inilah inti pointer arithmetic: tipe pointer membawa informasi ukuran elemen, sehingga pi + 1 dan pc + 1 menghasilkan lompatan alamat yang berbeda meskipun ditulis dengan sintaks yang sama.
#include <stdio.h>
int main(void) {
int data[5] = {10, 20, 30, 40, 50};
int *pi = data;
printf("Alamat awal pi = %p\n", (void*)pi);
printf("Alamat pi + 1 = %p (selisih %ld byte)\n",
(void*)(pi + 1),
(long)((char*)(pi + 1) - (char*)pi));
printf("Alamat pi + 2 = %p (selisih %ld byte)\n",
(void*)(pi + 2),
(long)((char*)(pi + 2) - (char*)pi));
printf("Nilai *(pi + 3) = %d\n", *(pi + 3));
char teks[] = "ABCDE";
char *pc = teks;
printf("\nAlamat awal pc = %p\n", (void*)pc);
printf("Alamat pc + 1 = %p (selisih %ld byte)\n",
(void*)(pc + 1),
(long)((pc + 1) - pc));
printf("Nilai *(pc + 2) = %c\n", *(pc + 2));
return 0;
}Output yang Dihasilkan:
Alamat awal pi = 0x7ffffa105550
Alamat pi + 1 = 0x7ffffa105554 (selisih 4 byte)
Alamat pi + 2 = 0x7ffffa105558 (selisih 8 byte)
Nilai *(pi + 3) = 40
Alamat awal pc = 0x7ffffa10554a
Alamat pc + 1 = 0x7ffffa10554b (selisih 1 byte)
Nilai *(pc + 2) = CPada int *pi, ekspresi pi + 1 maju 4 byte (dari ...550 ke ...554); pi + 2 maju 8 byte (ke ...558). Sebaliknya, pada char *pc, ekspresi pc + 1 maju hanya 1 byte (dari ...54a ke ...54b). Compiler otomatis mengalikan offset dengan sizeof(T) sesuai tipe pointer—maka programmer tidak perlu menyebut ukuran secara eksplisit. Inilah inti pointer arithmetic: tipe pointer membawa informasi ukuran elemen.
Mari kita telaah baris-baris kode di atas:
- Baris 4-5 (
int data[5] = ...; int *pi = data;): arraydataberisi 5intyang menempati alamat berurutan;pimenunjuk ke elemen pertama. Nama arraydataotomatis decay menjadiint *. - Baris 7-12 (
printf("... pi + 1 ...")): cetak alamatpi,pi + 1, danpi + 2. Selisih byte dihitung dengan cast kechar *agar selisih byte (bukan elemen) yang ditampilkan:(char*)(pi + 1) - (char*)pimenghasilkan 4 (ukuranint). - Baris 13 (
printf("Nilai *(pi + 3) = %d", *(pi + 3));): pointer arithmetic + dereference.pi + 3menunjuk ke elemen keempat; dereference membaca nilaiintdi alamat tersebut, yaitu40. - Baris 15-16 (
char teks[] = "ABCDE"; char *pc = teks;): string literal diletakkan ke arraycharyang bisa dimodifikasi;pcmenunjuk ke huruf pertama'A'. - Baris 19 (
printf("... pc + 1 ... selisih %ld", (long)((pc + 1) - pc));): di sinipc + 1danpckeduanya bertipechar *, sehingga selisihnya 1 elemen (= 1 byte) tanpa perlu cast. Hasilnya 1 byte, sesuaisizeof(char). - Baris 21 (
*(pc + 2)): maju 2 byte daripc(yang menunjuk'A'), lalu dereference. Hasilnya'C'—huruf ketiga.
Iterasi Array dengan Pointer #
Pointer arithmetic memungkinkan iterasi array tanpa variabel indeks sama sekali. Kita pakai dua pointer: awal yang menunjuk ke elemen pertama, dan akhir yang menunjuk ke posisi one-past-the-end (satu elemen setelah elemen terakhir). Standar C mengizinkan pembentukan pointer one-past-the-end secara khusus untuk keperluan ini, tetapi pointer tersebut tidak boleh di-dereference.
#include <stdio.h>
int main(void) {
int arr[6] = {5, 10, 15, 20, 25, 30};
int *awal = arr;
int *akhir = arr + 6; /* satu-past-the-end */
printf("Selisih pointer (akhir - awal) = %ld elemen\n",
(long)(akhir - awal));
/* Iterasi dengan pointer, bukan indeks */
int *p = awal;
int i = 0;
while (p != akhir) {
printf(" [%d] alamat %p -> %d\n", i, (void*)p, *p);
p++;
i++;
}
/* Pointer bisa juga dibandingkan dengan < > <= >= */
int *tengah = arr + 3;
printf("\ntengah di antara awal dan akhir? %s\n",
(awal < tengah && tengah < akhir) ? "ya" : "tidak");
return 0;
}Output yang Dihasilkan:
Selisih pointer (akhir - awal) = 6 elemen
[0] alamat 0x7ffe810f9780 -> 5
[1] alamat 0x7ffe810f9784 -> 10
[2] alamat 0x7ffe810f9788 -> 15
[3] alamat 0x7ffe810f978c -> 20
[4] alamat 0x7ffe810f9790 -> 25
[5] alamat 0x7ffe810f9794 -> 30
tengah di antara awal dan akhir? yaEkspresi akhir - awal menghasilkan 6—jumlah elemen, bukan jumlah byte (yang seharusnya 24). Loop while (p != akhir) berhenti tepat saat p mencapai posisi one-past-the-end. Pola ini begitu umum di C dan menjadi dasar idiom iterator di C++ (yang menggeneralisasi konsep ini). Perbandingan awal < tengah && tengah < akhir valid karena ketiga pointer menunjuk ke elemen dalam array yang sama.