Friday, December 23, 2016

Bab 6. Pemrograman C Belajar Dari Contoh


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