Artikel ini menjelaskan bagaimana data dalam sebuah program C dapat diacukan dengan menunjuk ke alamat mesin tempat ia disimpan.
- 7.1. Mengakses Data Melalui Penunjuk (pointer) [ video ]
- 7.2. Menggunakan Aritmetika Penunjuk [ video ]
- 7.3. Melewatkan Pointer ke Fungsi [ video ]
- 7.4. Membuat Larik Penunjuk [ video ]
- 7.5. Menunjuk ke Fungsi-fungsi [ video ]
- 7.6. Ringkasan
Catatan:
- Silahkan Anda mengeklik icon menu untuk berpindah subbagian.
- Video pelengkap dalam tutorial ini akan lebih jelas dilihat dengan resolusi 720p dan mode Full screen.
7.1. Mengakses Data Melalui Penunjuk (pointer)
Pointer-pointer merupakan bagian yang sangat berguna untuk pemrograman C yang efisien. Mereka merupakan variabel-variabel yang menyimpan alamat memori variabel-variabel lainnya.
Saat sebuah variabel biasa dideklarasikan maka memori dialokasikan pada sebuah alamat memori yang kosong dan ukurannya sesuai dengan tipe datanya. Selanjutnya, di mana saja program menemukan nama variabel tersebut maka ia mengacu pada data yang tersimpan pada alamat memori tersebut.
Secara hal yang sama, di mana saja program menemukan nama sebuah variabel pointer maka ia mengacu pada alamat yang disimpannya. Namun, sebuah variabel pointer bisa “diungkap” (dereferenced) untuk mengakses nilai atau objek data simpanan pada alamat yang dikandungnya.
Variabel-variabel pointer dideklarasikan dengan cara yang sama seperti deklarasi variabel lainnya namun namanya diberi awalan sebuah tanda “*
“. Dalam kasus ini, karakter *
mewakili “operator tak-langsung” (dereference/indirection operator) dan juga menandakan bahwa variabel yang dideklarasikan merupakan sebuah pointer. Tipe data pointer harus cocok dengan tipe data variabel yang ditunjuknya.
Sekali dideklarasikan, sebuah variabel pointer bisa disematkan alamat variabel lain dengan menggunakan operator “alamat” (address of) &
. Variabel pointer tidak diawali dengan operator tak-langsung *
dalam pernyataan penyematan kecuali pada saat pointer tersebut diinisialisasi seketika dalam pendeklarasiannya.
Nama variabel pointer, saat hanya digunakan sendiri, mengacukan pada sebuah alamat memori yang diekspresikan dalam heksadesimal.
Ketika operator tak-langsung *
digunakan dalam sebuah deklarasi variabel maka memberi kesan sepertinya variabel tersebut adalah sebuah pointer namun memberi prefiks sebuah nama variabel pointer dengan operator tak-langsung *
di suatu lokasi dalam program akan mengungkap data yang tersimpan di alamat yang disematkan pada variabel pointer tersebut.
Itu artinya bahwa sebuah program bisa mendapatkan alamat tersemat melalui sebuah variabel pointer hanya dengan menggunakan namanya, atau bisa mendapatkan data tersimpan di alamat tempat ia menunjuk dengan memberikan prefiks pada namanya menggunakan operator tak-langsung *
.
-
Mulailah membuat sebuah program baru dengan sebuah instruksi prapemroses untuk mengikutsertakan fungsi-fungsi dalam pustaka masukan/keluaran standar.
#include <stdio.h>
-
Tambahkan sebuah fungsi
main()
yang mendeklarasikan dan menginisialisasi sebuah variabel bilangan bulat biasa dan sebuah variabel pointer bilangan bulat.int main() { int v__bilangan = 8 ; int *p__bilangan = &v__bilangan ; }
-
Selanjutnya, dalam blok fungsi
main()
berikan keluaran isi kedua variabel dan nilai yang dibuang acuannya menggunakan pointer.printf( "Variabel biasa berisi: %d\n" , v__bilangan ) ; printf( "Variabel pointer berisi: %p\n" , p__bilangan ) ; printf( "Pointer menunjuk ke nilai: %d\n\n" , *p__bilangan ) ;
-
Sekarang dalam blok fungsi
main()
, sematkan sebuah nilai baru pada variabel biasa melalui variabel pointer, kemudian keluarkan isinya dan nilai tak-langsungnya sekali lagi.*p__bilangan = 12 ; printf( "Variabel biasa berisi: %d\n" , v__bilangan ) ; printf( "Variabel pointer berisi: %p\n" p__bilangan ) ; printf( "Pointer menunjuk ke nilai: %d\n\n" , *p__bilangan ) ;
-
Pada akhir blok fungsi
main()
berikan keluaran sebuah nilai bilangan bulat nol seperti yang disyaratkan pada saat pendeklarasian fungsi.return 0 ;
-
Simpanlah berkas program kemudian kompilasi dan jalankan program untuk melihat isi-isi variabel dan nilai tak-langsungnya.
Secara keseluruhan, Anda dapat melihat langkah-langkah tersebut dalam Video 7.1 (silahkan Anda pilih mode Full screen dan resolusi 720p untuk tampilan video yang lebih jelas):
7.2. Menggunakan Aritmetika Penunjuk
Sekali saja sebuah pointer dibuat dengan sebuah alamat yang disematkan maka ia bisa disematkan ulang atau dipindahkan menggunakan aritmetika ke alamat lain.
Operator penambah cacah ++
dan pengurang cacah --
akan memindahkan pointer maju atau mundur ke alamat memori lainnya untuk tipe data terkait — semakin besar tipe data maka semakin besar lompatannya. Bahkan lompatan terbesar bisa dicapai menggunakan operator +=
dan -=
untuk menentukan seberapa banyak ruang untuk melompat.
Aritmetika pointer secara khusus sangat berguna saat melibatkan larik karena anggota-anggota dalam sebuah larik menempati ruang memori yang berurutan. Hanya dengan menyematkan nama sebuah larik ke sebuah pointer maka secara otomatis menyematkannya ke alamat memori anggota pertama larik tersebut. Menambah cacah pointer dengan satu satuan maka memindahkan pointer sepanjang alamat anggota berikutnya.
-
Mulailah membuat sebuah program baru dengan sebuah instruksi prapemroses untuk mengikutsertakan fungsi-fungsi dalam pustaka masukan/keluaran standar.
#include <stdio.h>
-
Tambahkan sebuah fungsi
main()
yang mendeklarasikan sebuah variabel bilangan bulat, kemudian deklarasikan dan inisialisasi sebuah variabel larik bilangan bulat.int main() { int i ; int v__larik_bilangan[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 } ; }
-
Selanjutnya, dalam blok fungsi
main()
deklarasikan dan inisialisasi sebuah variabel pointer pada anggota pertama larik kemudian berikan keluaran alamat tersebut dan nilai yang diungkap oleh pointer.int *p__bilangan = v__larik_bilangan ; printf( "\nDi alamat: %p bernilai: %d\n" , p__bilangan , *p__bilangan ) ;
-
Sekarang, dalam blok fungsi
main()
tambahkan cacah pointer untuk berpindah sepanjang anggota larik satu demi satu.p__bilangan++ ; printf( "Di alamat: %p bernilai: %d\n" , p__bilangan , *p__bilangan ) ; p__bilangan++; printf( "Di alamat: %p bernilai: %d\n" , p__bilangan , *p__bilangan ) ;
-
Sekarang lompat mundur dua ruang memori ke alamat anggota pertama larik.
p__bilangan -= 2 ; printf( "Di alamat: %p bernilai: %d\n\n" , p__bilangan , *p__bilangan ) ;
-
Selanjutnya tambahkan sebuah perulangan untuk memberi keluaran nomor indeks masing-masing anggota dan nilai yang dikandungnya.
for( i = 0; i < 10; i++ ) { printf( "Anggota %d bernilai: %d\n" , i , *p__bilangan ) ; p__bilangan++ ; }
-
Pada akhir blok fungsi
main()
berikan keluaran sebuah bilangan bulat nol seperti yang disyaratkan pada saat pendeklarasian fungsi.return 0 ;
-
Simpanlah berkas program kemudian kompilasi dan jalankan program untuk melihat nilai-nilai keluaran sepanjang perpindahan pointer antara anggota larik.
Secara keseluruhan, Anda dapat melihat langkah-langkah tersebut dalam Video 7.2 (silahkan Anda pilih mode Full screen dan resolusi 720p untuk tampilan video yang lebih jelas):
7.3. Melewatkan Pointer ke Fungsi
Dalam program C, argumen-argumen fungsi melewatkan data-datanya “berdasar nilai” ke sebuah variabel lokal di dalamnya untuk fungsi yang dipanggil. Itu artinya bahwa fungsi tidak beroperasi pada nilai aslinya, melainkan pada sebuah salinannya.
Melewatkan sebuah pointer ke nilai asli bisa menyelesaikan halangan tersebut sehingga mengizinkan fungsi yang dipanggil beroperasi pada nilai aslinya. Teknik melewatkan data “berdasar acuan” merupakan sebuah keuntungan yang disediakan oleh pointer.
-
Mulailah sebuah program baru dengan sebuah instruksi prapemroses untuk mengikutsertakan fungsi-fungsi dalam pustaka masukan/keluaran standar.
#include <stdio.h>
-
Deklarasikan dua prototipe fungsi tersuai (custom function) yang masing-masing melewatkan sebuah variabel pointer bilangan bulat ke sebuah fungsi.
void f__dua_kali( int *pf__bilangan ) ; void f__tiga_kali( int *pf__bilangan ) ;
-
Tambahkan sebuah fungsi
main()
yang mendeklarasikan dan menginisialisasi sebuah variabel biasa bertipe data bilangan bulat dengan sebuah nilai numerik, dan sebuah variabel pointer bilangan bulat dengan alamat variabel biasa tersebut.int main() { int v__bilangan = 5 ; int *p__bilangan = &v__bilangan ; }
-
Selanjutnya, dalam blok fungsi
main()
berikan keluaran alamat tersimpan menggunakan variabel pointer dan nilai tak-langsungnya.printf( "p__bilangan menyimpan alamat: %p\n" , p__bilangan ) ; printf( "Nilai *p__bilangan: %d\n\n" , *p__bilangan ) ;
-
Sekarang keluarkan nilai asli variabel biasa bilangan bulat.
printf( "Nilai v__bilangan adalah %d\n" , v__bilangan ) ;
-
Setelah blok fungsi
main()
, definisikan dua fungsi tersuai yang dideklarasikan oleh prototipe, yang masing-masing menerima sebuah pointer bilangan bulat sebagai sebuah argumen ketika dipanggil.void f__dua_kali( int *pf__bilangan ) { *pf__bilangan = ( *pf__bilangan * 2 ) ; } void f__tiga_kali( int *pf__bilangan ) { *pf__bilangan = ( *pf__bilangan * 3 ) ; }
-
Kembali ke blok fungsi
main()
, tambahkan panggilan untuk setiap fungsi tersuai, melewatkan alamat variabel biasa sebagai acuan, dan memberikan keluaran nilai asli variabel biasa yang diubah.f__dua_kali( p__bilangan ) ; printf( "Nilai v__bilangan sekarang adalah %d\n" , v__bilangan ) ; f__tiga_kali( p__bilangan ) ; printf( "dan nilai v__bilangan sekarang adalah %d\n" , v__bilangan ) ;
-
Pada akhir blok fungsi
main()
berikan keluaran sebuah nilai bilangan bulat nol seperti yang disyaratkan pada saat pendeklarasian fungsi.return 0 ;
-
Simpanlah berkas program kemudian kompilasi dan jalankan program untuk melihat nilai-nilai keluaran setelah melewatkan sebuah acuan.
Secara keseluruhan, Anda dapat melihat langkah-langkah tersebut dalam Video 7.3 (silahkan Anda pilih mode Full screen dan resolusi 720p untuk tampilan video yang lebih jelas):
7.4. Membuat Larik Penunjuk
Sebuah program C bisa berisi pointer larik yang setiap anggota sebuah larik menyimpan alamat variabel-variabel lain.
Kemampuan tersebut secara khusus berguna untuk menangani untaian karakter. Sebuah larik karakter yang berakhir dengan karakter null \0
memiliki status sebagai untaian karakter, sehingga bisa disematkan ke sebuah variabel pointer. Nama sebuah larik untaian char
berlaku seperti sebuah pointer pada anggota pertamanya sehingga operator alamat &
tidak dibutuhkan saat menyematkan sebuah kalimat ke sebuah variabel pointer.
-
Mulailah sebuah program baru dengan sebuah instruksi prapemroses untuk mengikutsertakan fungsi-fungsi dalam pustaka masukan/keluaran standar.
#include <stdio.h>
-
Tambahkan sebuah fungsi
main()
yang mendeklarasikan sebuah variabel bilangan bulat, kemudian deklarasikan dan inisialisasi sebuah variabel larik bilangan bulat.int main() { int i ; int v__larik_bilangan[5] = { 1, 2, 3, 4, 5 } ; }
-
Selanjutnya dalam blok fungsi
main()
, deklarasikan dan inisialisasi beberapa variabel pointer bilangan bulat dengan alamat masing-masing anggota.int *p__bilangan_0 = &v__larik_bilangan[0] ; int *p__bilangan_1 = &v__larik_bilangan[1] ; int *p__bilangan_2 = &v__larik_bilangan[2] ; int *p__bilangan_3 = &v__larik_bilangan[3] ; int *p__bilangan_4 = &v__larik_bilangan[4] ;
-
Sekarang deklarasikan dan inisialisasi sebuah larik pointer bilangan bulat dengan setiap anggotanya berisi satu pointer bilangan bulat.
int *p__larik_bilangan[5] = { p__bilangan_0 , p__bilangan_1 , p__bilangan_2 , p__bilangan_3 , p__bilangan_4} ;
-
Selanjutnya, deklarasikan dan inisialisasi sebuah larik karakter, sebuah pointerke larik tersebut, dan sebuah larik pointer karakter dengan sebuah kalimat di setiap anggotanya.
char v__larik_karakter[12] = { 'H' , 'a', 'l', 'o', ' ', 'd' , 'u', 'n', 'i', 'a', '!', '\0' } ; char *p__kalimat = v__larik_karakter ; char *p__larik_kata[3] = { "Apel" , "Belimbing" , "Ceri" } ;
-
Tambahkan sebuah perulangan untuk mengeluarkan alamat setiap anggota larik pointer bilangan bulat dan nilai-nilai tak-langsungnya.
for( i = 0; i < 5; i++ ) { printf( "Nilai di %p : %d\n" , p__larik_bilangan[i] , *p__larik_bilangan[i] ) ; }
-
Tambahkan sebuah pernyataan untuk mengeluarkan nilai yang tersimpan dalam larik karakter.
printf( "\nKalimatnya : %s\n\n" , p__kalimat ) ;
-
Tambahkan sebuah perulangan untuk mengeluarkan kata yang tersimpan dalam masing-masing anggota larik pointer kata.
for( i = 0; i < 3; i++ ) { printf( "Kata ke-%d : %s\n" , (i+1), p__larik_kata[i] ) ; }
-
Pada akhir blok fungsi
main()
berikan keluaran nilai bilangan bulat nol seperti yang disyaratkan pada saat pendeklarasian fungsi.return 0 ;
-
Simpanlah berkas program kemudian kompilasi dan jalankan program untuk melihat nilai-nilai keluaran dari anggota larik pointer.
Secara keseluruhan, Anda dapat melihat langkah-langkah tersebut dalam Video 7.4 (silahkan Anda pilih mode Full screen dan resolusi 720p untuk tampilan video yang lebih jelas):
7.5. Menunjuk ke Fungsi-fungsi
Semua pointer bisa dibuat untuk menunjuk ke fungsi-fungsi, walaupun kemampuan tersebut lebih sedikit digunakan daripada pointer yang menunjuk ke nilai-nilai data.
Sebuah pointer ke sebuah fungsi sama seperti sebuah pointer ke data namun harus selalu diapit dalam tanda kurung saat menggunakan operator tak-langsung *
untuk menghindari kesalahan compiler. Itu juga diikuti dengan tanda kurung lebih lanjut berisi sembarang argumen yang dilewatkan ke fungsi ke tempat mana pointer menunjuk.
Pointer fungsi berisi alamat dalam memori di tempat mana titik awal fungsi tersebut. Saat pointer fungsi diungkap maka fungsi yang ditunjuknya dipanggil, dan argumen-argumen yang diberikan ke pointer fungsi dilewatkan ke fungsi yang dipanggil.
Sebuah pointer fungsi bahkan bisa dilewatkan sebagai sebuah argumen ke fungsi lain — sehingga fungsi penerima bisa memanggil fungsi ke titik mana pointer fungsi menunjuk.
-
Mulailah membuat sebuah program baru dengan sebuah instruksi prapemroses untuk mengikutsertakan fungsi-fungsi masukan/keluaran standar.
#include <stdio.h>
-
Deklarasikan sebuah prototipe fungsi tersuai yang memiliki sebuah argumen bilangan bulat dan lainnya yang memiliki sebuah argumen pointer fungsi dan sebuah argumen bilangan bulat.
int f__loncat( int vf__a ) ; int f__pemanggil( int (*pf__fungsi) (int) , int vf__b ) ;
-
Tambahkan sebuah fungsi
main()
yang mendeklarasikan sebuah variabel bilangan bulat, kemudian deklarasikan dan inisialisasi sebuah variabel pointer fungsi.int main() { int v__bilangan ; int (*fp__fungsi)() = f__loncat ; }
-
Setelah blok fungsi
main()
, definisikan fungsi tersuai yang pertama untuk memberi keluaran nilai yang diterima dan juga sebuah bilangan bulat.int f__loncat( int vf__a ) { printf( "\nNilai yang diterima: %d\n" , vf__a ) ; return ( ( 2 * vf__a ) + 2 ) ; }
-
Selanjutnya, definisikan fungsi tersuai yang kedua untuk memanggil sebuah fungsi biasa dari sebuah pointer fungsi yang diterima, dan melewatkan nilai sebuah bilangan bulat yang diterimanya.
int f__pemanggil( int (*pf__fungsi)(int) , int vf__b ) { (*pf__fungsi)(vf__b) ; }
-
Kembali ke blok fungsi
main()
, sematkan sebuah nilai ke variabel bilangan bulat dengan memanggil fungsi biasa melalui pointer fungsi, dan mengeluarkan nilainya.v__bilangan = (*fp__fungsi)(5) ; printf( "Nilai yang dikeluarkan: %d\n" , v__bilangan ) ;
-
Sekarang sematkan sebuah nilai baru ke variabel bilangan bulat dengan melewatkan pointer fungsi dan sebuah bilangan bulat ke fungsi lain, yang nantinya akan memanggil fungsi biasa kemudian memberikan nilai keluarannya.
v__bilangan = f__pemanggil( fp__fungsi , 3 ) ; printf( "Nilai yang dikeluarkan: %d\n" , v__bilangan ) ;
-
Pada akhir blok fungsi
main()
berikan keluaran sebuah bilangan bulat nol seperti yang disyaratkan pada saat pendeklarasian fungsi.return 0 ;
-
Simpanlah berkas program kemudian kompilasi dan jalankan program untuk melihat nilai yang dikeluarkan melalui pointer fungsi.
Secara keseluruhan, Anda dapat melihat langkah-langkah tersebut dalam Video 7.5 (silahkan Anda pilih mode Full screen dan resolusi 720p untuk tampilan video yang lebih jelas):
7.6. Ringkasan
- Sebuah pointer merupakan sebuah variabel yang menyimpan alamat memori variabel lainnya yang diungkapkan dalam bentuk bilangan heksadesimal.
- Operator tak-langsung (dereference operator)
*
digunakan untuk mengacukan nilai yang dikandung dalam variabel tempat pointer menunjuk. - Dalam deklarasi sebuah variabel pointer, nama variabel diberi awalan karakter
*
untuk menandakan bahwa variabel tersebut adalah sebuahpointer
. - Sebuah variabel pointer bisa disematkan alamat variabel lain dengan menggunakan operator alamat (address of operator)
&
. - Aritmetika pointer mengizinkan sebuah pointer berpindah antar ruang memori secara berurutan dan secara khusus sangat berguna untuk berpindah antar anggota dalam larik.
- Saat sebuah nama variabel bilangan bulat larik disematkan ke sebuah pointer maka pointer tersebut secara otomatis menyimpan alamat memori anggota pertama larik terkait.
- Semua pointer dapat dilewatkan sebagai argumen-argumen ke fungsi-fungsi lain.
- Melewatkan sebuah pointer sebagai sebuah argumen fungsi berdasar acuan maka mengizinkan fungsi untuk beroperasi pada nilai aslinya.
- Setiap anggota dalam sebuah larik pointer bisa menyimpan alamat variabel lainnya.
- Sebuah larik pointer secara khusus sangat berguna untuk menangani untaian karakter-karakter.
- Saat sebuah nama larik variabel untaian karakter disematkan ke sebuah pointer maka pointer tersebut secara otomatis menyimpan keseluruhan untaian.
- Sebuah pointer ke sebuah fungsi harus selalu dikurung dalam tanda kurung saat menggunakan operator tak-langsung
*
untuk menghindari kesalahan compiler. - Mengungkap sebuah pointer fungsi akan memanggil fungsi ke lokasi mana ia menunjuk dan melewatkan argumen apa saja yang ditentukan ke pointer tersebut.
Daftar Pustaka
- Kernighan, Brian W. & Ritchie, Dennis M. (1988). The C Programming Language. New Jersey: Prentice Hall.
- Griffiths, David & Griffiths, Dawn (2012). Head First C. California: O’Reilly.
- McGarth, Mike (2012). C Programming: Grasp the Nuts and Bolts of Programming. Warwickshire: In Easy Steps Ltd.
- Stevanovic, Milan (2014). Advanced C and C++ Compiling: An Engineering Guide to Compiling, Linking, and Libraries using C and C++. California: Apress.