2. bagaimana pointer digunakan untuk mengakses dua dimensi array?

Hubungan antara pointer dan array sangatlah erat. Perhatikan contoh berikut :

char str[80], *p1;
p1 = str;

Disini, p1 telah diset ke alamat dari elemen pertama array str. Untuk mengakses elemen ke lima dari array kita bisa menuliskan

str[4];
/* atau */
 *(p1+4)

Kedua pernyataan diatas akan mengembalikan nilai dari elemen ke lima. Ingat, array dimulai dengan 0. Untuk mengakses elemen ke lima, kita harus menggunakan 4 untuk mengindekskan str. Kita juga menambahkan 4 ke pointer p1 untuk mengakses elemen ke lima karena p1 sebelumnya menunjuk ke elemen pertama array str. (Ingat bahwa sebuah nama array tanpa indeks mengembalikan alamat pertama dari array itu, yang merupakan alamat dari elemen pertama.)

C/C++ menyediakan dua cara untuk mengakses array: aritmatika pointer dan mengindeks array (array-indexing). Meskipun array-indexing kelihatan lebih mudah, aritmatika pointer lebih cepat. Karena kecepatan adalah sebuah perbandingan dalam pemrograman C/C++, programmer biasanya menggunakan pointer untuk mengakses array.

Berikut ini adalah dua versi fungsi putstr(), yang pertama menggunakan cara array-indexing dan yang kedua dengan pointer. Fungsi putstr() ini menuliskan string yang diinputkan sebagai argumen ke konsol.

/* Dengan mengindekskan array s. */
void putstr(char *s)
{
	register int t;
	for (t = 0; s[t]; ++t) putchar(s[t]);
}

/* Mengakses s dengan pointer. */
void putstr(char *s)
{
	while (*s) putchar(*s++);
}

Array Pointer

Pointer juga bisa diarraykan layaknya tipe data lain. Misalnya deklarasi untuk array pointer ke int dengan 10 elemen adalah

 int *x[10]; 

Untuk memberikan alamat sebuah variabel (misalnya variabel var) ke elemen ketiga dari array pointer, tulis

 x[2] = var; 

untuk menemukan nilai var, tulis

*x[2] 

Untuk melewatkan array pointer ke sebuah fungsi, kita bisa menggunakan metode yang sama yang digunakan untuk melewatkan array lainnya, tinggal memanggil fungsi dengan nama array tanpa indeks. Misalnya, sebuah fungsi yang bisa menerima array x akan terlihat seperti ini

void display_array(int *q[])
{
int t;
for(t=0; t<10; t++)
printf("%d", *q[t]);
}

Ingat, q bukan sebuah pointer ke integer, tapi q adalah pointer ke sebuah array pointer ke integer (Perhatikan gambar dibawah.) Parameter q tidak akan bisa menerima argumen selain sebuah pointer ke integer, karena itulah q dideklarasikan sebagai array pointer.

2. bagaimana pointer digunakan untuk mengakses dua dimensi array?

Array pointer sering digunakan untuk menyimpan pointer ke string. Kita bisa membuat fungsi yang menghasilkan pesan error berdasarkan kode angka, seperti berikut:

void syntax_error(int num)
{
static char *err[] = {
"Cannot Open File\n",
"Read Error\n",
"Write Error\n",
"Media Failure\n"
};
printf("%s", err[num]);
}

array err menyimpan pointer ke setiap string. Seperti yang kita lihat, printf() didalam syntax_error() dipanggil dengan pointer karakter yang menunjuk ke salah satu dari berbagai pesan error yang diindekskan berdasarkan pada angka yang dilewatkan ke fungsi. Misalnya, jika num diberi nilai 2, pesan Write Error akan ditampilkan.

Multiple Indirection

Kita bisa membuat sebuah pointer yang menunjuk ke pointer yang menunjuk ke nilai target. Situasi ini disebut multiple indirection, atau pointers to pointers. Perhatikan gambar untuk lebih memahamin konsepnya. Nilai dari pointer normal adalah alamat objek yang berisi nilai yang diinginkan. Dalam kasus pointer ke pointer, pointer pertama berisi alamat pointer kedua, yang menunjuk ke objek yang berisi nilai yang diinginkan.

Multiple Indirection bisa dilakukan pada tingkat apapun yang dibutuhkan, tapi terlebih dari pointer ke pointer jarang diperlukan. Faktanya, indirection yang berlebihan sulit untuk dikendalikan dan rentan terhadap konseptual error.

2. bagaimana pointer digunakan untuk mengakses dua dimensi array?

Variabel yang merupakan pointer ke pointer harus dideklarasikan dengan demikian. Hal ini dilakukan denan menambahkan tanda bintang di depan nama variabel. Misalnya, deklarasi berikut memberitahukan kompiler bahwa newbalance adalah pointer ke pointer tipe float:

          float **newbalance;

Perlu dipahami bahwa newbalance bukan pointer ke angka floating-point tapi adalah sebuah pointer ke pointer float.

Untuk mengakses nilai target yang secara tidak langsung ditunjuk oleh pointer ke pointer, kita harus memakai tanda bintang dua kali, seperti pada contoh berikut:

#include <stdio.h>
int main(void)
{
int x, *p, **q;
x = 10;
p = &x;
q = &p;
printf("%d", **q); /* tampilkan nilai x */
return 0;
}

Disini, p dideklarasikan sebagai pointer ke integer dan q sebagai pointer ke pointer ke interger. Pemanggilan printf() menampilkan angka 10 pada konsol.

Inisialisasi Pointer

Setelah non-static lokal pointer dideklarasikan tapi sebelum diberikan nilai, maka pointer berisi nilai yang tidak diketahui. (Global dan static lokal pointer secara otomatis diinisialisasikan dengan nol.) Sehingga ketika menggunakan pointer sebelum memberikan nilai yang valid, kemungkinan program akan error dan parahnya mungkin berdampak pada sistem operasi, tipe error yang sangat berbahaya.

Ada sebuah kaidah penting yang diikuti oleh kebanyakan programmer C/C++ ketika berurusan dengan pointer: Pointer yang tidak menunjuk ke lokasi memori manapun diberikan nilai kosong (nol). Dengan kaidah, semua pointer yang kosong berarti dia tidak menunjuk kemanapun dan tidak boleh dipakai. Bagaimanapun, hanya karena suatu pointer bernilai kosong tidak membuatnya “aman”. Penggunaan nilai kosong ini hanyalah sebuah kaidah yang diikuti oleh programmer. Hal itu bukan sebuah aturan yang dibuat oleh bahasa C atau C++. Misalnya, jika kita menggunakan pointer kosong di sebelah kiri pernyataan pengerjaan (assignment statement), kita masih menjalankan resiko kegagalan program atau sistem operasi.

Karena pointer kosong dianggap tak terpakai, kita bisa menggunakan pointer kosong untuk membuat rutin pointer lebih mudah di kodekan dan lebih efisien. Misalnya, kita bisa menggunakan pointer kosong untuk menandai akhir dari array pointer. Rutin yang mengakses array itu akan tahu bahwa dia telah mencapai akhir ketika menjumpai nilai kosong. Fungsi search() berikut mengilustrasikan jenis pendekatan ini.

/* Mencari nama */
int search(char *p[], char *name)
{	
register int t;
for(t=0; p[t]; ++t)
if(!strcmp(p[t], name)) return t;
return -1; /* tidak ditemukan*/
}

Loop for dalam fungsi search() akan terus berjalan sampai kecocokan atau pointer kosong ditemukan. Dengan anggapan bahwa akhir dari array adalah kosong, maka kondisi kontrol loop menjadi salah saat nilai kosong ini ditemukan.

Programmer C/C++ biasanya menginisialisasikan string. Kita bisa melihat contohnya pada fungsi syntax_error() di atas. Variasi lain dari inisialisasi ini adalah:

 
char *p = "hello world"; 

Seperti yang bisa kita lihat, pointer p bukan sebuah array. Alasan mengapa inisialisasi ini berhasil karena bagaimana kompiler beroperasi. Semua kompiler C/C++ membuat sebuah string table, yang digunakan untuk menyimpan konstan string yang digunakan program. Dengan begitu, deklarasi di atas menempatkan alamat hello world, yang disimpan dalam tabel string, ke dalam pointer p. Pointer p ini bisa digunakan sama halnya dengan string lain (tapi string ini tidak bisa diubah). Agar dapat lebih memahaminya, perhatikan contoh program berikut:

#include <stdio.h>
#include <string.h>
char *p = "hello world";
int main(void)
{
register int t;
/* print the string forward and backwards */
printf(p);
for(t=strlen(p)-1; t>-1; t--) printf("%c", p[t]);
return 0;
}

Dalam standar C++, tipe dari literal string adalah const char*. Tapi C++ menyediakan konversi otomatis ke char*. Sehingga, program di atas masih valid. Bagaimanapun, konversi otomatis ini adalah fitur usang, yang berarti bahwa kita tidak boleh mengandalkan fitur ini untuk kode baru. Untuk program baru, kita harus beranggapan bahwa literal string adalah konstan dan deklarasi p pada program di atas harus ditulis seperti ini.

const char *p = "hello world";

Pointer ke Fungsi

Fitur hebat lainnya dari C++ adalah function pointer (pointer ke fungsi), meskipun membingungkan. Fungsi memang tidak sama dengan variabel tapi fungsi juga memiliki lokasi fisik di memori yang bisa diberikan ke pointer. Alamat ini adalah titik masuk ke fungsi dan merupakan alamat yang digunakan ketika fungsi dipanggil. Ketika pointer menunjuk ke fungsi, maka fungsi itu bisa dipanggil melalui pointer. Pointer fungsi juga memungkinkan fungsi untuk diberikan sebagai argumen ke fungsi lainnya.

Untuk mendapatkan alamat fungsi, kita hanya perlu menuliskan nama fungsi tanpa tanda kurung ataupun argumen. Perhatikan contoh berikut untuk lebih jelasnya:

#include <stdio.h>
#include <string.h>
void check(char *a, char *b,
int (*cmp)(const char *, const char *));
int main(void)
{
char s1[80], s2[80];
int (*p)(const char *, const char *);
p = strcmp;
gets(s1);
gets(s2);
check(s1, s2, p);
return 0;
}
void check(char *a, char *b,
int (*cmp)(const char *, const char *))
{
printf("Testing for equality.\n");
if(!(*cmp)(a, b)) printf("&quot;Equal");
else printf("Not Equal");
}

Ketika fungsi check() dipanggil, dua pointer karakter dan satu pointer fungsi diberikan sebagai parameter. Di dalam fungsi check(), argumen dideklarasikan sebagai pointer karakter dan pointer fungsi. Perhatikan bagaimana pointer fungsi dideklarasikan.  Kita harus menggunakan bentuk yang sama ketika mendeklrasikan pointer fungsi lainnya, meskipun tipe kembali dan parameter fungsi bisa saja berbeda. Tanda kurung pada *cmp diperlukan agar kompiler dapat mengartikan pernyataan dengan benar.

Di dalam check(), ekspresi

(*cmp) (a, b)

memanggil strcmp(), yang ditunjuk oleh cmp, dengan argumen a dan b. Tanda kurung pada *cmp harus ada. Ini adalah salah satu cara untuk memanggil fungsi melalui pointer. Yang kedua, syntaxnya lebih sederhana, seperti berikut, bisa juga digunakan.

cmp(a, b);

Namun kita akan lebih sering menjumpai gaya syntax pertama, ini dikarenakan  lebih jelas menyatakan bahwa sebuah fungsi sedang dipanggil menggunakan pointer. Bentuk kedua bisa saja membuat kita menganggap bahwa cmp adalah sebuah fungsi lain. Diluar dari itu, kedua ekspresi ini sama saja.

Perhatikan bahwa check() bisa dipanggil dengan menggunakan strcmp() secara langsung, seperti berikut ini:

check(s1, s2, strcmp);

Ini menghilangkan keperluan akan variabel pointer tambahan.

Kita mungkin bertanya – tanya mengapa ada orang yang menuliskan program seperti ini. Tentu saja, tidak ada yang didapatkan dan ada kekeliruan yang cukup berarti diperlihatkan pada contoh sebelumnya. Bagaimanapun, kadang-kadang lebih menguntungkan untuk melewatkan fungsi sebagai parameter atau membuat array fungsi. Misalnya, ketika kompiler atau penerjemah dituliskan, parser (bagian yang memeriksa ekspresi) seringkali menggunakan berbagai fungsi pendukung, misalnya fungsi yang menghitung operasi matematika, melakukan I/O, atau akses ke sistem. Daripada menggunakan pernyataan switch yang banyak dengan semua fungsi ini di dalamnya, array pointer fungsi bisa dibuat. Dengan cara ini, fungsi yang tepat dipilih berdasarkan indeksnya. Untuk lebih memahaminya kita akan menggunakan versi pengembangan dari contoh sebelumnya. Dalam program ini, check() bisa dibuat untuk memeriksa persamaan alfabet ataupun persamaan angka dengan memanggilnya dengan fungsi perbandingan berbeda.

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
void check(char *a, char *b,
int (*cmp)(const char *, const char *));
int numcmp(const char *a, const char *b);
int main(void)
{
char s1[80], s2[80];
gets(s1);
gets(s2);
if(isalpha(*s1))
check(s1, s2, strcmp);
else
check(s1, s2, numcmp);
return 0;
}
void check(char *a, char *b,
int (*cmp)(const char *, const char *))
{
printf("Testing for equality.\n");
if(!(*cmp)(a, b)) printf("Equal");
else printf("Not Equal");
}
int numcmp(const char *a, const char *b)
{
if(atoi(a)==atoi(b)) return 0;
else return 1;
}

Dalam program ini, jika kita memasukkan abjad, strcmp() diberikan ke check(). Jika tidak, numcmp() yang digunakan. Karena check() memanggil fungsi yang diterimanya sebagai argumen, dia bisa menggunakan perbandingan fungsi berbeda dalam kasus yang berbeda.