Aritmetika pointer adalah operasi penjumlahan, pengurangan, dan perbandingan yang dilakukan terhadap alamat. Operatornya sama dengan operator bilangan biasa, tetapi makna satu langkahnya ditentukan oleh tipe yang ditunjuk. Topik ini menjelaskan mengapa p + 1 tidak selalu berpindah satu byte, bagaimana dua pointer dibandingkan dan dikurangkan, serta batasan yang membuat operasi tersebut sah. Pemahaman ini menjadi kunci teknik iterasi berbasis pointer yang dipakai pada array dan string.
Penjumlahan pointer bergeser seukuran tipe#
Apabila p menunjuk elemen sebuah array bertipe T, penjumlahan p + n menghasilkan pointer yang menunjuk n elemen setelahnya, dan jarak byte yang ditempuh mengikuti rumus berikut.
Rumus itulah alasan satu langkah int * berpindah empat byte pada mesin yang menjalankan contoh modul ini, sedangkan satu langkah double * berpindah delapan byte. Kompilator mengalikan n dengan ukuran tipe secara otomatis, sehingga penulis program tidak perlu mengalikannya sendiri. Program berikut mengukur jarak byte hasil pergeseran pada dua tipe berbeda.
#include <stdio.h>
int main(void)
{
int data[4] = {10, 20, 30, 40};
int *p = data;
printf("int : selisih byte p+1 dari p : %ld\n",
(long)((char *)(p + 1) - (char *)p));
printf("int : selisih byte p+3 dari p : %ld\n",
(long)((char *)(p + 3) - (char *)p));
printf("int : isi *(p + 2) : %d\n", *(p + 2));
double suhu[3] = {36.5, 37.0, 38.5};
double *q = suhu;
printf("double: selisih byte q+1 dari q : %ld\n",
(long)((char *)(q + 1) - (char *)q));
return 0;
}int : selisih byte p+1 dari p : 4
int : selisih byte p+3 dari p : 12
int : isi *(p + 2) : 30
double: selisih byte q+1 dari q : 8Satu langkah pointer int berjarak empat byte karena sizeof(int) bernilai empat, sedangkan pointer double berjarak delapan byte. Jarak p + 3 yang berlipat tiga pada keluaran menegaskan bahwa pengali n bekerja pada satuan elemen, bukan pada byte secara langsung. Diagram berikut merangkum pergeseran tersebut pada array int.
Aritmetika pointer menghilangkan perkalian manual yang seharusnya ditulis saat bergeser antarelemen. Tanpa aritmetika, memindahkan penunjuk satu elemen memerlukan ekspresi seperti (char *)p + sizeof(int), sedangkan bentuk p + 1 memberikan hasil yang sama. Inilah alasan perulangan berbasis pointer terasa ringkas.
Pengurangan dan perbandingan dua pointer#
Pengurangan dua pointer yang menunjuk array yang sama menghasilkan jumlah elemen di antara keduanya, bukan jumlah byte. Hasilnya bertipe ptrdiff_t, yaitu tipe bilangan bertanda untuk selisih indeks, sehingga contoh mengonversinya ke long sebelum dicetak. Pointer juga dapat dibandingkan dengan operator relasional, dan perbandingan yang paling sering digunakan adalah menguji apakah pointer belum melewati ujung array.
#include <stdio.h>
int main(void)
{
int data[5] = {7, 14, 21, 28, 35};
int *awal = data;
int *akhir = data + 5;
printf("akhir - awal (jumlah elemen): %ld\n", (long)(akhir - awal));
int jumlah = 0;
for (int *p = awal; p < akhir; p++) {
jumlah += *p;
}
printf("Jumlah seluruh elemen : %d\n", jumlah);
int *tengah = data + 2;
printf("tengah > awal (1 = ya) : %d\n", tengah > awal);
return 0;
}akhir - awal (jumlah elemen): 5
Jumlah seluruh elemen : 105
tengah > awal (1 = ya) : 1Ekspresi data + 5 menunjuk satu posisi setelah elemen terakhir. Keadaan seperti ini disebut one past the end, dan standar C mengizinkannya selama pointer itu hanya dihitung atau dibandingkan, tidak pernah didereference. Perulangan for pada program menggunakan aturan ini sebagai batas berhentinya, sehingga penjumlahan lima elemen berlangsung tanpa membaca di luar array.
Penulisan p++ pada perulangan tersebut layak dibedah sejenak karena sering dipertukarkan dengan p += 1 maupun p = p + 1. Ketiganya menghasilkan pergeseran yang sama persis, yaitu satu elemen ke arah alamat yang lebih tinggi, sedangkan p-- bergerak satu elemen sebaliknya. Operator ++ dan -- pada pointer bekerja pada satuan elemen, bukan pada byte, karena keduanya hanyalah bentuk ringkas dari penjumlahan dan pengurangan pointer. Setelah kebiasaan ini terbentuk, perulangan berbasis pointer terasa seperti menggerakkan kursor di atas array.
Batasan aritmetika pointer#
Aritmetika pointer hanya bermakna di dalam satu array yang sama. Standar C mengizinkan pointer bergerak dari elemen pertama hingga satu posisi setelah elemen terakhir, dan segala perhitungan di luar rentang itu menimbulkan undefined behavior. Perbandingan dua pointer dari array yang berbeda juga tidak terdefinisi. Alasannya, alamat dua objek yang tidak berkaitan tidak menjamin urutan apa pun, sekalipun kedua array dideklarasikan berdampingan dalam satu fungsi. Untuk pointer generik void *, standar C bahkan melarang aritmetika sama sekali karena ukuran objek yang ditunjuk tidak diketahui.
// tidak-dijalankan
int a[3] = {1, 2, 3};
int b[3] = {4, 5, 6};
int *pa = a;
int *pb = b;
/* pb - pa; tidak sah: kedua pointer tidak berasal dari array yang sama */
/* *(pa + 5); tidak sah: melewati satu posisi setelah elemen terakhir */Perulangan p < akhir pada bagian sebelumnya aman karena awal dan akhir sama-sama berasal dari array data. Prinsip umumnya sederhana, yaitu simpan batas array bersama dengan pointer penunjuknya, lalu pastikan keduanya berasal dari objek yang sama. Kompilasi dengan opsi -Wall -Wextra turut membantu, karena sebagian operasi di luar batas terdeteksi sebagai peringatan pada waktu kompilasi. Dengan kebiasaan itu, hampir seluruh bug aritmetika pointer dapat dicegah sebelum program dijalankan.
Kesalahan umum#
- Mengira
p + 1berpindah satu byte. Pergeseran dihitung berdasarkan ukuran tipe yang ditunjuk, sehingga pointerintberpindah empat byte pada mesin contoh. Konversi kechar *hanya perlu dilakukan apabila memang ingin bergerak per byte. - Menderereference pointer pada posisi satu setelah ujung array. Menyimpannya sah, tetapi membaca atau menulis lewat pointer itu merusak memori di luar array. Gunakan pointer semacam itu hanya sebagai batas perbandingan.
- Mengurangkan pointer dari dua array yang berbeda. Hasilnya tidak terdefinisi meskipun program tampak berjalan normal. Pastikan kedua sisi pengurangan berasal dari objek yang sama.
Ringkasan#
p + nmenunjuknelemen setelahp, dengan jarak byte sebesarndikalisizeoftipe yang ditunjuk.- Pengurangan dua pointer dari array yang sama menghasilkan jumlah elemen di antaranya, dan perbandingan
<serta==menjadi batas perulangan yang lazim. - Aritmetika pointer hanya sah di dalam satu array, sampai dengan satu posisi setelah elemen terakhir, dan tidak berlaku untuk
void *.
Referensi#
- Arithmetic operators — cppreference.com — Kaidah penjumlahan dan pengurangan pointer beserta syarat sahnya.
- Array declaration — cppreference.com — Hubungan subscript array dengan aritmetika pointer dan batas aksesnya.