Bab. 6 Pointer
Tujuan
Instruksional
|
|
·
Definisi dan
inisialisasi variabel pointer.
·
Operator pointer.
·
Pelewatan argumen
ke fungsi lewat referensi.
·
Penggunaan
kualifier const dengan pointer.
·
Pengurutan bubble
menggunakan pemanggilan-
dengan-referensi.
|
·
Operator sizeof.
·
Ekspresi pointer
dan aritmatika pointer.
·
Relasi antara
pointer dan array.
·
Array pointer.
·
Pointer ke fungsi.
|
6.1 Introduksi
Pada bab ini, akan didiskusikan salah
satu fitur paling penting dari bahasa pemrograman C, pointer. Pointer adalah
kapabilitas C yang paling susah dipahami. Pointer memampukan program untuk
mensimulasikan pemanggilan-dengan-referensi dan untuk menciptakan dan
memanipulasi struktur data dinamis, yaitu struktur data yang dapat mengembang
dan menyusut pada saat eksekusi, seperti senarai berantai (linked list), antrian (queue),
tumpukan (stack), dan pohon (tree). Bab ini akan menjelaskan konsep
dasar pointer. Bab 9 akan menguji penggunaan pointer dengan struktur. Bab 11
akan mengenalkan teknik pengelolaan memori dinamis dan menyajikan beberapa
contoh dalam menciptakan dan menggunakan struktur data dinamis.
6.2 Definisi dan Insialisasi Variabel
Pointer
Pointer adalah variabel yang memiliki
nilai berupa alamat memori. Normalnya, variabel secara langsung memuat sebuah
nilai spesifik. Pointer, di sisi lain, memuat alamat suatu variabel yang memuat
sebuah nilai. Nama variabel secara langsung mereferensi suatu nilai, dan
pointer secara tidak langsung mereferensi suatu nilai (Gambar 6.1). Pereferensian sebuah nilai melalui suatu
pointer dinamakan dengan indireksi.
Gambar 6.1 | Langsung dan tak-langsung
mereferensi sebuah variabel
Pointer, seperti semua variabel, harus
didefinisikan sebelum digunakan. Definisi
int *hitungPtr, hitung;
menspesifikasi bahwa variabel hitungPtr bertipe int * (yaitu, sebuah pointer yang menunjuk ke suatu integer) dan dibaca sebagai
“hitungPtr adalah sebuah pointer yang menunjuk ke
suatu int” atau “hitungPtr menunjuk ke suatu objek bertipe int”. Di samping itu, variabel hitung didefinisikan sebagai sebuah int, bukan suatu pointer ke sebuah int. Simbol * hanya dipakai
untuk hitungPtr di dalam definisi tersebut. Ketika * digunakan dengan cara ini di dalam suatu definisi, ia
mengindikasikan bahwa variabel yang sedang didefinisikan adalah pointer.
Pointer dapat didefinisikan untuk menunjuk ke objek sembarang tipe.
Pointer harus diinisialisasi ketika
didefinisikan atau di dalam sebuah statemen penugasan. Pointer bisa
diinisialisasi dengan NULL, 0, atau sebuah
alamat. Pointer dengan nilai NULL tidak menunjuk
apapun. NULL adalah sebuah konstanta simbolik yang
didefinisikan di dalam header <stddef.h> (dan beberapa header lain, seperti <stdio.h>). Penginisialisasian sebuah pointer dengan 0 sama dengan
penginisialisasian sebuah pointer dengan NULL, tetapi NULL lebih
direkomendasikan. Ketika 0 ditugaskan kepada pointer, pertama-tama 0 dikonversi
menjadi suatu pointer yang bertipe sesuai. Nilai 0 merupakan satu-satunya nilai
integer yang bisa ditugaskan secara langsung kepada variabel pointer. Penugasan
alamat suatu variabel kepada sebuah pointer akan didiskusikan pada bagian 6.3.
6.3 Operator Pointer
Oparator alamat & merupakan suatu operator unary yang menghasilkan nilai
balik berupa sebuah alamat operand. Sebagai contoh, diasumsikan definisi
int y = 5;
int *yPtr;
statemen
yPtr = &y;
menugaskan alamat dari variabel y kepada variabel pointer yPtr. Variabel yPtr kemudian
dikatakan “menunjuk ke” y. Gambar 6.2
menampilkan representasi skematik atas memori setelah statemen tersebut
dieksekusi.
Gambar 6.2 | Representasi grafikal atas sebuah
pointer yang menunjuk ke suatu variabel integer di dalam memori
Gambar 6.3 menampilkan representasi
atas pointer di dalam memori, bila diasumsikan bahwa variabel y disimpan pada lokasi 600000, dan variabel pointer yPtr disimpan pada
lokasi 500000. Operand dari operator alamat harus
berupa sebuah variabel; operator alamat tidak dapat diterapkan pada konstanta,
pada ekspresi, atau pada variabel yang dideklarasikan dengan register.
Gambar 6.3 | Representasi atas y dan yPtr di dalam memori
Operator unary *, umumnya dikenal sebagai operator indireksi atau operator
dereferensi, menghasilkan nilai balik berupa nilai dari objek yang ditunjuk
operandnya. Sebagai contoh,
printf( "%d", *yPtr );
menampilkan nilai dari variabel y, misalnya 5. Penggunaan * dengan cara ini disebut dengan mendereferensi suatu pointer.
Gambar 6.4 mendemonstrasikan operator
pointer & dan *. Penspesifikasi konversi printf, %p, menampilkan lokasi memori sebagai integer heksadesimal. Perhatikan bahwa
alamat dari a dan nilai dari aPtr adalah identik pada keluaran, sehingga menegaskan bahwa
alamat dari a memang
ditugaskan kepada variabel pointer aPtr (baris 11).
Operator & dan * adalah komplemen satu sama lain. Gambar 6.5 mencantumkan derajat
keutamaan dan asosiatifitas yang telah dikenalkan sejauh ini.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
/* Gambar 6.4: gambar06_04.c
Menggunakan operator & dan */
#include <stdio.h>
int main( void )
{
int
a; /* a adalah suatu integer */
int
*aPtr; /* aPtr adalah suatu pointer yang menunjuk ke
sebuah integer */
a =
7;
aPtr
= &a; /* aPtr ditetapkan bernilai alamat dari a */
printf( "Alamat dari a adalah %p"
"\nNilai dari aPtr adalah %p", &a, aPtr);
printf( "\n\nNilai dari a adalah %d"
"\nNilai dari *aPtr adalah %d", a, *aPtr);
printf( "\n\nMenunjukkan bahwa * dan & komplemen "
"satu sama lain\n&*aPtr = %p"
"\n*&aPtr = %p\n", &*aPtr, *&aPtr);
return
0; /* indikasi terminasi sukses */
} /*
akhir main */
|
Alamat dari a adalah 0018FF44
Nilai dari aPtr adalah 0018FF44
Nilai dari a adalah 7
Nilai dari *aPtr adalah 7
Menunjukkan bahwa * dan &
komplemen satu sama lain
&*aPtr = 0018FF44
*&aPtr = 0018FF44
Gambar 6.4 | Penggunaan
operator & dan *
Operator
|
Asosiatifitas
|
Tipe
|
[] ()
++ --
! * &
(tipe)
* / %
+ -
< <=
> >=
== !=
&
||
?:
= +=
-= *= /=
%=
,
|
kiri ke kanan
kanan ke kiri
kiri ke kanan
kiri ke kanan
kiri ke kanan
kiri ke kanan
kiri ke kanan
kiri ke kanan
kanan ke kiri
kanan ke kiri
kiri ke kanan
|
tertinggi
unary
multiplikatif
aditif
relasional
ekualitas
AND logikal
OR logikal
kondisional
penugasan
koma
|
Gambar 6.5 | Keutamaan dan asosiatifitas operator
6.4 Pelewatan Argumen ke Fungsi dengan
Referensi
Ada dua cara dalam melewatkan argumen
ke suatu fungsi, pemanggilan-dengan-nilai dan pemanggilan-dengan referensi. Semua
argumen di dalam C dilewatkan dengan nilai. Seperti yang telah Anda lihat pada
Bab 4, statemen return dipakai untuk
menghasilkan nilai balik dari fungsi terpanggil kepada pemanggil (atau untuk
mengembalikan kendali program dari fungsi terpanggil tanpa perlu menghasilkan
nilai balik). Banyak fungsi memerlukan kapabilitas untuk memodifikasi satu atau
lebih variabel di dalam pemanggil dan untuk melewatkan sebuah pointer ke objek
data yang besar agar menghindar penyalinan objek. Untuk kepentingan ini, C menyediakan
kapabilitas pemanggilan-dengan-referensi.
Dalam C, Anda bisa menggunakan pointer
dan operator indireksi untuk mensimulasikan pemanggilan-dengan-referensi.
Ketika memanggil suatu fungsi dengan argumen yang seharusnya dimodifikasi,
alamat argumen yang harus dilewatkan.
Hal ini dilakukan dengan menggunakan
operator alamat (&) terhadap
variabel (di dalam pemanggil) yang memiliki yang akan dimodifikasi. Seperti
yang Anda telah pelajari pada Bab 5, array tidak dilewatkan menggunakan
operator & karena C secara otomatis melewatkan
lokasi awal array di dalam memori (nama suatu array ekivalen dengan &namaArray[0]). Ketika alamat suatu variabel
dilewatkan ke sebuah fungsi, operator indireksi (*) dapat dipakai di dalam fungsi untuk memodifikasi nilai pada lokasi
tersebut di dalam memori pemanggil.
Program pada Gambar 6.6 dan Gambar 6.7
menyajikan dua versi fungsi yang mengkubikkan suatu integer, kubikDgnNilai dan kubikDgnReferensi. Gambar 6.6 melewatkan variabel angka ke fungsi kubikDgnNilai menggunakan
pemanggilan-dengan-nilai (baris 14). Fungsi kubikDgnNilai memangkat-tiga-kan argumennya dan melewatkan nilai yang
baru tersebut ke main menggunakan
statemen return. Nilai baru tersebut ditugaskan kepada
angka di dalam main (baris 14).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
/* Gambar 6.6: gambar06_06.c
Kubik atas suatu variabel menggunakan pemanggilan-dengan-nilai */
#include <stdio.h>
int kubikDgnNilai( int n ); /* prototipe */
int main( void )
{
int
angka = 5; /* menginisialisasi angka */
printf( "Nilai asli angka adalah %d", angka );
/*
melewatkan angka dengan nilai ke kubikDgnNilai */
angka = kubikDgnNilai( angka
);
printf( "\nNilai baru dari angka adalah %d\n", angka
);
return
0; /* indikasi terminasi sukses */
} /* akhir main */
/* menghitung dan menjadikan nilai balik
kubik atas argumen int */
int kubikDgnNilai( int n )
{
return
n * n * n; /* kubik atas variabel lokal dan menjadikannya
nilai balik */
} /* akhir fungsi kubikDgnNilai */
|
Nilai asli angka adalah 5
Nilai baru dari angka adalah 125
Gambar 6.6 | Kubik atas
suatu variabel menggunakan pemanggilan-dengan-nilai
Gambar 6.7 melewatkan variabel angka menggunakan pemanggilan-dengan-referensi (baris 15), alamat
dari angka dilewatkan kepada fungsi kubikDgnReferensi. Fungsi kubikDgnReferensi mengambil sebuah pointer yang menunjuk ke sebuah int (yaitu nPtr) sebagai
parameter (baris 22). Fungsi tersebut mendereferensi pointer dan mengkubikkan
nilai yang ditunjuk oleh nPtr (baris 24),
kemudian menugaskan hasilnya kepada *nPtr (yang
sebenarnya adalah angka di dalam main), sehingga mengubah nilai angka di dalam main. Gambar 6.8 dan
Gambar 6.9 menganalisa secara grafikal atas program pada Gambar 6.6 dan Gambar
6.7.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
/* Fig. 7.7: fig07_07.c
Kubik atas suatu variabel menggunakan pemanggilan-dengan-referensi */
#include <stdio.h>
void kubikDgnReferensi( int *nPtr ); /*
prototipe */
int main( void )
{
int
angka = 5; /* menginisialisasi angka */
printf( "Nilai asli angka adalah %d", angka );
/*
melewatkan alamat dari angka ke kubikDgnReferensi */
kubikDgnReferensi( &angka
);
printf( "\nNilai baru dari angka adalah %d\n", angka
);
return
0; /* indikasi terminasi sukses */
} /* akhir main */
/* menghitung kubik
dari *nPtr; memodifikasi variabel angka di dalam main */
void kubikDgnReferensi( int *nPtr )
{
*nPtr = *nPtr * *nPtr * *nPtr; /* cube *nPtr */
} /* akhir fungsi kubikDgnReferensi */
|
Nilai asli angka adalah 5
Nilai baru dari angka adalah 125
Gambar 6.7 | Kubik atas
suatu variabel menggunakan pemanggilan-dengan-referensi
Suatu fungsi yang menerima alamat
sebagai argumen harus mendefinisikan parameter pointer untuk menerima alamat
tersebut. Sebagai contoh, pada Gambar 6.7, header fungsi kubikDgnReferensi (baris 22) adalah
void kubikDgnReferensi( int *nPtr )
Header tersebut menspesifikasi bahwa kubikDgnReferensi menerima alamat sebuah variabel
integer sebagai argumen, menyimpan alamat tersebut di dalam nPtr dan tidak menghasilkan nilai balik apapun.
Prototipe fungsi untuk kubikDgnReferensi memuat int * yang diapit oleh sepasang kurung. Sama seperti tipe data yang lain,
adalah hal yang tidak perlu untuk mencantumkan nama pointer di dalam prototipe
fungsi. Nama pointer dicantumkan hanya untuk kepentingan dokumentasi.
Gambar 6.8 | Analisa atas pemanggilan-dengan-nilai
Di dalam header fungsi dan di dalam
prototipe fungsi yang mengharapkan sebuah array subskript-tunggal sebagai
argumen, notasi pointer di dalam daftar parameter fungsi kubikDgnReferensi dapat digunakan. Kompiler tidak bisa
membedakan antara suatu fungsi yang menerima sebuah pointer dengan fungsi yang
menerima sebuah array subskript-tunggal. Hal ini, tentu saja, berarti bahwa
fungsi harus mengetahui kapan ia menerima sebuah array atau kapan ia menerima
sebuah variabel. Ketika kompiler menjumpai sebuah parameter fungsi untuk array
subskript-tunggal dalam format int b[ ], kompiler akan
mengkonversi parameter tersebut menjadi notasi pointer int *b. Dua format tersebut dapat saling
dipertukarkan.
Gambar 6.9 | Analisa atas
pemanggilan-dengan-referensi dengan sebuah argumen pointer
6.5 Penggunaan Kualifier const dengan
Pointer
Kualifier const memampukan Anda untuk menginformasikan kompiler bahwa nilai dari variabel
tertentu tidak boleh dimodifikasi. Kualifier const tidak ada pada awal C, karena ditambahkan ke dalam bahasa C oleh komiter
ANSI C.
Selama bertahun-tahun, banyak sekali
kode warisan yang ditulis dengan C yang tidak menggunakan const karena memang tidak tersedia saat itu. Karena alasan
tersebut, ada kesempatan signifikan dalam memperbaiki kode C lama.
Pada Bab 5, telah dijelaskan bahwa
semua pemanggilan dalam C adalah pemanggilan-dengan-nilai, dimana salinan
argumen di dalam pemanggilan fungsi dilewatkan kepada fungsi. Jika salinan
tersebut dimodifikasi di dalam fungsi, nilai asli di dalam pemanggil tidak akan
berubah. Pada banyak kasus, sebuah nilai yang dilewatkan kepada suatu fungsi
perlu dimodifikasi sehingga fungsi dapat melakukan tugasnya. Namun, dalam
beberapa kesempatan, nilai tidak perlu diubah di dalam fungsi terpanggil,
meskipun yang dimanipulasi hanyalah salinan dari nilai asli.
Perhatikan sebuah fungsi yang mengambil
suatu array subskript-tunggal dan ukurannya sebagai argumen dan yang menampilkan isi array tersebut. Fungsi
semacam itu menjelajah array (menggunakan loop) dan menampilkan setiap elemen
array secara individual. Ukuran array dipakai di dalam tubuh fungsi untuk
menentukan subskript tertinggi array, sehingga loop dapat berhenti ketika
proses untuk menampilkan selesai. Baik ukuran array maupun isinya tidak berubah
di dalam tubuh fungsi.
Jika suatu percobaan dilakukan untuk
memodifikasi sebuah nilai yang dideklarasikan const, maka kompiler mengeluarkan peringatan atau pesan error, bergantung pada
kompiler yang digunakan.
Ada empat cara untuk melewatkan pointer
ke suatu fungsi: pointer tak-konstan ke data tak-konstan, pointer konstan ke
data tak-konstan, pointer tak-konstan ke data konstan, dan pointer konstan ke
data konstan. Tiap kombinasi tersebut mempunyai hak akses yang berbeda. Akan
disajikan beberapa contoh untuk menjelaskan hal ini.
Mengkonversi
String Menjadi Huruf Besar Menggunakan Pointer Tak-Konstan ke Data Tak-Konstan
Level tertinggi atas hak akses data
diberikan oleh pointer tak-konstan ke data tak-konstan. Pada kasus ini, data
dapat dimodifikasi melalui pointer yang didereferensi, dan pointer dapat
dimodifikasi untuk menunjuk ke item data lain. Deklarasi untuk pointer
tak-konstan ke data tak-konstan tidak mencantumkan const. Pointer semacam itu digunakan untuk menerima string sebagai argumen
dalam suatu fungsi yang menggunakan aritmatika pointer untuk memproses (bisa
jadi memodifikasi) setiap karakter di dalam string. Fungsi konversiKeHurufBesar pada Gambar 6.10 mendeklarasikan
parameternya, sebuah pointer tak-konstan ke data tak-konstan yang dinamakan sPtr (char *sPtr), pada baris
21. Fungsi ini memproses array string (yang ditunjuk oleh sPtr) karakter demi karakter menggunakan aritmatika pointer.
Fungsi pustaka standar C islower (yang dipanggil
pada baris 25) menguji isi karakter pada alamat yang ditunjuk oleh sPtr. Jika karakter berada dalam rentang a sampai z, maka islower akan menghaslikan nilai balik true dan fungsi pustaka standar C toupper (baris 26) dipanggil untuk mengkonversi karakter tersebut menjadi huruf
besar; sebaliknya, islower menghasilkan false dan karakter berikutnya di dalam string yang diproses.
Baris 29 memindahkan pointer ke karakter berikutnya di dalam string. Aritmatika
pointer akan didiskusikan lebih detil pada bagian 6.8.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
/* Gambar 6.10: gambar06_10.c
Mengkonversi suatu string menjadi huruf besar menggunakan
pointer tak-konstan ke data tak-konstan */
#include <stdio.h>
#include <ctype.h>
void konversiKeHurufBesar(char *sPtr); /*
prototipe */
int main( void )
{
char
string[] = "pematang siantar"; /* menginisialisasi array
char */
printf( "String sebelum konversi adalah: %s", string
);
konversiKeHurufBesar( string );
printf( "\nString setelah konversi adalah: %s\n",
string );
return
0; /* indikasi terminasi sukses */
} /* akhir main */
/* mengkonversi string menjadi huruf besar
*/
void konversiKeHurufBesar(char *sPtr)
{
while
(*sPtr != '\0') { /* karakter sekarang
bukan '\0' */
if
(islower( *sPtr ))
{ /* jika karakter adalah huruf kecil, */
*sPtr = toupper( *sPtr );
/* dikonversi ke huruf besar */
}
/* akhir if */
++sPtr; /* memindahkan
sPtr ke karakter berikutnya */
} /*
akhir while */
} /* akhir fungsi konversiKeHurufBesar */
|
String sebelum konversi adalah:
pematang siantar
String setelah konversi adalah:
PEMATANG SIANTAR
Gambar 6.10 | Kubik atas
suatu variabel menggunakan pemanggilan-dengan-referensi
Menampilkan
String Karakter Demi Karakter Menggunakan Pointer Tak-Konstan ke Data Konstan
Pointer tak-konstan ke data konstan
dapat dimodifikasi untuk menunjuk ke sembarang item data bertipe sesuai, tetapi
data yang ditunjuk tersebut tidak dapat dimodifikasi. Pointer semacam itu dapat
dipakai untuk menerima argumen array pada suatu fungsi yang memproses setiap
elemen array tanpa memodifikasinya. Sebagai contoh, fungsi tampilKarakter (Gambar 6.11) mendeklarasikan
parameter sPtr bertipe const char * (baris 22). Deklarasi itu dibaca dari kanan ke kiri sebagai “sPtr merupakan sebuah pointer yang menunjuk ke suatu karakter
konstan”. Fungsi ini menggunakan statemen for untuk menampilkan setiap karakter di dalam string sampai dijumpai
karakter null. Setelah tiap karakter ditampilkan, pointer sPtr diinkremen agar menunjuk ke karakter berikutnya di dalam string.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
/* Gambar 6.11: gambar06_11.c
Menampilkan sebuah string karakter demi karakter menggunakan
suatu pointer tak-konstan ke data konstan */
#include <stdio.h>
void tampilKarakter(const char *sPtr);
int main( void )
{
/*
menginisialisasi array char */
char
string[] = "teknik elektro unram";
printf( "String adalah:\n" );
tampilKarakter( string );
printf( "\n" );
return
0; /* indikasi terminasi sukses */
} /* akhir main */
/* sPtr tidak dapat memodifikasi karakter
yang ditunjuk,
sPtr adalah pointer "read-only" */
void tampilKarakter( const char *sPtr )
{
/*
menjelajah keseluruhan string */
for
( ; *sPtr != '\0'; sPtr++ ) { /* tidak ada inisialisasi */
printf( "%c", *sPtr );
} /*
akhir for */
} /* akhir fungsi tampilKarakter */
|
String adalah:
teknik elektro unram
Gambar 6.11 | Menampilkan sebuah string
karakter demi karakter menggunakan suatu pointer tak-konstan ke data konstan
Gambar 6.12 mengilustrasikan percobaan
untuk mengkompilasi suatu fungsi yang menerima sebuah pointer tak-konstan (xPtr) ke data konstan. Fungsi ini mencoba untuk memodifikasi
data yang ditunjuk oleh xPtr pada baris 20,
yang menyebabkan error kompilasi.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
/* Gambar. 6.12: gambar06_12.c
Mencoba untuk memodifikasi data melalui
pointer tak-konstan ke data konstan. */
#include <stdio.h>
void f( const int *xPtr ); /* prototipe */
int main( void )
{
int
y; /* mendefinisikan y */
f( &y ); /* f mencoba
memodifikasi secara ilegal */
return
0; /* indikasi terminasi sukses */
} /* akhir main */
/* xPtr tidak dapat digunakan untuk
memodifikasi
nilai dari variabel yang ditunjuk */
void f(const
int *xPtr)
{
*xPtr = 100; /* error: tidak
dapat memodifikasi objek konstan */
} /* akhir fungsi f */
|
error C2166: l-value specifies const
object
Error executing cl.exe.
gambar06_12.obj - 1 error(s), 0
warning(s)
Gambar 6.12 | Mencoba untuk
memodifikasi data melalui pointer tak-konstan ke data konstan
Seperti yang Anda ketahui, array
merupakan sekumpulan data bertipe sama. Pada Bab 9, akan didiskusikan
sekumpulan data berbagai tipe yang dinamakan dengan struktur. Ketika suatu
fungsi dipanggil dengan array sebagai argumen, array tersebut secara otomatis
dilewatkan dengan referensi kepada fungsi. Namun, struktur selalu dilewatkan
dengan nilai, dimana salinan dari keseluruhan struktur dilewatkan. Hal ini
memerlukan biaya komputasi yang cukup signifikan akibat penyalinan setiap data
di dalam struktur dan penyimpannya pada tumpukan pemanggilan fungsi. Ketika
data struktur dilewatkan kepada suatu fungsi, Anda bisa memakai pointer ke data
konstan untuk melakukan pemanggilan-dengan-referensi. Ketika pointer ke
struktur dilewatkan, hanya salinan dari alamat struktur yang perlu disimpan.
Pada mesin dengan alamat 4-byte, salinan memori 4-byte yang perlu dibuat,
daripada harus membuat salinan atas ratusan atau bahkan ribuan byte struktur
pada kasus pelewatan struktur dengan nilai.
Mencoba
Memodifikasi Pointer Konstan ke Data Tak-Konstan
Sebuah pointer konstan ke data
tak-konstan selalu menunjuk ke lokasi memori yang sama, dan data pada lokasi
tersebut dapat dimodifikasi melalui pointer itu. Hal ini merupakan kasus
default pada suatu nama array. Nama array merupakan sebuah pointer konstan yang
menunjuk ke awal array. Semua data di dalam array dapat diakses dan diubah
menggunakan nama array (lewat subskript array). Pointer konstan ke data
tak-konstan dapat dipakai untuk menerima array sebagai argumen pada suatu
fungsi yang mengakses elemen array menggunakan notasi subskript array. Pointer
yang dideklarasikan const harus
diinisialisasi ketika didefinisikan (jika pointer merupakan parameter fungsi,
maka diinisialisasi dengan sebuah pointer yang dilewatkan kepada fungsi
tersebut). Gambar 6.13 mencoba untuk memodifikasi pointer konstan. Pointer ptr didefinisikan pada baris 12
menjadi bertipe int * const. Definisi
tersebut dibaca dari kanan ke kiri sebagai “ptr merupakan sebuah pointer konstan yang menunjuk ke suatu integer”. Pointer
tersebut diinisialisasi pada baris 12 dengan alamat dari variabel integer x. Program mencoba menugaskan alamat y kepada ptr (baris 15),
tetapi kompiler membangkitkan pesan error.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
/* Gambar 6.13: gambar06_13.c
Mencoba untuk memodifikasi pointer konstan ke data tak-konstan */
#include <stdio.h>
int main( void )
{
int
x; /* mendefinisikan x */
int
y; /* mendefinisikan y */
/*
ptr adalah pointer konstan ke suatu integer yang dapat dimodifikasi
lewat ptr, tetapi ptr selalu menunjuk ke lokasi memori yang sama */
int
* const ptr = &x;
*ptr
= 7; /* dibolehkan: *ptr tidak const */
ptr
= &y; /* error: ptr adalah const; tidak dapat menugaskan alamat baru */
return 0; /* indikasi terminasi sukses */
} /* akhir main */
|
gambar06_13.c(15) : error C2166:
l-value specifies const object
Gambar 6.13 | Mencoba untuk
memodifikasi pointer konstan ke data tak-konstan
Mencoba
Memodifikasi Pointer Konstan ke Data Konstan
Hak akses yang paling minim adalah
pointer konstan ke data konstan. Pointer semacam itu selalu menunjuk ke lokasi
memori yang sama, dan data pada lokasi memori tersebut tidak dapat
dimodifikasi. Hal inilah yang terjadi
ketika sebuah array dilewatkan ke suatu fungsi yang hanya “melihat” array
menggunakan notasi subskript dan tidak memodifikasi array tersebut. Gambar 6.14
mendefinisikan variabel pointer ptr (baris 13)
menjadi bertipe const int * const, yang dibaca dari kanan ke kiri sebagai “ptr merupakan sebuah pointer yang menunjuk ke suatu konstanta integer”. Kode
pada Gambar 6.14 mengakibatkan pesan error yang dibangkitkan ketika percobaan
untuk memodifikasi data yang ditunjuk oleh ptr (baris 16) dilakukan dan percobaan untuk memodifikasi alamat yang disimpan
di dalam variabel pointer (baris 17) dilakukan.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
/* Gambar 6.14: gambar06_14.c
Mencoba untuk memodifikasi pointer konstan ke data konstan. */
#include <stdio.h>
int main( void )
{
int
x = 5; /* menginisialisasi x */
int
y; /* mendefinisikan y */
/*
ptr merupakan suatu pointer konstan ke integer konstan. ptr selalu
menunjuk ke lokasi yang sama; integer pada lokasi tersebut
tidak dapat dimodifikasi */
const int *const ptr = &x;
printf( "%d\n", *ptr );
*ptr
= 7; /* error: *ptr adalah const; tidak dapat menugaskan nilai baru */
ptr
= &y; /* error: ptr adalah const; tidak dapat menugaskan alamat baru */
return
0; /* indikasi terminasi sukses */
} /* akhir main */
|
gambar06_14.c(16) : error C2166:
l-value specifies const object
gambar06_14.c(17) : error C2166:
l-value specifies const object
gambar06_14.obj - 2 error(s), 0
warning(s)
Gambar 6.14 | Mencoba untuk
memodifikasi pointer konstan ke data konstan
6.6 Pengurutan Bubble Menggunakan
Pemanggilan-dengan-Referensi
Akan diperbaiki program pengurutan
bubble pada Gambar 5.15 dengan memanfaatkan dua fungsi, pengurutanBubble dan tukar. Fungsi pengurutanBubble mengurutkan array. Fungsi ini memanggil fungsi tukar (baris 51) untuk menukar elemen arrat array[j] dengan array[j+1]. Ingat bahwa C memaksa penyembunyian informasi antar fungsi, sehingga tukar tidak dapat mengakses elemen-elemen array secara individual
di dalam pengurutanBubble. Karena pengurutanBubble menginginkan agar tukar memiliki akses terhadap elemen array yang akan ditukar, pengurutanBubble melewatkan setiap elemen tersebut
menggunakan pemanggilan-dengan-referensi kepada tukar, dimana alamat setiap elemen array dilewatkan secara eksplisit. Meskipun
keseluruhan array secara otomatis dilewatkan dengan referensi, elemen array
secara individual adalah skalar dan dilewatkan dengan nilai. Oleh karena itu, pengurutanBubble menggunakan operator alamat (&) pada tiap elemen array di dalam pemanggilan tukar (baris 51) untuk melakukan pemanggilan-dengan-referensi
sebagai berikut
tukar( &array[ j ], &array[ j + 1 ] );
Fungsi tukar menerima &array[j] di dalam
variabel pointer elemen1Ptr (baris 59).
Meskipun tukar, karena penyembunyian informasi, tidak
diijinkan untuk mengetahui nama array[j], tukar dapat menggunakan *elemen1Ptr sebagai sinonim bari array[j], ketika tukar mereferensi *elemen1Ptr, yang terjadi
sebenarnya adalah tukar mereferensi array[j] di dalam pengurutanBubble. Sama halnya, ketika tukar mereferensi *elemen2Ptr, yang terjadi sebenarnya adalah ia
mereferensi array[j+1] di dalam pengurutanBubble. Meskipun tukar tidak diijinkan
melakukan
tampung = array[ j ];
array[ j ] = array[ j + 1 ];
array[ j + 1 ] = tampung;
tetapi efek yang sama dapat dicapai
dengan menuliskan baris 61 sampai 63
int tampung = *elemen1Ptr;
*elemen1Ptr = *elemen2Ptr;
*elemen2Ptr = tampung;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
/* Gambar 6.15: gambar06_15.c
Program ini menempatkan nilai ke array, mengurutkan nilai-nilai tersebut
menjadi urutan menaik, dan menampilkan array yang dihasilkan. */
#include <stdio.h>
#define UKURAN 10
void pengurutanBubble( int *
const array, const int ukuran ); /*
prototipe */
int main( void )
{
/* menginisialisasi array a */
int a[ UKURAN ] = { 2, 6, 4, 8, 10,
12, 89, 68, 45, 37 };
int i; /* kounter */
printf( "Item data dalam urutan asli\n"
);
/* loop menjelajah array a */
for ( i = 0; i < UKURAN; i++ ) {
printf( "%4d", a[ i ] );
} /* akhir for */
pengurutanBubble( a, UKURAN ); /*
mengurutkan array */
printf( "\nItem data dalam urutan
menaik\n" );
/*
loop menjelajah array a */
for (
i = 0; i < UKURAN; i++ ) {
printf( "%4d", a[ i ] );
} /*
akhir for */
printf( "\n" );
return 0; /* indikasi terminasi sukses */
} /* akhir main */
/* mengurutkan
sebuah array integer menggunakan algoritma pengurutan bubble */
void pengurutanBubble( int * const array, const
int ukuran )
{
void tukar( int
*elemen1Ptr, int *elemen2Ptr ); /* prototipe */
int
pass; /* kounter pass */
int
j; /* kounter perbandingan */
/*
loop untuk mengendalikan pass */
for
( pass = 0; pass < ukuran - 1; pass++ ) {
/*
loop untuk mengendalikan perbandingan tiap pass */
for
( j = 0; j < ukuran - 1; j++ ) {
/* tukar elemen jika tidak terurut */
if
( array[ j ] > array[ j + 1 ] ) {
tukar(
&array[ j ], &array[ j + 1 ] );
}
/* akhir if */
}
/* akhir for dalam */
} /*
akhir for luar */
} /* akhir fungsi pengurutanBubble */
/* menukar
nilai-nilai pada lokasi memori yang ditunjuk oleh elemen1Ptr dan
elemen2Ptr */
void tukar( int *elemen1Ptr, int
*elemen2Ptr )
{
int
tampung = *elemen1Ptr;
*elemen1Ptr = *elemen2Ptr;
*elemen2Ptr = tampung;
} /* akhir fungsi tukar */
|
Item data dalam urutan asli
2 6 4
8 10 12
89 68 45 37
Item data dalam urutan menaik
2 4 6
8 10 12
37 45 68 89
Gambar 6.15 | Mencoba untuk
memodifikasi pointer konstan ke data konstan
Beberapa fitur fungsi pengurutanBubble perlu diperhatikan. Header fungsi
(baris 27) mendeklarasikan array sebagai int * const array, menggantikan int array[], untuk mengindikasikan bahwa pengurutanBubble menerima array subskript-tunggal
sebagai argumen. Parameter ukuran dideklarasikan const untuk memaksa prinsip hak akses minim. Meskipun parameter ukuran menerima salinan dari sebuah nilai di
dalam main, dan pemodifikasian salinan tersebut
tidak mengubah nilai di dalam main, pengurutanBubble tidak mengubah ukuran untuk melakukan
tugasnya. Ukuran array tetap tidak berubah selama eksekusi atas fungsi pengurutanBubble. Oleh karena itu, ukuran dideklarasikan const untuk memastikan bahwa ia tidak dimodifikasi. Jika ukuran
array dimodifikasi selama proses pengurutan, maka algoritma pengurutan tidak akan
berjalan dengan benar.
Prototipe untuk fungsi tukar (baris 39) dicantumkan di dalam tubuh fungsi pengurutanBubble karena pengurutanBubble merupakan satu-satunya fungsi yang memanggil tukar. Penempatan prototipe tukar di dalam pengurutanBubble membatasi pemanggilan terhadap tukar hanya dari pengurutanBubble. Fungsi lain yang mencoba memanggil tukar tidak memiliki akses terhadap prototipe fungsi tukar, jadi kompiler akan menghasilkan prototipe asumsi (tipe
nilai balik dan tipe parameter adalah int) secara otomatis. Hal ini biasanya menghasilkan pesan error karena header
fungsi tidak sesuai dengan pemanggilan yang dilakukan.
Fungsi pengurutanBubble menerima ukuran array sebagai parameter (baris 37). Fungsi
tersebut harus mengetahui ukuran array untuk mengurutkan array. Ketika sebuah
array dilewatkan kepada suatu fungsi, alamat memori dari elemen pertama array
diterima oleh fungsi tersebut. Alamat, tentu saja, tidak memberikan informasi
tentang jumlah elemen di dalam array. Oleh karena itu, Anda harus melewatkan
ukuran array kepada fungsi. [Perhatian: Praktek pemrograman yang umum dijumpai
adalah melewatkan sebuah pointer yang menunjuk ke awal array dan sebuah pointer
yang menunjuk ke lokasi di luar batas array. Perbedaan dua pointer tersebut
sama dengan panjang array].
6.7 Operator sizeof
C menyediakan operator
unary spesial sizeof untuk menentukan ukuran dalam byte dari suatu array (atau dari
sembarang tipe data) selama kompilasi program berlangsung. Ketika diterapkan
terhadap nama suatu array seperti pada Gambar 6.16 (baris 14), operator sizeof
menghasilkan nilai balik berupa jumlah byte di dalam array (sebagai integer).
Variabel bertipe float normalnya
disimpan dalam 4 byte memori, dan array didefinisikan untuk mempunyai 20 elemen. Oleh karena itu,
terdapat total 80 byte di dalam array.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
/* Gambar 6.16: gambar06_16.c
Penerapan sizeof terhadap nama array menghasilkan
nilai balik jumlah byte di dalam array. */
#include <stdio.h>
size_t getUkuran(float *ptr); /* prototipe */
int main( void )
{
float
array[ 20 ]; /* menciptakan array */
printf( "Jumlah byte di dalam array adalah %d"
"\nJumlah byte yang dihasilkan getUkuran adalah %d\n",
sizeof( array ), getUkuran( array ) );
return
0; /* indakasi terminasi sukses */
} /*
akhir main */
/*menghasilkan ukuran dari ptr */
size_t
getUkuran(float *ptr)
{
return sizeof( ptr );
} /* akhir fungsi getUkuran */
|
Jumlah byte di dalam array adalah 80
Jumlah byte yang dihasilkan getUkuran
adalah 4
Gambar 6.16 | Penerapan sizeof terhadap nama array
menghasilkan nilai balik jumlah byte di dalam array
Jumlah elemen di dalam suatu array juga dapat ditentukan dengan sizeof. Sebagai contoh, perhatikan definisi array berikut:
double riil[ 22 ];
Variabel bertipe double biasanya disimpan di dalam memori 8
byte. Jadi, array riil memuat total
176 byte. Untuk menentukan jumlah elemen di dalam array, ekspresi berikut dapat
digunakan:
sizeof( real ) / sizeof( riil[ 0 ] )
Ekspresi di atas menentukan jumlah byte
di dalam array riil dan membagi
nilai tersebut dengan jumlah byte yang digunakan di dalam memori untuk
menyimpan elemen pertama array riil (suatu nilai double).
Fungsi getSize menghasilkan nilai balik bertipe size_t. Tipe data size_t didefinisikan
oleh standar C sebagai tipe integral (unsigned atau unsigned long) dari nilai
yang dijadikan nilai balik oleh operator sizeof. Tipe size_t didefinisikan
di dalam header <stddef.h> (yang juga dicantumkan di dalam beberapa header lain seperti <stdio.h>). [Perhatian: Jika Anda mencoba untuk
mengkompilasi Gambar 6.16 dan mendapatkan error, Anda bisa menyertakan <stddef.h> di dalam program Anda]. Gambar 6.17
menghitung jumlah byte yang digunakan untuk menyimpan tiap tipe data. Hasilnya
bisa berbeda tergantung dari komputer yang digunakan.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
/* Gambar 6.17: gambar06_17.c
Mendemonstrasikan operator sizeof */
#include <stdio.h>
int main( void )
{
char
c;
short
s;
int
i;
long
l;
float
f;
double
d;
long
double ld;
int
array[ 20 ]; /* menciptakan array int 20-elemen */
int
*ptr = array; /* menciptakan pointer ke array */
printf( " sizeof c = %d\tsizeof(char) = %d"
"\n sizeof s = %d\tsizeof(short) = %d"
"\n sizeof i = %d\tsizeof(int) = %d"
"\n sizeof l = %d\tsizeof(long) = %d"
"\n sizeof f = %d\tsizeof(float) = %d"
"\n sizeof d = %d\tsizeof(double) = %d"
"\n sizeof ld = %d\tsizeof(long double) = %d"
"\n sizeof array = %d"
"\n sizeof ptr = %d\n",
sizeof c, sizeof( char ), sizeof s, sizeof( short ), sizeof i,
sizeof( int ), sizeof l, sizeof( long ), sizeof f,
sizeof( float ), sizeof d, sizeof( double ), sizeof ld,
sizeof( long double ), sizeof array, sizeof ptr );
return
0; /* indikasi terminasi sukses */
} /* akhir main */
|
sizeof c = 1 sizeof(char)
= 1
sizeof s = 2 sizeof(short)
= 2
sizeof i = 4 sizeof(int)
= 4
sizeof l = 4 sizeof(long)
= 4
sizeof f = 4 sizeof(float)
= 4
sizeof d = 8 sizeof(double)
= 8
sizeof ld = 8 sizeof(long
double) = 8
sizeof array = 80
sizeof ptr = 4
Gambar 6.17 | Penggunaan operator sizeof untuk menentukan ukuran tipe
data standar
Operator sizeof dapat diterapkan pada sembarang nama variabel, tipe, atau nilai (termasuk
nilai dari suatu ekspresi). Ketika diterapkan pada nama variabel (yang bukan
nama array) atau pada konstanta, jumlah byte yang digunakan untuk menyimpan
tipe variabel atau konstanta yang dijadikan nilai balik. Sepasang kurung yang
digunakan sizeof diperlukan jika nama tipe terdiri-dari
dua kata sebagai operand (misalnya long double atau unsigned short). Sepasang kurung tidak diperlukan
bila operand hanya memuat satu kata.
6.8 Ekspresi Pointer dan Aritmatika
Pointer
Pointer merupakan operand yang sah
untuk digunakan di dalam ekspresi aritmatika, ekspresi penugasan, dan ekspresi
perbandingan. Namun, tidak semua operator yang biasanya digunakan di dalam
ketiga jenis ekspresi tersebut bisa diterapkan pada variabel pointer. Bagian
ini akan menjelaskan operator-operator yang dapat memiliki pointer sebagai
operandnya, dan bagaimana operator-operator tersebut digunakan.
Himpunan terbatas atas operasi
aritmatika dapat diterapkan pada pointer. Sebuah pointer dapat diinkremen (++) atau didekremen (--), sebuah integer dapat ditambahkan pada pointer (+ atau +=), sebuah
integer dapat dikurangi dari suatu pointer (- atau -=), dan suatu
pointer dapat dikurangi dari pointer lainnya.
Gambar 6.18 | Array v dan sebuah variabel pointer vPtr yang menunjuk
ke v
Diasumsikan bahwa suatu array int v[5] telah didefinisikan dan elemen
pertamanya berada pada lokasi 3000 di dalam memori. Diasumsikan pula bahwa
pointer vPtr telah diinisialisasi untuk menunjuk ke
v[0], yaitu nilai dari vPtr adalah 3000. Gambar
6.18 mengilustrasikan situasi ini untuk sebuah mesin dengan integer 4-byte.
Variabel vPtr dapat diinisialisasi untuk menunjuk ke
array v dengan salah satu statemen
vPtr = v;
vPtr = &v[ 0 ];
Dalam aritmatika konvensional, 300 + 2
akan menghasilkan nilai 3002. Hal ini tidak berlaku pada aritmatika pointer.
Ketika sebuah integer ditambahkan pada atau dikurangi dari suatu pointer,
pointer tidak diinkremen atau didekremen sebesar integer tersebut, melainkan
sebesar integer tersebut dikalikan dengan ukuran objek yang ditunjuk pointer
itu. Jumlah byter bergantung pada tipe data objek. Sebagai contoh, statemen
vPtr += 2;
akan menghasilkan 3008 (3000 + 2 * 4), bila
diasumsikan bahwa sebuah integer disimpan di dalam memori 4-byte. Di dalam
array v, vPtr sekarang akan menunjuk ke v[2] (Gambar 6.19).
Jika integer tersebut disimpan di dalam memori 2-byte, maka perhitungan
tersebut akan menghasilkan 3004 (3000 + 2 * 2). Jika array tersebut bertipe berbeda
(bukan integer), maka statemen tersebut akan menginkremen sejauh dua kali
jumlah byter yang diperlukan untuk menyimpan sebuah objek bertipe data
tersebut. Ketika melakukan aritmatika pointer terhadap suatu array karakter,
hasilnya akan konsisten dengan aritmatika biasa, karena setiap karakter
masing-masing adalah 1-byte.
Gambar 6.19 | Pointer vPtr setelah aritmatika pointer
Jika vPtr diinkremen ke 3016, yang menunjuk ke v[4], statemen
vPtr -= 4;
akan menetapkan vPtr kembali ke 3000, awal array. Jika sebuah pointer ingin
diinkremen atau didekremen sejauh satu, maka operator inkremen (++) dan dekremen (--) dapat
digunakan. Salah satu statemen
++vPtr;
vPtr++;
menginkremen pointer agar menunjuk ke
lokasi berikutnya di dalam array. Salah satu statemen
--vPtr;
vPtr--;
mendekremen pointer agar menunjuk ke
elemen sebelumnya di dalam array. Variabel pointer dapat dikurangi satu sama
lain. Sebagai contoh, jika vPtr memuat lokasi
3000, dan v2Ptr memuat alamat 3008, statemen
x = v2Ptr - vPtr;
akan menugaskan kepada x jumlah elemen array dari vPtr sampai v2Ptr, pada kasus ini
2 (bukan 8). Aritmatika pointer tidak berguna jika tidak diterapkan pada array.
Anda tidak bisa mengasumsikan bahwa dua variabel bertipe sama disimpan di dalam
memori yang bertetangga jika kedua variabel tersebut bukan elemen suatu array.
Suatu pointer dapat ditugaskan kepada
pointer lain bila keduanya memiliki tipe yang sama. Pengecualian terhadap
aturan ini adalah pointer ke void (yaitu, void *), yang merupakan sebuah pointer
generik yang dapat merepresentasikan sembarang tipe pointer. Semua tipe pointer
dapat ditugaskan kepada suatu pointer ke void, dan sebuah pointer ke void dapat
ditugaskan ke suatu pointer sembarang tipe. Pada kedua kasus, operasi cast tidak dibutuhkan.
Suatu pointer ke void tidak dapat didereferensi. Perhatikan ini: Kompiler
mengetahui bahwa suatu pointer ke int menunjuk ke
memori 4-byte pada suatu mesin dengan integer 4-byte, tetapi sebuah pointer ke void hanya memuat lokasi memori untuk tipe data yang tidak
diketahui, dimana jumlah byte persis yang ditunjuk pointer tidak diketahui oleh
pointer. Kompiler harus mengetahui tipe data untuk menentukan jumlah byte yang
akan didereferensi.
Pointer dapat dibandingkan menggunakan
operator ekualitas dan operator relasional, tetapi perbandingan semacam itu
tidak berguna kecuali jika pointer menunjuk ke elemen-elemen array yang sama.
6.9 Relasi Antara Pointer dan Array
Array dan pointer berelasi secara intim
di dalam C dan seringkali saling dipertukarkan. Suatu nama array dapat dianggap
sebagai sebuah pointer konstan. Pointer dapat digunakan untuk melakukan
berbagai operasi yang melibatkan array.
Diasumsikan bahwa array integer b[5] dan variabel pointer integer bPtr telah didefinisikan. Karena nama array (tanpa subskript) adalah sebuah
pointer yang menunjuk ke elemen pertama array, Anda dapat menetapkan bPtr menunjuk ke alamat elemen pertama di dalam array b dengan statemen
bPtr = b;
Statemen ini ekivalen dengan mengambil
alamat dari elemen pertama array sebagai berikut:
bPtr = &b[ 0 ];
Elemen array b[3] dapat direferensi dengan ekspresi pointer
*( bPtr + 3 )
Di dalam ekspresi di atas, 3 adalah
offset bagi pointer. Ketika pointer menunjuk ke awal suatu array, offset
mengindikasikan elemen array yang mana yang harus direferensi, dan nilai offset
indentik dengan subskript array. Notasi tersebut dikatakan sebagai notasi
pointer/offset. Sepasang kurung diperlukan karena derajat keutamaan * lebih tinggi daripada derajat keutamaan +. Tanpa kurung, ekspresi tersebut akan menambahkan 3 pada
nilai dari ekspresi *bPtr (yaitu, 3 akan
ditambahkan pada b[0], bila
diasumsikan bPtr menunjuk ke
awal array). Sama seperti elemen array yang dapat direferensi dengan suatu
ekspresi pointer, alamat
&b[ 3 ]
dapat ditulis dengan ekspresi pointer
bPtr + 3
Array sendiri dapat diperlakukan
sebagai sebuah pointer dan digunakan di dalam aritmatika pointer. Sebagai
contoh, ekspresi
*( b + 3 )
juga menunjuk ke elemen array b[3]. Secara umum, semua ekspresi array yang disubskript dapat
ditulis dengan pointer dan offset. Pada kasus ini, notasi pointer/offset
digunakan dengan nama array sebagai pointer. Statemen tersebut tidak
memodifikasi nama array; b masih menunjuk
ke elemen pertama di dalam array.
Pointer dapat disubskript persis
seperti array. Sebagai contoh, jik abPtr memiliki nilai b, maka ekspresi
bPtr[ 1 ]
menunjuk ke elemen array b[1]. Notasi ini dikenal dengan notasi pointer/subskript.
Ingat bahwa nama array secara esensi
adalah sebuah pointer konstan; ia selalu menunjuk ke awal array. Jadi, ekspresi
b += 3
tidak bisa dilakukan karena mencoba
memodifikasi nilai dari nama array dengan aritmatika pointer.
Gambar 6.20 menggunakan empat metode
yang didiskusikan dalam menunjuk elemen array, yaitu pensubskriptan array,
pointer/offset dengan nama array sebagai pointer, pensubskriptan pointer, dan
pointer/offset dengan pointer.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
/* Gambar 6.20: gambar06_20.c
Menggunakan subskript dan pointer dengan array */
#include <stdio.h>
int main( void )
{
int
b[] = { 10, 20, 30, 40 }; /* menginisialisasi array b */
int
*bPtr = b; /* menetapkan bPtr menunjuk ke array b */
int
i; /* kounter */
int
offset; /* kounter */
/*
menampilkan array b menggunakan notasi pensubskriptan array */
printf( "Array b ditampilkan dengan:\nNotasi subskript
array\n" );
/*
loop menjelajah array b */
for
( i = 0; i < 4; i++ ) {
printf( "b[ %d ] = %d\n", i, b[ i ]);
} /*
akhir for */
/*
menampilkan array b menggunakan nama array dan notasi pointer/offset */
printf( "\nNotasi pointer/offset dimana\n"
"pointer adalah nama
array\n" );
/*
loop menjelajah array b */
for
( offset = 0; offset < 4; offset++ ) {
printf( "*( b + %d ) = %d\n", offset, *(b + offset) );
} /*
akhir for */
/*
menampilkan array b menggunakan bPtr dan notasi subskript array */
printf( "\nNotasi pensubskriptan pointer\n" );
/*
loop menjelajah array b */
for
( i = 0; i < 4; i++ ) {
printf( "bPtr[ %d ] = %d\n", i, bPtr[ i ]);
} /*
akhir for */
/*
menampilkan array b menggunakan bPtr dan notasi pointer/offset */
printf( "\nNotasi pointer/offset\n" );
/*
loop menjelajah array b */
for
( offset = 0; offset < 4; offset++ ) {
printf( "*( bPtr + %d ) = %d\n", offset, *(bPtr +
offset) );
} /* end for */
return
0; /* indikasi terminasi sukses */
} /* akhir main */
|
Array b ditampilkan denga:
Notasi subskript array
b[ 0 ] = 10
b[ 1 ] = 20
b[ 2 ] = 30
b[ 3 ] = 40
Notasi pointer/offset dimana
pointer adalah nama array
*( b + 0 ) = 10
*( b + 1 ) = 20
*( b + 2 ) = 30
*( b + 3 ) = 40
Notasi pensubskriptan pointer
bPtr[ 0 ] = 10
bPtr[ 1 ] = 20
bPtr[ 2 ] = 30
bPtr[ 3 ] = 40
Notasi pointer/offset
*( bPtr + 0 ) = 10
*( bPtr + 1 ) = 20
*( bPtr + 2 ) = 30
*( bPtr + 3 ) = 40
Gambar 6.20 | Penggunaan empat metode
pereferensian elemen array
Untuk mengilustrasikan keintiman array
dan pointer, akan disajikan dua contoh fungsi penyalin string, salin1 dan salin2, pada Gambar 6.21. Kedua fungsi menyalin sebuah string ke dalam suatu
array karakter. Bila dibandingkan prototipe fungsi salin1 dan salin2, fungsi tampak identik. Keduanya
menyelesaikan tugas yang sama, tetapi diimplementasikan secara berbeda.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
/* Gambar 6.21: gambar06_21.c
Menyalin string dengan notasi array dan notasi pointer. */
#include <stdio.h>
void salin1( char * const s1, const
char * const s2 ); /* prototipe */
void salin2( char *s1, const char *s2 );
/* prototipe */
int main( void )
{
char string1[ 10 ]; /* menciptakan
array string1 */
char *string2 = "Pantai";
/* menciptakan pointer ke string */
char string3[ 10 ]; /* menciptakan
array string3 */
char string4[] = "Senggigi
Gili"; /* menciptakan pointer ke string */
salin1( string1, string2 );
printf( "string1 = %s\n",
string1 );
salin2( string3, string4 );
printf( "string3 = %s\n", string3
);
return
0; /* indikasi terminasi sukses */
} /* akhir main */
/* menyalin s2 kepada s1 menggunakan notasi
array */
void salin1( char * const s1, const char *
const s2 )
{
int
i; /* kounter */
/*
loop menjelajah string */
for
( i = 0; ( s1[ i ] = s2[ i ] ) != '\0'; i++ ) {
;
/* tidak melakukan aapapun di tubuh */
} /*
akhir for */
} /* akhir fungsi salin1 */
/* salin s2 kepada s1 menggunakan notasi
pointer */
void salin2( char *s1, const char *s2 )
{
/*
loop menjelajah string */
for
( ; ( *s1 = *s2 ) != '\0'; s1++, s2++ ) {
;
/* tidak melakukan apapun */
} /*
akhir for */
} /* akhir fungsi salin2 */
|
string1 = Pantai
string3 = Senggigi Gili
Gambar 6.21 | Penyalinan sebuah string
menggunakan notasi pointer dan notasi array
Fungsi salin1 menggunakan notasi subskript array untuk menyalin string di dalam s2 ke dalam array karakter s1. Fungsi mendefinisikan variabel kounter i sebagai subskript array. Header statemen for (baris 29) melakukan seluruh operasi penyalinan, dimana tubuh statemen for adalah statemen kosong. Header tersebut menspesifikasi
bahwa i diinisialisasi dengan nol dan
diinkremen sebesar satu setiap kali iterasi loop berjalan. Ekspresi s1[i] = s2[i] menyalin satu karakter dari s2 ke dalam s1. Ketika
karakter null ditemukan di dalam s2, karakter
tersebut ditugaskan pada s1, dan nilai dari
penugasan menjadi nilai yang ditugaskan ke operand kiri (s1). Loop berhenti karena nilai integer dari karakter null
adalah nol (false).
Fungsi salin2 menggunakan pointer dan aritmatika pointer untuk menyalin string di dalam
s2 ke dalam array karakter s1. Lagi, header statemen for (baris 38) melakukan seluruh penyalinan. Header tidak mencantumkan
inisialisasi variabel. Seperti pada fungsi salin1, ekspresi (*s1 = *s2) melakukan
operasi penyalinan. Pointer s2 didereferensi,
dan karakter yang dihasilkan ditugaskan kepada pointer yang didereferensi *s1. Setelah penugasan di dalam kondisi, pointer diinkremen
agar menunjuk ke elemen berikutnya dari array s1 dan karakter berikutnya pada string s2. Ketika karakter null ditemukan di dalam s2, karakter tersebut ditugaskan kepada pointer terdereferensi s1 dan loop berhenti.
Argumen pertama pada salin1 dan salin2 harus berupa suatu array dengan panjang cukup untuk menampung string pada
argumen kedua. Jika tidak, error akan terjadi jika dicoba untuk menulis ke
suatu lokasi memori yang tidak menjadi bagian array. Di samping itu, parameter
kedua dari setiap fungsi dideklarasikan sebagai const char * (sebuah string konstan). Pada kedua fungsi, argumen kedua disalin ke
argumen pertama.
6.10 Array Pointer
Array bisa memuat pointer. Kegunaan
umum dari array pointer adalah untuk membentuk array string. Setiap entri di
dalam array tersebut adalah sebuah string, tetapi di dalam C, string secara
esensi merupakan sebuah pointer yang menunjuk ke karakter pertama. Jadi, setiap
entri di dalam suatu array string adalah sebuah pointer yang menunjuk ke
karakter pertama dari suatu string. Perhatikan definisi array string suit
berikut:
const char *suit[ 4 ] = { "Hearts", "Diamonds", "Clubs", "Spades" };
suit[4] mengindikasikan suatu array 4-elemen.
Deklarasi char * mengindikasikan bahwa setiap elemen
array suit bertipe “pointer ke char”. Kualifier const mengindikasikan bahwa string yang
ditunjuk oleh setiap pointer elemen tidak dapat dimodifikasi. Empat nilai yang
ditempatkan di dalam array adalah “Hearts”, “Diamonds”, “Clubs”, dan “Spades”. Setiap elemen
disimpan di dalam memori sebagai string yang diakhir dengan karakter null.
Keempat string tersebut memiliki panjang 7, 9, 6, dan 7 karakter. Meskipun
tampak bahwa keempat string tersebut ditempatkan di dalam array suit, sebenarnya hanya empat pointer saja yang disimpan di dalam
array tersebut (Gambar 6.22). Setiap pointer menunjuk ke elemen pertama dari
string. Jadi, meskipun array suit berukuran tetap, tetapi ia memberikan akses
terhadap array karakter dengan panjang tidak terbatas. Fleksibilitas ini
merupakan satu contoh kapabilitas penstrukturan data di dalam C.
Gambar 6.22 Representasi grafikal atas array suit
6.11 Pointer Ke Fungsi
Sebuah pointer ke fungsi memuat alamat
dari suatu fungsi di dalam memori. Pada Bab 5, Anda telah melihat bahwa nama
array sebenarnya merupakan alamat di dalam memori dari elemen pertama array.
Sama halnya, nama fungsi sebenarnya merupakan alamat awal di dalam memori dari
kode yang melakukan tugas fungsi. Pointer ke fungsi dapat dilewatkan ke fungsi,
dijadikan nilai balik, disimpan di dalam array, dan ditugaskan pada fungsi
lain.
Untuk mengilustrasikan kegunaan pointer
ke fungsi, Gambar 6.26 menyajikan versi termodifikasi atas pengurutan bubble
pada Gambar 6.15. Versi baru ini memuat main dan fungsi bubble, tukar, menaik, dan menurun. Fungsi bubble menerima sebuah pointer ke fungsi sebagai argumen. Program meminta
pengguna untuk memilih apakah array harus diurutkan dengan tatanan menaik atau
tatanan menurun. Jika pengguna memasukkan 1, maka sebuah pointer ke fungsi menaik dilewatkan kepada fungsi bubble, menyebabkan array diurutkan dengan
tatanan menaik. Jika pengguna memasukkan 2, maka sebuah pointer ke fungsi menurun dilewatkan kepada fungsi bubble, menyebabkan array diurutkan dengan
tatanan menurun. Contoh keluaran program ditampilkan pada Gambar 6.27.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
|
/* Gambar 6.26: gambar06_26.c
Program pengurutan multiguna menggunakan fungsi pointer */
#include <stdio.h>
#define UKURAN 10
/* prototipe */
void bubble( int kerja[], const int
ukuran, int (*banding)( int a, int b ));
int menaik( int a, int b );
int menurun( int a, int b );
int main( void )
{
int
urutan; /* 1 untuk menaik atau 2 untuk menurun */
int
kounter; /* kounter */
/*
menginisialisasi array a */
int
a[ UKURAN ] = { 2, 6, 4, 8, 10, 12, 89, 68, 45, 37 };
printf( "Masukkan 1 untuk mengurutkan secara menaik,\n"
"Masukkan 2 untuk mengurutkan secara
menurun: " );
scanf( "%d", &urutan );
printf( "\nItem data dalam urutan asli\n" );
/*
menampilkan array asli */
for
( kounter = 0; kounter < UKURAN; kounter++ ) {
printf( "%5d", a[ kounter ] );
} /*
akhir for */
/*
mengurutkan array secara menaik; melewatkan fungsi menaik sebagai
argumen untuk menspesifikasi tatanan pengurutan */
if
( urutan == 1 ) {
bubble( a, UKURAN, menaik );
printf( "\nItem data dalam urutan menaik\n" );
} /*
akhir if */
else
{ /* melewatkan fungsi menurun */
bubble( a, UKURAN, menurun );
printf( "\nItem data dalam urutan menurun\n" );
} /*
akhir else */
/*
menampilkan array terurut */
for
( kounter = 0; kounter < UKURAN; kounter++ ) {
printf( "%5d", a[ kounter ] );
} /*
akhir for */
printf( "\n" );
return
0; /* indikasi terminasi sukses */
} /* akhir main */
/* pengurutan bubble multiguna; parameter
banding adalah sebuah pointer ke
fungsi perbandingan yang menentukan tatanan pengurutan */
void bubble( int kerja[], const int
ukuran, int (*banding)( int a, int b ))
{
int
pass; /* kounter pass */
int
hitung; /* kounter perbandingan */
void
tukar( int *elemen1Ptr, int *elemen2ptr ); /* prototipe */
/*
loop untuk mengendalikan pass */
for
( pass = 1; pass < ukuran; pass++ ) {
/*
loop untuk mengendalikan jumlah perbandingan tiap pass */
for
( hitung = 0; hitung < ukuran - 1; hitung++ ) {
/* jika elemen tidak terurut, ditukar */
if ( (*banding)( kerja[ hitung ],
kerja[ hitung + 1 ] )) {
tukar( &kerja[ hitung ], &kerja[ hitung + 1 ] );
}
/* akhir if */
}
/* akhir for */
} /*
akhir for */
} /* akhir fungsi bubble */
/* menukar elemen pada lokasi memori yang ditunjuk
elemen1Ptr dan
elemen2Ptr */
void tukar( int *elemen1Ptr, int
*elemen2Ptr )
{
int
tampung; /* variabel penampung sementara */
tampung = *elemen1Ptr;
*elemen1Ptr = *elemen2Ptr;
*elemen2Ptr = tampung;
} /* akhir fungsi swap */
/* menentukan apakah terurut secara menaik
*/
int menaik( int a, int b )
{
return
b < a; /* ditukar jika b kurang dari a */
} /* akhir fungsi menaik */
/* menentukan apakah terurut secara menurun
*/
int menurun( int a, int b )
{
return
b > a; /* ditukar jika b lebih dari a */
} /* akhir fungsi menurun */
|
Gambar 6.26 Program
pengurutan multiguna dengan pointer fungsi
Masukkan 1 untuk mengurutkan secara
menaik,
Masukkan 2 untuk mengurutkan secara
menurun: 1
Item data dalam urutan asli
2 6 4
8 10 12
89 68 45
37
Item data dalam urutan menaik
2 4 6
8 10 12
37 45 68
89
Masukkan 1 untuk mengurutkan secara
menaik,
Masukkan 2 untuk mengurutkan secara
menurun: 2
Item data dalam urutan asli
2 6 4
8 10 12
89 68 45
37
Item data dalam urutan menurun
89 68 45
37 12 10
8 6 4
2
Gambar 6.27 | Contoh keluaran program
pengurutan bubble
Parameter berikut muncul di dalam
header fungsi bubble (baris 52)
int (*banding)( int a, int b )
Ini memberitahu bubble untuk mengharapkan sebuah parameter (banding) yang merupakan sebuah pointer ke
fungsi yang menerima dua parameter integer dan menghasilkan nilai balik
integer. Sepasang kurung dibutuhkan untuk mengapit *banding dan mengelompokkan * dengan banding dalam mengindikasikan bahwa banding adalah suatu pointer. Jika Anda tidak
menyertakan kurung tersebut, deklarasi tersebut akan menjadi
int *banding( int a, int b )
yang mendeklarasikan sebuah fungsi yang
menerima dua integer sebagai parameternya dan menghasilkan nilai balik berupa
suatu pointer ke integer.
Prototipe fungsi bubble ditampilkan pada baris 7. Prototipe
tersebut dapat pula dituliskan sebagai
int (*)( int, int );
tanpa nama pointer-fungsi dan nama
parameter.
Fungsi yang dilewatkan ke bubble dipanggil di dalam suatu statemen if (baris 66) sebagai berikut:
if ( (*banding)( kerja[ hitung ], kerja[ hitung
+ 1 ] ) )
Sama seperti sebuah pointer ke variabel
yang didereferensi untuk mengakses nilai variabel tersebut, pointer ke fungsi
juga dapat didereferensi untuk menggunakan fungsi tersebut. Pemanggilan
tersebut dapat pula dilakukan tanpa mendereferensi pointer, seperti
if ( banding( kerja[ hitung ], kerja[ hitung + 1 ] ) )
yang menggunakan pointer secara
langsung seperti nama fungsi.
Penggunaan
Pointer Fungsi untuk Menciptakan Sistem Menu
Gambar 6.28 menyajikan sebuah contoh
generik dalam mendefinisikan dan menggunakan array pointer ke fungsi.
Didefinisikan tiga fungsi: fungsi1, fungsi2, dan fungsi3, yang masing-masing mengambil sebuah argumen integer dan tidak
menghasilkan nilai balik apapun. Ketiga pointer ke fungsi disimpan di dalam
array f, yang didefinisikan pada baris 14.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
/* Gambar 6.28: gambar06_28.c
Mendemonstrasikan
array yang memuat pointer ke fungsi */
#include <stdio.h>
/* prototipe */
void fungsi1( int a );
void fungsi2( int b );
void fungsi3( int c );
int main( void )
{
/*
menginisialisasi array 3 pointer ke fungsi yang mengabil
sebuah argumen integer dan mengembalikan void */
void
(*f[ 3 ])( int ) = { fungsi1, fungsi2, fungsi3 };
int
pilihan; /* variabel untuk menampung pilihan pengguna */
printf( "Masukkan suatu angka antara 0 dan 2, 3 untuk
mengakhiri: " );
scanf( "%d", &pilihan );
/*
memproses pilihan pengguna */
while
( pilihan >= 0 && pilihan < 3 ) {
/*
memanggil fungsi pada pilihan lokasi di dalam array f dan
melewatkan pilihan sebagai argumen */
(*f[ pilihan ])( pilihan );
printf(
"Masukkan suatu angka antara 0 dan 2, 3 untuk mengakhiri:
");
scanf( "%d", &pilihan );
} /*
akhir while */
printf( "Eksekusi program selesai.\n" );
return
0; /* indikasi terminasi sukses */
} /* akhir main */
void fungsi1( int a )
{
printf( "Anda memasukkan %d jadi fungsi1 dipanggil\n\n",
a );
} /* akhir fungsi1 */
void fungsi2( int b )
{
printf( "Anda memasukkan %d jadi fungsi2 dipanggil\n\n",
b );
} /* akhir fungsi2 */
void fungsi3( int c )
{
printf( "Anda memasukkan %d jadi fungsi3 dipanggil\n\n",
c );
} /* akhir fungsi3 */
|
Masukkan suatu angka antara 0 dan 2, 3
untuk mengakhiri: 1
Anda memasukkan 1 jadi fungsi2
dipanggil
Masukkan suatu angka antara 0 dan 2, 3
untuk mengakhiri: 0
Anda memasukkan 0 jadi fungsi1
dipanggil
Masukkan suatu angka antara 0 dan 2, 3
untuk mengakhiri: 2
Anda memasukkan 2 jadi fungsi3
dipanggil
Masukkan suatu angka antara 0 dan 2, 3
untuk mengakhiri: 3
Eksekusi program selesai.
Gambar 6.28 | Mendemonstrasikan array yang
memuat pointer ke fungsi
Kesimpulan
§
Pointer memampukan program untuk mensimulasikan
pemanggilan-dengan-referensi dan untuk menciptakan dan memanipulasi struktur
data dinamis, yaitu struktur data yang dapat mengembang dan menyusut pada saat
eksekusi, seperti senarai berantai (linked
list), antrian (queue), tumpukan
(stack), dan pohon (tree).
§
Pointer adalah variabel yang memiliki nilai berupa alamat memori.
Normalnya, variabel secara langsung memuat sebuah nilai spesifik. Pointer, di
sisi lain, memuat alamat suatu variabel yang memuat sebuah nilai. Nama variabel
secara langsung mereferensi suatu nilai, dan pointer secara tidak langsung
mereferensi suatu nilai.
§
Pointer harus diinisialisasi ketika didefinisikan atau di dalam sebuah
statemen penugasan. Pointer bisa diinisialisasi dengan NULL, 0, atau sebuah alamat. Pointer dengan
nilai NULL tidak menunjuk
apapun. NULL adalah sebuah
konstanta simbolik yang didefinisikan di dalam header <stddef.h> (dan beberapa header lain, seperti <stdio.h>). Penginisialisasian sebuah pointer
dengan 0 sama dengan penginisialisasian sebuah pointer dengan NULL, tetapi NULL lebih direkomendasikan.
§
Oparator alamat & merupakan suatu
operator unary yang menghasilkan nilai balik berupa sebuah alamat operand.
§
Operator unary *, umumnya
dikenal sebagai operator indireksi atau operator dereferensi, menghasilkan
nilai balik berupa nilai dari objek yang ditunjuk operandnya.
§
Ada dua cara dalam melewatkan argumen ke suatu fungsi,
pemanggilan-dengan-nilai dan pemanggilan-dengan referensi. Banyak fungsi
memerlukan kapabilitas untuk memodifikasi satu atau lebih variabel di dalam
pemanggil dan untuk melewatkan sebuah pointer ke objek data yang besar agar
menghindar penyalinan objek. Untuk kepentingan ini, C menyediakan kapabilitas
pemanggilan-dengan-referensi.
§
Kualifier const memampukan Anda
untuk menginformasikan kompiler bahwa nilai dari variabel tertentu tidak boleh
dimodifikasi. Kualifier const tidak ada pada
awal C, karena ditambahkan ke dalam bahasa C oleh komiter ANSI C.
§
Level tertinggi atas hak akses data diberikan oleh pointer tak-konstan ke
data tak-konstan. Pada kasus ini, data dapat dimodifikasi melalui pointer yang
didereferensi, dan pointer dapat dimodifikasi untuk menunjuk ke item data lain.
Deklarasi untuk pointer tak-konstan ke data tak-konstan tidak mencantumkan const. Pointer semacam itu digunakan untuk
menerima string sebagai argumen dalam suatu fungsi yang menggunakan aritmatika
pointer untuk memproses (bisa jadi memodifikasi) setiap karakter di dalam
string.
§
Pointer tak-konstan ke data konstan dapat dimodifikasi untuk menunjuk ke
sembarang item data bertipe sesuai, tetapi data yang ditunjuk tersebut tidak
dapat dimodifikasi. Pointer semacam itu dapat dipakai untuk menerima argumen
array pada suatu fungsi yang memproses setiap elemen array tanpa memodifikasinya.
§
Sebuah pointer konstan ke data tak-konstan selalu menunjuk ke lokasi
memori yang sama, dan data pada lokasi tersebut dapat dimodifikasi melalui
pointer itu. Hal ini merupakan kasus default pada suatu nama array. Nama array
merupakan sebuah pointer konstan yang menunjuk ke awal array. Semua data di
dalam array dapat diakses dan diubah menggunakan nama array (lewat subskript
array). Pointer konstan ke data tak-konstan dapat dipakai untuk menerima array
sebagai argumen pada suatu fungsi yang mengakses elemen array menggunakan
notasi subskript array. Pointer yang dideklarasikan const harus diinisialisasi ketika didefinisikan (jika pointer
merupakan parameter fungsi, maka diinisialisasi dengan sebuah pointer yang
dilewatkan kepada fungsi tersebut).
§
Hak akses yang paling minim adalah pointer konstan ke data konstan.
Pointer semacam itu selalu menunjuk ke lokasi memori yang sama, dan data pada
lokasi memori tersebut tidak dapat dimodifikasi. Hal inilah yang terjadi ketika sebuah array
dilewatkan ke suatu fungsi yang hanya “melihat” array menggunakan notasi
subskript dan tidak memodifikasi array tersebut.
§
C menyediakan operator
unary spesial sizeof untuk menentukan ukuran dalam byte dari suatu array (atau dari
sembarang tipe data) selama kompilasi program berlangsung.
§
Operator sizeof dapat diterapkan pada sembarang nama variabel, tipe, atau nilai (termasuk
nilai dari suatu ekspresi). Ketika diterapkan pada nama variabel (yang bukan
nama array) atau pada konstanta, jumlah byte yang digunakan untuk menyimpan
tipe variabel atau konstanta yang dijadikan nilai balik. Sepasang kurung yang
digunakan sizeof diperlukan jika nama tipe terdiri-dari dua kata sebagai operand (misalnya
long double atau unsigned short). Sepasang kurung tidak diperlukan
bila operand hanya memuat satu kata.
§
Himpunan terbatas atas operasi aritmatika dapat diterapkan pada pointer.
Sebuah pointer dapat diinkremen (++) atau
didekremen (--), sebuah
integer dapat ditambahkan pada pointer (+ atau +=), sebuah integer dapat
dikurangi dari suatu pointer (- atau -=), dan suatu pointer dapat dikurangi
dari pointer lainnya.
§
Array dan pointer berelasi secara intim di dalam C dan seringkali saling
dipertukarkan. Suatu nama array dapat dianggap sebagai sebuah pointer konstan.
Pointer dapat digunakan untuk melakukan berbagai operasi yang melibatkan array.
§
Array bisa memuat pointer. Kegunaan umum dari array pointer adalah untuk
membentuk array string. Setiap entri di dalam array tersebut adalah sebuah
string, tetapi di dalam C, string secara esensi merupakan sebuah pointer yang
menunjuk ke karakter pertama. Jadi, setiap entri di dalam suatu array string
adalah sebuah pointer yang menunjuk ke karakter pertama dari suatu string.
§
Pointer ke fungsi dapat dilewatkan ke fungsi, dijadikan nilai balik,
disimpan di dalam array, dan ditugaskan pada fungsi lain.
Soal
1. Carilah kesalahan pada potong program
berikut, bila diasumsikan
int *zPtr; /* zPtr akan mereferensi array z */
int *aPtr = NULL;
void *sPtr = NULL;
int angka, i;
int z[ 5 ] = { 1, 2, 3, 4, 5 };
sPtr = z;
a) ++zPtr;
b) /* menggunakan pointer untuk
mendapatkan nilai pertama dari array; asumsi zPtr diinisialisasi */
angka = zPtr;
c)
/* menugaskan
elemen 2 dari array kepada angka */
angka = *zPtr[2];
d)
/* menampilkan array z; diasumsikan zPtr
diinisialisasi */
for ( i = 0; i <= 5; i++ ) {
printf( "%d ", zPtr[ i ] );
}
e)
/*
menugaskan nilai yang ditunjuk oleh sPtr ke angka */
angka = *sPtr;
f)
++z
2. Carilah kesalahan pada potong program
berikut. Jika kesalahan tersebut dapat dikoreksi, jelaskan bagaimana.
a) int *angka;
printf( "%d\n", * angka);
b) float *realPtr;
long *integerPtr;
integerPtr =
realPtr;
c) int *
x, y;
x = y;
d) char s[]
= "Ini adalah
array karakter";
int hitung;
for ( ; *s != '\0'; s++)
printf( "%c ", *s );
e) short *numPtr,
hasil;
void *generikPtr = numPtr;
result =
*generikPtr + 7;
f) float x =
19.34;
float xPtr = &x;
printf( "%f\n", xPtr );
g) char *s;
printf( "%s\n", s );
3. Apakah yang
dilakukan program ini?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
/* soal06_3.c */
/* Apa yang dilakukan program ini? */
#include <stdio.h>
void misteri1( char *s1, const char *s2
); /* prototipe */
int main( void )
{
char
string1[ 80 ]; /* menciptakan array char */
char
string2[ 80 ]; /* menciptakan array char */
printf(
"Masukkan dua string: " );
scanf(
"%s%s" , string1, string2 );
misteri1(
string1, string2 );
printf("%s",
string1 );
return
0; /* indikasi terminasi sukses */
}
/* akhir main */
/* Apa yang dilakukan fungsi ini? */
void misteri1( char *s1, const char *s2
)
{
while
( *s1 != '\0' ) {
s1++;
} /* akhir while */
for ( ; *s1 = *s2; s1++, s2++ ) {
; /* statemen kosong */
} /* akhir for */
} /* akhir fungsi misteri1 */
|
4. Apakah yang
dilakukan program ini?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
/* soal06_4.c */
/* Apa yang dilakukan program ini? */
#include <stdio.h>
int misteri2( const char *s
); /* prototipe */
int main( void )
{
char
string[ 80 ]; /* menciptakan array char */
printf(
"Masukkan sebuah string: ");
scanf(
"%s", string );
printf(
"%d\n", misteri2 ( string ) );
return
0; /* indikasi terminasi sukses */
} /* akhir main */
/* Apa yang dilakukan fungsi ini? */
int misteri2( const char *s
)
{
int x; /* kounter */
/* loop menjelajah string */
for ( x = 0; *s != '\0'; s++ ) {
x++;
} /* akhir for */
return x;
} /* akhir fungsi misteri2 */
|
5. Apa yang
dilakukan program ini?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
/* soal06_5.c */
/* Apa yang dilakukan program ini? */
#include <stdio.h>
int misteri3( const char *s1,
const char *s2 ); /* prototipe */
int main( void )
{
char string1[ 80 ]; /* menciptakan array char */
char string2[ 80 ]; /* menciptakan array char */
printf( "Masukkan dua string: " );
scanf( "%s%s",
string1 , string2 );
printf( "The result is %d\n",
misteri3( string1, string2 ) );
return 0; /* indikasi terminasi sukses */
} /* akhir main */
int misteri3( const char *s1,
const char *s2 )
{
for
( ; *s1 != '\0' && *s2 != '\0';
s1++, s2++ ) {
if ( *s1 != *s2 ) {
return 0;
}
/* akhir if */
}
/* akhir for */
return
1;
} /* akhir fungsi misteri3 */
|
No comments:
Post a Comment