Modul Ajar & Praktikum Pemrograman Lanjut TIF 4117
Program Studi Teknik Informatika Fakultas Teknik Universitas Brawijaya 2011
DAFTAR ISI 1 BAB 1: POINTER DASAR ................................................................................................................... 4 1.1 PENDAHULUAN.................................................................................................................................................................. 5 1.2 REFERENSI ....................................................................................................................................................................... 5 1.3 DEKLARASI VARIABEL POINTER ......................................................................................................................................... 6 1.4 PENDEFINISIAN POINTER DAN PROSES REFERENSI ............................................................................................................. 7 1.5 AKSES POINTER DAN PROSES DEREFERENSI ...................................................................................................................... 8 1.6 SIMBOL *: DEKLARASI VS DEREFERENSI .......................................................................................................................... 10 1.7 VOID POINTER ............................................................................................................................................................... 10 1.8 NULL POINTER ............................................................................................................................................................... 11 1.9 ARITMETIKA POINTER ..................................................................................................................................................... 12 1.10 POINTER SEBAGAI PARAMETER FORMAL FUNGSI ............................................................................................................. 13 1.11 TUGAS .......................................................................................................................................................................... 14 2 BAB 2: ARRAY ................................................................................................................................. 15 2.1 PENDAHULUAN................................................................................................................................................................ 16 2.2 ARRAY SATU DIMENSI .................................................................................................................................................... 16 2.2.1 Deklarasi Array Satu Dimensi ............................................................................................................................ 16 2.2.2 Inisialisasi Array Satu Dimensi .......................................................................................................................... 16 2.2.3 Akses array satu dimensi ................................................................................................................................... 18 2.2.4 Array dan Pointer ................................................................................................................................................ 19 2.2.5 Array sebagai Parameter Fungsi ...................................................................................................................... 21 2.3 ARRAY MULTIDIMENSI .................................................................................................................................................... 21 2.3.1 Deklarasi Array Dua Dimensi ............................................................................................................................ 22 2.3.2 Inisialisasi Array Dua Dimensi ........................................................................................................................... 22 2.3.3 Akses Array Dua Dimensi .................................................................................................................................. 24 2.3.4 Array Dua Dimensi sebagai Parameter Fungsi ............................................................................................... 24 2.4 TUGAS ........................................................................................................................................................................... 25 3 BAB 3: POINTER LANJUTAN - ALOKASI MEMORI DINAMIK........................................................... 26 3.1 PENDAHULUAN................................................................................................................................................................ 27 3.2 PENGALOKASIAN MEMORI SECARA DINAMIK .................................................................................................................... 27 3.2.1 Fungsi malloc ................................................................................................................................................... 27 3.2.2 Operator new ...................................................................................................................................................... 29 3.3 DEALOKASI ATAU PEMBEBASAN MEMORI .......................................................................................................................... 29 3.3.1 Fungsi free ........................................................................................................................................................ 30 3.3.2 Operator delete ............................................................................................................................................... 30 3.4 REALOKASI MEMORI ....................................................................................................................................................... 31 3.5 TUGAS ........................................................................................................................................................................... 32 4 BAB 4: STRING................................................................................................................................ 33 4.1 PENDAHULUAN................................................................................................................................................................ 34 4.2 DEKLARASI STRING......................................................................................................................................................... 34 4.2.1 Deklarasi via Array of Characters ..................................................................................................................... 34 4.2.2 Deklarasi via Pointer........................................................................................................................................... 34 4.3 INISIALISASI DAN PENDEFINISIAN STRING ....................................................................................................................... 36 4.3.1 Inisialisasi via Array ............................................................................................................................................ 36 4.3.2 Inisialisasi via Pointer ......................................................................................................................................... 38 4.4 PENANGANAN STRING ..................................................................................................................................................... 41 4.4.1 Fungsi strlen ................................................................................................................................................... 41 4.4.2 Fungsi strcmp ................................................................................................................................................... 41 4.4.3 Fungsi strcpy ................................................................................................................................................... 41 4.4.4 Fungsi strncpy ................................................................................................................................................. 42 4.4.5 Fungsi strcat ................................................................................................................................................... 42
4.4.6 Fungsi 4.4.7 Fungsi 4.4.8 Fungsi 4.4.9 Fungsi
strncat ................................................................................................................................................. 43 atof ........................................................................................................................................................ 43 atoi ........................................................................................................................................................ 44 atol ........................................................................................................................................................ 44 4.5 TUGAS ........................................................................................................................................................................... 45 5 BAB 5: STRUCT................................................................................................................................ 46 5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 5.9
PENDAHULUAN................................................................................................................................................................ 47 STRUKTUR ATAU STRUCT................................................................................................................................................. 47 DEKLARASI TIPE STRUCT DAN VARIABEL BERTIPE STRUCT ................................................................................................ 47 KEYWORD TYPEDEF ........................................................................................................................................................ 48 INISIALISASI DAN AKSES ANGGOTA STRUCT ..................................................................................................................... 49 RUANG LINGKUP STRUCT ................................................................................................................................................ 50 POINTER TO STRUCT ....................................................................................................................................................... 50 UNION ........................................................................................................................................................................... 51 TUGAS ........................................................................................................................................................................... 53
6 BAB 6: OPERASI FILE...................................................................................................................... 54 6.1 6.2 6.3 6.4 6.5 6.6 6.7 6.8
PENDAHULUAN................................................................................................................................................................ 55 FILE ............................................................................................................................................................................... 55 PORTAL I/O STANDAR: STDIN DAN STDOUT..................................................................................................................... 56 PENANGANAN FILE: TINGKAT TINGGI VS TINGKAT RENDAH ............................................................................................. 56 POSISI PROGRAM PADA FILE ........................................................................................................................................... 57 MEMBUKA FILE ............................................................................................................................................................... 57 MENUTUP FILE ............................................................................................................................................................... 58 MENULIS FILE ................................................................................................................................................................ 59 6.8.1 Fungsi fprintf ................................................................................................................................................. 59 6.8.2 Fungsi fputc...................................................................................................................................................... 59 6.8.3 Fungsi fputs...................................................................................................................................................... 60 6.9 MEMBACA FILE ............................................................................................................................................................... 60 6.9.1 Fungsi fscanf ................................................................................................................................................... 60 6.9.2 Fungsi fgetc...................................................................................................................................................... 60 6.9.3 Fungsi fgets...................................................................................................................................................... 61 6.9.4 Fungsi feof ........................................................................................................................................................ 61 6.10 CONTOH APLIKASI ........................................................................................................................................................ 61 6.11TUGAS ........................................................................................................................................................................... 63 7 DAFTAR PUSTAKA ........................................................................................................................... 64
3
1 BAB 1: Pointer Dasar Pokok bahasan: Referensi Deklarasi dan definisi pointer Referensi dan dereferensi Void pointer Null pointer Aritmetika pointer Pointer sebagai parameter fungsi
4
1.1 Pendahuluan Sebuah pointer (kadang-kadang diterjemahkan sebagai “penunjuk”) adalah sebuah variabel yang bertipe khusus, yaitu yang menyimpan alamat dari sebuah variabel lainnya. Pointer memiliki peran yang sangat penting dalam C/C++. Secara khusus, pointer berperan vital dalam implementasi struktur data seperti array, string, dan linked list. Selain itu, pointer juga dapat digunakan untuk pengiriman parameter fungsi dan untuk menunjuk pada alamat fungsi. Untuk tujuan yang terakhir ini pembahasannya di luar fokus bab ini. Dalam memahami pointer, ada beberapa konsep dasar atau terkait yang perlu dipelajari. Diantaranya adalah referensi, pendeklarasian pointer, inisialisasi dan pendefinisian pointer, akses pointer, dereferensi, null ponter, void pointer, dan penggunaan pointer untuk pengiriman parameter fungsi.
1.2 Referensi Seperti yang telah diketahui, setiap variabel yang dideklarasikan akan dialokasikan tempat tertentu pada memori. Keberadaan variabel ini memudahkan kita untuk menggunakan data yang tersimpan pada memori, tanpa memedulikan lokasi fisik data tersebut. Untuk mengakses atau mengubah data tersebut cukup merujuknya melalui nama variabelnya. Tetapi sebenarnya kita masih bisa mengakses lokasi data tersebut melalui alamat variabelnya jika diperlukan. Alamat variabel ini sering juga disebut dengan “referensi” dari variabel tersebut. Dalam bahasa C/C++, proses me“referensi” atau mengakses alamat sebuah variabel dapat menggunakan operator alamat atau referensi yang direpresentasikan dengan simbol ampersand (&). Contoh di bawah ini menunjukkan cara mengakses alamat dari variabel menggunakan operator &. 1 #include <stdio.h> 2 #include <stdlib.h> 3 4 int main(){ 5 int a = 17; 6 double b = 13.5; 7 8 printf("Isi variabel: \n"); 9 printf("\t a = %d",a); 10 printf("\t b = %.2lf",b); 11 12 printf("\n\nAlamat variabel: \n"); 13 printf("\t a = %d ",&a); 14 printf("\t b = %d ",&b); 15 system(“pause”);
5
16 17 }
return 0; Gambar 1.1
Sebagai latihan, ketiklah contoh di atas untuk menjadi sebuah program. Setelah menjalankan program tersebut dan memperhatikan hasilnya, cobalah untuk mengubah format keluaran &a dan &b pada baris ke-13 dan ke-14 dari %d menjadi %p lalu amati hasilnya. Ambillah kesimpulan dari perubahan tersebut. Saat variabel a dan b dideklarasikan dan diinisialisasikan dengan nilai masing-masing 17 dan 13.5, di memori dialokasikan tempat yang sesuai, seperti yang diilustrasikan pada gambar 1.2.
Gambar 1.2 Nilai alamat dari a dan b yang tertulis di sini hanya sebagai contoh; nilai yang sebenarnya dapat berbeda dalam setiap menjalankan program. Alamat a dan b dapat diakses dengan ekspresi masing-masing &a dan &b.
1.3 Deklarasi Variabel Pointer Sebelum diisikan dengan alamat dari variabel lainnya, sebuah pointer perlu dideklarasikan terlebih dahulu. Sintaksis pendeklarasiannya adalah sebagai berikut. tipedatavariabelyangditunjuk *namavariabelpointer; atau tipedatavariabelyangditunjuk * namavariabelpointer; Perhatikan penggunaan simbol bintang/asterisk (*) di atas. Simbol ini membedakan deklarasi sebuah variabel non-pointer dengan deklarasi variabel pointer. Tipe data variabel yang ditunjuk pointer dapat berupa sembarang tipe. Aturan penamaan variabel pointer harus mengikuti aturan yang sama dengan penamaan variabel biasa. Contoh deklarasi pointers: int* pi; // pointer to integer float* pf; // pointer to float
6
Pointer yang telah dideklarasikan hanya dapat diisikan dengan alamat dari variabel yang bertipe data sesuai dengan deklarasi pointernya. Sebagai contoh, sebuah pointer to integer hanya dapat menyimpan alamat dari variabel yang bertipe data integer.
1.4 Pendefinisian Pointer dan Proses Referensi Saat dideklarasikan, sebuah pointer menunjuk pada alamat yang ‘acak’ atau tidak terdefinisi. Dalam keadaan demikian usaha untuk mengakses pointer ini dapat berakibat fatal karena pointer ini mungkin berisi alamat dari memori yang sedang dikelola oleh sistem atau aplikasi lain. Oleh karena itu, untuk menggunakan sebuah pointer kita harus memastikan bahwa dia berisi alamat yang benar atau yang kita inginkan. Inisialisasi atau pendefinisian sebuah pointer dapat dilakukan dengan salah satu dari dua cara. Pertama, dengan proses referensi ke variabel yang sudah ada. Kedua, dengan alokasi memori dinamik. Sub bab ini hanya akan membahas cara yang pertama. Cara yang kedua akan didiskusikan di bab lainnya. Sintaksis proses referensi sebuah pointer ke variabel lain yang sudah ada adalah: namavariabelpointer = &namavariabellainnya; Perhatikan pemakaian operator referensi (&) pada pernyataan di atas. Tentunya, kedua variabel di atas sudah harus dideklarasikan sebelumnya. Contoh di bawah ini menunjukkan inisialisasi pointer dengan cara referensi ke variabel lainnya. Jalankanlah program ini dan perhatikan hasilnya. 1 #include <stdio.h> 2 #include <stdlib.h> 3 4 int main(){ 5 int x; // Nilai x acak 6 int *px; // Nilai px acak 7 8 printf("\nKondisi x dan px sebelum inisialisasi "); 9 printf("\nNilai x: %d", x); 10 printf("\nAlamat x: %d", &x); 11 printf("\nNilai px = %d", px); 12 printf("\nAlamat px = %d\n", &px); 13 px = &x; 14 15 x = 10; 16 17 printf("\nKondisi x dan px setelah inisialisasi "); 18 printf("\nNilai x: %d", x); 19 printf("\nAlamat x: %d", &x); 7
20 21 22 23 24 25 }
printf("\nNilai px = %d", px); printf("\nAlamat px = %d", &px); system("pause"); return 0; Gambar 1.3
Beberapa proses yang terjadi saat program ini dijalankan dapat diilustrasikan pada gambar 1.4 berikut. deklarasi int x; int *px; inisialisasi pointer p dengan referensi px=&x; inisialisasi x x=10;
x px
x tidak terdefinisi px tidak terdefinisi
px
x
px
x
10
Gambar 1.4
1.5 Akses Pointer dan Proses Dereferensi Pengaksesan pointer dengan cara memanggil nama variabel pointer sama dengan mengakses alamat variabel yang ditunjuknya. Untuk mengakses nilai yang dikandung oleh variabel yang ditunjuknya, kita dapat menggunakan simbol bintang (*) yang diletakkan sebelum nama variabel pointer. Proses ini disebut dereferensi. Perhatikan contoh dereferensi pada kode di bawah ini. 1 #include <stdio.h> 2 #include <stdlib.h> 3 4 int main(){ 5 int x; // Nilai x acak 6 int *px = &x; // Deklarasi dan inisialisasi px 7 x = 11; 8 9 printf("\nKondisi x dan px setelah inisialisasi"); 10 printf("\nNilai x: %d", x); 11 printf("\nNilai px: %d", px); 8
12 13 14 15 16 17 18 19 20 21 22 23 24 25 }
// dereferensi untuk mengakses nilai x via pointer px printf("\nNilai *px = %d", *px); // dereferensi untuk mengubah nilai x via pointer px *px = 5; printf("\nKondisi x dan px setelah x diubah"); printf("\nNilai x: %d", x); printf("\nNilai px: %d", px); printf("\nNilai *px = %d", *px); system("pause"); return 0; Gambar 1.5
Beberapa proses yang terjadi saat kode di atas ini dijalankan dapat diilustrasikan pada gambar berikut: deklarasi x int x;
x
x tidak terdefinisi
deklarasi & inisialisasi p int px=&x;
px
x
inisialisasi x x=11;
px
x
px
x
dereferensi px *px=5;
11
5
Gambar 1.6 Amati penggunaan dereferensi pada baris ke-14 dan ke-16 pada gambar 1.4, lalu ambil kesimpulan tentang perbedaan kegunaannya. Amati juga perbedaan antara px dan *px. Cobalah untuk menambahkan variabel baru y bertipe integer dan inisialisasikan dengan sebuah nilai, misalnya 100. Referensikan alamat y ini ke pointer px setelah baris ke-19, kemudian cetak nilai px dan *px. Amati dan simpulkan hasilnya. Setelah itu coba ubah nilai y melalui px, misalnya menjadi 1000, dan cetak nilai y dan *px untuk mengamati perubahan y.
9
1.6 Simbol *: Deklarasi vs Dereferensi Perlu diingat benar bahwa simbol bintang (*) dalam pembahasan pointer memiliki dua semantik (makna) dan penggunaan yang berbeda. Makna pertama adalah untuk menunjukkan bahwa sebuah variabel merupakan sebuah pointer. Makna ini didapat dari pendeklarasian pointer tersebut. Contoh * untuk deklarasi dapat dilihat di baris ke-6 gambar 1.2 dan baris ke-6 gambar 1.3, seperti di bawah ini. int *px; // Deklarasi px: px adalah sebuah “pointer to integer” int *px = &x; // Deklarasi dan inisialisasi px Makna kedua adalah untuk mengambil isi dari alamat yang disimpan oleh pointer tersebut. Proses ini disebut dengan “dereferensi” seperti yang telah dijelaskan sebelumnya. Contoh * untuk deklarasi dapat dilihat di baris ke-14 dan baris ke-16 gambar 1.3, seperti di bawah ini. // dereferensi untuk mengakses nilai x via pointer px printf("\nNilai *px = %d", *px); // dereferensi untuk mengubah nilai x via pointer px *px = 5; Dua makna yang berbeda dari simbol * ini sangat penting untuk diperhatikan dan diingat karena sering disalahpahami.
1.7 Void Pointer Void pointer adalah pointer dengan tipe khusus. Dalam C/C++ void melambangkan ketiadaan tipe data, sehingga void pointer adalah pointer yang menunjuk pada nilai yang tidak bertipe data.
Dengan demikian void pointer dapat menunjuk pada data dari sembarang tipe, seperti integer, float, double, char atau string. Tetapi void pointer ini memiliki keterbatasan. Karena pointer ini bersifat ‘void’, data yang ditunjuknya tidak dapat secara langsung didereferensikan. Agar dapat didereferensikan, pointer ini (atau alamat yang tersimpan di dalamnya) harus dikonversi paksa (dicast ) dahulu menjadi pointer yang dapat menunjuk pada tipe data kongkrit. Untuk memahami konsep void pointer dan proses casting di atas, kerjakan dan analisis kode di bawah ini. 1 #include <stdio.h> 2 #include <stdlib.h> 3 4 int main(){ 5 void* pv; // Pointer terhadap void 6 int* pi; // Pointer terhadap integer 7 double* pd; // Pointer terhadap double 8 int ivalue = 100; 9 double dvalue = 50.55; 10
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 }
// pv dapat menunjuk pada variabel bertipe data integer pv = &ivalue; printf("pv=%d, sizeof(pv)=%d\n",pv, sizeof(pv)); // pernyataan ini salah krn pv tidak dapat didereferensikan printf("*pv=%d, sizeof(*pv)=%d\n",*pv, sizeof(*pv)); // pv dapat menunjuk pada variabel bertipe data double pv = &dvalue; printf("pv=%d, sizeof(pv)=%d\n",pv, sizeof(pv)); // pernyataan ini salah krn pv tidak dapat didereferensikan printf("*pv=%.2lf, sizeof(*pv)=%d\n",*pv, sizeof(*pv)); // pd menerima hasil casting dari pv pd = (double*)pv; printf("pd=%d, sizeof(pd)=%d\n",pd, sizeof(pd)); // pernyataan ini benar, pd dapat didereferensikan printf("*pd=%.2lf, sizeof(*pd)=%d\n",*pd, sizeof(*pd)); system("pause"); return 0; Gambar 1.7
1.8 Null Pointer Sebuah null pointer adalah sebuah pointer yang memiliki nilai khusus yang menununjukkan bahwa pointer tersebut tidak menyimpan alamat memori manapun. Nilai ini dihasilkan dari penugasan nilai NULL atau 0 ke sebuah pointer. Contoh: int *p; p = NULL; // atau p = 0; Null pointer berbeda dengan void pointer. Null pointer adalah pointer yang tidak menunjuk pada alamat memori manapun, sedangkan void pointer adalah pointer yang dapat menunjuk pada sebuah alamat memori tanpa tipe data tertentu. Istilah null diasosiasikan dengan nilai yang disimpan pada pointer, sedangkan void mengacu pada tipe data yang ditunjuk oleh pointer.
11
1.9 Aritmetika Pointer Operasi aritmetika dapat juga diterapkan pada pointer, tetapi dengan lebih banyak keterbatasan daripada operasi aritmetika pada tipe data integer, float, atau, double. Hanya penjumlahan dan pengurangan yang berlaku pada operasi aritmetika pointer. Perilaku kedua operasi ini bergantung pada ukuran tipe data yang ditunjuk oleh pointer yang bersangkutan. Dalam bahasa C/C++ tipe-tipe data yang berbeda dapat memiliki ukuran yang berbeda pula tergantung pada kompilator (compiler) dan jenis platform target programnya. Sebagai contoh, dengan kompilator dan platform tertentu, char akan berukuran 1 byte, short 2 bytes, dan long 4 bytes. Misalkan kita mendefiniskan 3 buah pointer berikut: char *mychar; short *myshort; long *mylong;
dan kita ketahui bahwa pointer-pointer tersebut masing-masing menunjuk pada alamat 1000, 2000 and 3000 pada memori. Lalu kita tuliskan: mychar++; //ekivalen mychar +=1; myshort++; //ekivalen myshort +=1; mylong++; //ekivalen mylong +=1;
Setelah itu mychar akan bernilai 2002 (bukan 1001), myshort bernilai 2002 (bukan 2001), dan mylong 3004 (bukan 3001), walaupun masing-masing hanya ditambah dengan 1. Alasannya adalah penambahan satu tersebut menyebabkan masing-masing pointer menunjuk pada elemen yang bertipe data sama pada lokasi memori berikutnya. Jadi, penambahan satu di sini bermakna perpindahan 1 unit tipe data pada elemen berikutnya, seperti yang diilustrasikan pada gambar di bawah ini.
Gambar 1.8 12
1.10
Pointer sebagai Parameter Formal Fungsi
Pointer dapat dimanfaatkan sebagai parameter formal dalam sebuah fungsi. Pointer ini akan menyimpan alamat dari variabel yang dilewatkan sebagai parameter aktual pada pemanggilan fungsi tersebut. Dengan demikian nilai variabel yang bersangkutan dapat diubah di dalam fungsi melalui proses dereferensi. Mekanisme pengiriman alamat variabel sebagai parameter pada fungsi disebut dengan passing by reference. Sebagai contoh, kerjakan dan amati kode di bawah ini. Kode ini menyediakan 2 buah fungsi, masing-masing untuk konversi nilai dari standar satuan metrik (cm) ke standar satuan Inggris (inch) dan sebaliknya. Penggunaan pointer sebagai parameter fungsi dapat diamati pada definisi fungsi inchToCm() di baris ke-7 sampai ke-9. Contoh passing by reference dapat dilihat pada baris ke-21. Bandingkanlah dengan mekanisme passing by value pada baris ke-17, menggunakan fungsi yang didefinisikan pada baris ke-4 sampai ke-6. 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
#include <stdio.h> #include <stdlib.h> double cmToInch(double a){ return a*0.393700787; } void inchToCm(double* a){ *a *= 2.54; } int main(){ // Panjang dalam cm double panjang=15; printf("panjang: %.2lf cm\n",panjang); // Konversi dari cm ke inch, passing by value panjang = cmToInch(panjang); printf("panjang: %.2lf inch\n",panjang); // Konversi kembali dari inch ke cm, passing by reference inchToCm(&panjang); printf("panjang: %.2lf cm\n",panjang);
}
system("PAUSE"); return 0; Gambar 1.9
13
1.11
Tugas
Tugas yang relevan dengan topik bab ini diberikan oleh dosen/asisten praktikum.
Tugas bab ini dapat dikombinasikan dengan tugas bab lain atas arahan dan ijin dosen/asisten praktikum.
Tugas bab ini dikumpulkan kepada dosen/asisten sesuai format yang diminta.
14
2 BAB 2: Array Pokok bahasan: Array satu dimensi o Deklarasi o Inisialisasi o Akses o Array dan pointer o Array sebagai parameter fungsi Array dua dimensi o Deklarasi o Inisialisasi o Akses o Array sebagai parameter fungsi
15
2.1 Pendahuluan Array (kadang-kadang diterjemahkan sebagai “larik”) adalah serangkaian elemen-elemen yang berasal dari tipe data yang sama, disusun secara berurutan di dalam memori, dan masing-masing dapat diakses dengan menambahkan indeks pada nama array tersebut. Tipe data dari elemen array dapat berupa tipe data sederhana maupun tipe data kompleks. Setiap elemen array mempunyai nomor indeks yang unik dan disimpan secara berurutan di dalam memori. Array dapat berdimensi satu, dua, tiga, ataupun lebih, sesuai dengan kebutuhan pemrogram.
2.2 Array Satu Dimensi 2.2.1 Deklarasi Array Satu Dimensi Penamaan array harus mengikuti aturan-aturan penamaan variabel. Seperti halnya variabel sederhana, array pun harus dideklarasikan dahulu sebelum digunakan. Deklarasi array berisi tipe data array, nama array, dan jumlah elemen yang dimilikinya. Sintaksis deklarasi array adalah sebagai berikut: tipedataelemen namaarray[jumlahelemen]; Contoh deklarasi array bernama A dengan 5 elemen integer: int A[5]; Dalam bahasa C/C++, elemen pertama suatu array diberi indeks nol, elemen kedua diberi indeks satu, dan seterusnya. Array billy dapat digambarkan sebagai berikut: A A[0] int
A[1] int
A[2] int
A[3] int
A[4] int
Gambar 2.1 Dalam deklarasi yang tidak langsung menyertakan inisialisasi array, jumlah elemen dalam array haruslah didefinisikan. Sebagai contoh, pernyataan berikut tidaklah valid. int A[]; // invalid, compile error
2.2.2 Inisialisasi Array Satu Dimensi Setelah dideklarasikan, array dapat didefinisikan atau diisi dengan nilai-nilai. Inisialisasi array dapat dilakukan dengan beberapa cara berikut. 16
1. Inisialisasi dalam satu pernyaatan dengan deklarasi Contoh: int Ai[4] = {2,4,5,7}; float Af[] = {2.2,4,6.5,8}; char Ac[] = {‘i’,’a’}; int B[4] = {1,3}; Sebagai catatan, jika jumlah elemen array dalam kurung tegak [] di sebelah kiri tanda penugasan (=) dideklarasikan, maka jumlah elemen ini harus sama dengan atau lebih besar daripada jumlah dari elemen yang dinisialisaskikan dalam kurung kurawal {} di sebelah kanan tanda penugasan. Hal ini terlihat pada contoh inisialisasi array Ai dan B di atas. Sementara itu, jika jumlah elemen array dalam kurung tegak [] di sebelah kiri tanda penugasan (=) tidak dideklarasikan, maka jumlah elemen array-nya adalah jumlah dari elemen yang dinisialisasikan dalam kurung kurawal {} di sebelah kanan tanda penugasan. Contohnya adalah pada inisialisasi array Af dan Ac di atas. Jika jumlah elemen array dalam kurung tegak [] di sebelah kiri tanda penugasan (=) lebih besar daripada jumlah dari elemen yang dinisialisasikan dalam kurung kurawal {} di sebelah kanan tanda penugasan, maka jumlah elemen yang valid adalah yang di sebelah kiri tanda penugasan. Elemen yang tidak tertulis akan by default diber nilai nol (0). Contohnya adalah pada inisialisasi array B di atas. Elemen-elemen dari B adalah 1, 3, 0, dan 0. Ai
2 Ai[0] Int
4 Ai[1] int
5 Ai[2] int
7 Ai[3] int
Af
2.2 Af[0] Float
4 Af[1] float
6.5 Af[2] float
8 Af[3] float
Ac
'i' Ac[0] Char
'a' Ac[1] char
Ai
1 Ai[0] int
3 Ai[1] int
0 Ai[2] int
0 Ai[3] int
Gambar 2.2 2. Inisialisasi elemen-elemen array secara individu melalui indeksnya masing-masing 17
Sintaksis pengisian nilai ke dalam indeks tertentu dari sebuah array: namaarray[indeksarray] = nilai; Contoh: double Ad[3]; // deklarasi array Ad[0] = 3.3; // inisialisasi elemen dari Ad pada indeks ke-0 Ad[1] = 1.5; // inisialisasi elemen dari Ad pada indeks ke-1 Ad[2] = 2.43; // inisialisasi elemen dari Ad pada indeks ke-2 Ad
3.3 Ad[0] double
1.5 Ad[1] double
2.43 Ad[2] double
Gambar 2.3 Jika nilai-nilai elemen array memiliki pola tertentu, inisialisasinya dapat memanfaatkan mekanisme seleksi dan/atau perulangan. Contohnya, array X yang berisi bilangan bulat antara 1 dan 5, inklusif. Deklarasi dan inisialisasi tersebut adalah sebagai berikut: int X[5]; for(int i=0; i<5; i++){ X[i]=i+1; }
2.2.3 Akses array satu dimensi Setelah sebuah array didefinisikan, elemen array dapat diakses dengan cara memanggil nama dan nomor indeksnya, seperti ekspresi berikut ini: namaarray[indeksarray] Sebagai contoh, lihat pernyataan berikut: int Ai[4] = {2,4,5,7}; // deklarasi Ai printf(“Elemen pada indeks ke-2 dari Ai: %d\n”, Ai[2]);//akses A[2] Contoh lainnya, sebuah array S yang tiap elemennya dicetak ke monitor dapat ditulis sebagai berikut: char S[]={‘s’,’a’,’l’,’a’,’m’}; for(int i=0; i<5; i++){ printf(“Elemen pada indeks ke-%d dari S: %c\n”,i,S[i]); }
18
2.2.4 Array dan Pointer Array memiliki hubungan erat dengan pointer. Dalam banyak kesempatan, array dikatakan “ekivalen” dengan pointer. Ekivalen di sini tidak berarti sama persis. Keduanya berbeda, tetapi dapat digunakan secara berkaitan. Perhatikan contoh berikut: int A[4] = {2,4,5,7}; // deklarasi A Seperti telah diketahui, elemen pada indeks ke-0 dan ke-1 dalam Ai dapat diakses dan dimodifikasi dengan cara berikut: A[0]=3; A[1]=10; Dengan ekivalensi antara array dan pointer, kedua elemen di atas dapat diakses dan dimodifikasi dengan cara berikut: *A=3; *(A+1)=10; Hal ini dapat terjadi karena variabel array A dapat dianggap sebagai sebuah variabel pointer A. Sebagai sebuah pointer, A berisikan alamat elemen pertama (indeks ke-0) dari array A, yaitu A[0]. Untuk lebih memahami hubungan pointer dan array, kerjakan dan analisis kode pada gambar 2.4 berikut. 1 #include <stdio.h> 2 #include <stdlib.h> 3 4 int main(){ 5 // Deklarasi dan inisialisasi A 6 int A[4]={2,4,5,7}; 7 8 // Akses dan modifikasi dengan 'cara array' 9 A[0]=10; A[1]=20; 10 11 // Akses dan modifikasi dengan 'cara pointer' 12 *(A+2)=30; *(A+3)=40; 13 14 // Tampilkan nilai A, alamat A, alamat A[0] 15 printf("A: %d, &A: %d, &A[0]: %d\n\n",A,&A,&A[0]); 16 17 // Cetak tiap elemen ke monitor dengan 'cara array' 18 for(int i=0; i<4; i++){ 19 printf("Elemen pada indeks ke-%d dari A: %d\n",i,A[i]); 20 printf("Alamat elemen pada indeks ke-%d dari A: %d\n", 21 i,&A[i]); 22 } 23 24 // Deklarasi pointer variabel 19
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 }
int *p; // Inisialisasi p dengan A p = A; // Cetak tiap elemen ke monitor dengan 'cara array' for(int i=0; i<4; i++){ printf("Elemen pada indeks ke-%d dari p: %d\n",i,p[i]); } // Pernyataan di bawah ini invalid, A adalah pointer konstan // A = p; system("PAUSE"); return 0; Gambar 2.4
Perhatikan bagaimana dua cara yang berbeda untuk mengisikan nilai pada elemen array (baris ke9 dan ke-12) ternyata memberikan efek yang sama. Perhatikan juga baris ke-17 sampai ke-22. Cobalah untuk membuat kode yang memiliki tujuan yang sama dengan yang berada pada baris ini, tetapi dengan ‘cara pointer’. Kemudian lihatlah baris ke-24 sampai selesai. Di sana sebuah pointer p dideklarasikan, diinisialisasi dengan A, dan seluruh elemennya ditampilkan menggunakan ‘cara array’. Hal ini menunjukkan bahwa p memiliki tipe data yang kompatibel dengan A. Ketika baris ke-27 dieksekusi, hubungan p dan A dapat diilustrasikan sebagai berikut:
Gambar 2.5 Sekarang perhatikan baris ke-35. Coba hilangkan tanda // sehingga pernyataan A = p dikompilasi. Perhatikan hasilnya. Walaupun A dapat dilihat sebagai pointer, tetapi dia tidak dapat ditugasi (assigned) atau diisikan dengan nilai pointer lain. Sekali array dideklarasikan, pointernya akan selalu menunjuk pada elemen yang sama, yaitu elemen pertama (indeks ke-0) dari array tersebut, tidak dapat diubah ke alamat memori lain. Dengan demikian variabel array disebut juga sebagai sebuah pointer konstan. Amati juga keluaran program ini oleh baris ke-15. Dari pengamatan nilai A dan alamat A[0], terlihat bahwa A dapat dilihat sebagai pointer yang menunjuk pada atau berisi alamat A[0]. Ada 20
hal lain yang menarik di sini. Alamat A ternyata juga memiliki nilai yang sama dengan nilai A dan alamat A[0]. Hal ini mungkin nampak membingungkan. Masalah ini tidak akan dibahas lebih dalam di sini. Kesimpulan sementara yang dapat ditarik adalah bahwa C/C++ memang memperlakukan array seperti ini untuk mendukung konsep bahwa array bisa dilihat sebagai sebuah pointer dan dia adalah pointer konstan.
2.2.5 Array sebagai Parameter Fungsi Array dapat dikirimkan sebagai parameter sebuah fungsi. Untuk bisa menerima parameter array ini, fungsi itu perlu didefinisikan dengan parameter formal pointer yang menunjuk pada tipe data yang sesuai dengan tipe data elemen array tersebut. Dengan kata lain, array tersebut harus dikirim by reference. Prototipe fungsi tersebut dituliskan dalam sintaksis berikut: tipedatakembalian namafungsi(tipedata *parameterformal); atau tipedatakembalian namafungsi(tipedata parameterformal[]); atau tipedatakembalian namafungsi(tipedata parameterformal[jumlahelemen]); Gambar berikut mencontohkan penggunaan array sebagai parameter fungsi. 1 2 3 4 5 6 7 8 9 10 11 12 13
#include <stdio.h> #include <stdlib.h> void cetakElemen(int *A, int index){ printf("Elemen pada indeks ke-%d: %d\n",index,A[index]); } int main(){ int X[]={7,8,9,10}; cetakElemen(X,2); system("pause"); return(0); } Gambar 2.6
2.3 Array Multidimensi Selain berdimensi satu, array juga dapat bermultidimensi. Array multidimensi disebut juga ‘array of arrays’. Contoh array multidimensi adalah array dua dimensi.
21
2.3.1 Deklarasi Array Dua Dimensi Deklarasi array dua dimensi terdiri dari tipe data array, nama array, jumlah elemen pada dimensi pertama, dan jumlah elemen pada dimensi kedua. Sintaksis deklarasi array adalah sebagai berikut: tipedataelemen namaarray[jumlahelemendimensi1][jumlahelemendimensi2]; Array dua dimensi sering dimodelkan sebagai sebuah matriks dua dimensi yang memiliki baris dan kolom. Dimensi pertama array tersebut direpresentasikan sebagai baris dan dimensi kedua sebagai kolom. Contoh deklarasi sebuah array dua dimensi: int A[2][3]; // array dengan 2 baris dan 3 kolom Array A ini dapat dibayangkan seperti sebuah matriks pada gambar 2.7. Baris 0 Baris 1
Kolom 0 A[0][0] A[1][0]
Kolom 1 A[0][1] A[1][1]
Kolom 2 A[0][2] A[1][2]
Gambar 2.7 Walaupun array dua dimensi ini bisa dilihat sebagai matriks dua dimensi, tetapi dalam memori elemen-elemen array ini tersusun secara linier dengan urutan seperti pada gambar 2.8. nama elemen A[0][0] A[0][1] A[0][2] A[1][0] A[1][1] A[1][2]
nilai
alamat 2293520 2293524 2293528 2293532 2293536 2293540
Gambar 2.8
2.3.2 Inisialisasi Array Dua Dimensi Sebagaimana array satu dimensi, inisialisasi array dua dimensi dapat dilakukan dengan beberapa cara berikut. 1. Inisialisasi dalam satu pernyaatan dengan deklarasi Contoh: int A[3][4] = {{2,4,5,5},{7,9,11,5},{0,2,2,1}}; int B[2][3] = {3,5,7,9,0,4}; int C[2][3] = {1,10,5,6}; 22
int D[3][2] = {{1},{10,5},{6}}; Aturan pengisian nilai elemen dalam array dua dimensi mengikuti aturan “row major order” (RMO). Untuk mengetahui bagaimana RMO bekerja untuk setiap array di atas, perhatikan ilustrasi untuk definisi A, B, C, dan D di bawah ini, lalu ambil kesimpulannya. int A[3][4] = {{2,4,5,5},{7,9,11,5},{0,2,2,1}}; A Baris 0 Baris 1 Baris 2
Kolom 0 2 7 0
Kolom 1 4 9 2
int B[2][3] = {3,5,7,9,0,4}; B Kolom 0 Baris 0 3 Baris 1 9
Kolom 2 5 11 2
Kolom 1 5 0
Kolom 3 5 5 1
Kolom 2 7 4
Jadi int B[2][3] = {3,5,7,9,0,4}; ekivalen dengan
int B[2][3] = {{3,5,7},{9,0,4}}; int C[2][3] = {1,10,5,6}; C Kolom 0 Baris 0 1 Baris 1 6
Kolom 1 10 0
Kolom 2 5 0
Jadi int C[2][3] = {1,10,5,6}; ekivalen dengan int C[2][3] = {1,10,5,6};
int D[3][2] = {{1},{10,5},{6}}; D Kolom 0 Baris 0 1 Baris 1 10 Baris 2 6
Kolom 1 0 5 0
Jadi, int D[3][2] = {{1},{10,5},{6}}; ekivalen dengan int D[3][2] = {{1},{10,5},{6}};
2. Inisialisasi elemen-elemen array secara individu melalui indeksnya masing-masing
23
Sintaksis pengisian nilai ke dalam indeks tertentu dari sebuah array: nama_array[indeks_pada_dimensi1][indeks_pada_dimensi2]=nilai; Contoh: double A[2][3]; A[0][0] = 3.3; A[1][1] = 1.5; A[2][0]= 2.43; // dst.
// // // //
deklarasi array inisialisasi A[0][0] inisialisasi A[1][1] inisialisasi A[2][0]
Jika nilai-nilai elemen array memiliki pola tertentu, inisialisasinya dapat memanfaatkan mekanisme seleksi dan/atau perulangan. Contohnya, deklarasi dan inisialisasi array 2x2 B yang berelemen integer dengan nilai masing-masing 0,0,1, dan 1, sebagai berikut: int B[2][2]; for(int i=0; i<2; i++){ for(int j=0; j<2, j++){ B[i][j]=i; } }
2.3.3 Akses Array Dua Dimensi Setelah sebuah array dua dimensi didefinisikan, elemen array dapat diakses dengan cara memanggil nama dan nomor indeks sesuai dimensinya, seperti ekspresi berikut ini: namaarray[indekspadadimensi1][indekspadadimensi2] Sebagai contoh, lihat pernyataan berikut: int A[2][3] = {2,4,5,7}; // deklarasi A printf(“A[2][3]: %d\n”, A[2][3]); // akses A[2][3] Contoh lainnya, sebuah array B yang tiap elemennya dicetak ke monitor dapat ditulis sebagai berikut: char B[2][2]={1,1,5,2,4}; for(int i=0; i<2; i++){ for(int j=0; j<2;i++){ printf(“B[%d][%d]:%d\n”,i,j,B[i][j]); }
2.3.4 Array Dua Dimensi sebagai Parameter Fungsi Seperti pada array satu dimensi, pengiriman parameter array dua dimensi pada fungsi juga dilakukan by reference. Prototipe fungsi tersebut dapat dituliskan dalam sintaksis berikut: tipedatakembalian namafungsi(tipedata **parameterformal); atau 24
tipedatakembalian namafungsi(tipedata parameterformal[][jumlahelemen]); atau tipedatakembalian namafungsi(tipedata parameterformal[jumlahelemen] [jumlahelemen]); Gambar 2.9 berikut mencontohkan penggunaan array sebagai parameter fungsi. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
#include <stdio.h> #include <stdlib.h> void cetak(int A[2][3]){ for(int i=0; i<2; i++){ for(int j=0; j<3; j++){ printf("A[%d][%d]=%d\n",i,j,A[i][j]); } } } int main(){ int Y[2][3]={1,2,3,4}; cetak(Y); system("pause"); return(0); } Gambar 2.9
2.4 Tugas Tugas yang relevan dengan topik bab ini diberikan oleh dosen/asisten praktikum. Tugas bab ini dapat dikombinasikan dengan tugas bab lain atas arahan dan ijin dosen/asisten praktikum. Tugas bab ini dikumpulkan kepada dosen/asisten sesuai format yang diminta.
25
3 BAB 3: Pointer Lanjutan - Alokasi Memori Dinamik Pokok bahasan: Alokasi memori dengan malloc dan new Dealokasi memori dengan free dan delete Realokasi memori dengan realloc
26
3.1 Pendahuluan Pada bab sebelumnya telah dijelaskan tentang konsep dasar pointer dan bagamaina mendeklarasikan dan mendefinisikannya dengan cara referensi. Contoh-contoh yang diberikan setelah itu untuk inisialisasi atau pendefinisian pointer hanya menggunakan cara referensi, yaitu membuat atau mengubah nilai pointer itu sehingga menunjuk ke variabel yang sudah ada sebelumnya. Sesungguhnya ini hanya salah satu cara, masih ada yang lainnya. Kita dapat membuat pointer tersebut menunjuk ke lokasi data yang baru yang belum dimiliki oleh variabel manapun. C/C++ menyediakan fasilitas untuk mengaplikasikan cara kedua di atas.
3.2 Pengalokasian Memori Secara Dinamik Proses membuat pointer menunjuk ke lokasi data yang baru yang belum dimiliki oleh variabel manapun dinamakan alokasi memori dinamik dan pointernya sendiri sering disebut dengan pointer dinamik. Kondisi ini disebut dinamik karena dua hal. Pertama, jumlah rangkaian memori yang dialokasikan dapat diubah oleh pemrogram atau berdasarkan masukan pengguna, selama memorinya tersedia. Dengan demikian kita dapat membuat semacam array ‘dinamik’ yang jumlah elemennya dapat diubah-ubah, mengatasi keterbatasan array konstan yang jumlah elemennya selalu tetap. Kedua, pengalokasian memori ini dilakukan saat eksekusi program atau run-time. Hal ini memungkinkan kita untuk meminta pengguna memasukkan jumlah elemen dari array ‘dinamik’ tersebut. Konsep yang akan dibahas berikutnya adalah fasilitas C/C++ yang dapat digunakan untuk implementasi alokasi memori dinamik
3.2.1 Fungsi malloc Untuk mengalokasikan memori secara dinamik kita dapat menggunakan fungsi malloc yang terdapat pada pustaka standar C. Prototipe fungsi ini memiliki sintaksis berikut: void * malloc(size_t size);
Parameter: Parameter fungsi ini (size) adalah ukuran blok memori dalam bytes yang dipesan ke malloc. Kembalian: Jika pemesanan memori sukses, fungsi ini mengembalikan sebuah pointer yang menunjuk pada awal blok tersebut. Isi dari blok-blok tersebut tidak diinisialisasikan (tidak terdefinisi).
27
Tipe data kembalian fungsi ini adalah void*, yang dapat di-cast (dikonversi paksa) ke tipe data yang bisa didereferensikan. Jika alokasi memori gagal, sebuah null pointer akan dikembalikan. Perhatikan contoh penggunaan malloc pada gambar 3.1 di bawah 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 30 31 32 33
#include <stdio.h> #include <stdlib.h> int main(){ int *A, *B; A=(int*)malloc(sizeof(int)); if(A!=NULL){ *A = 100; printf("*A=%d\n",*A); } else{ printf("Alokasi memori gagal."); }
}
B=(int*)malloc(5*sizeof(int)); if(B!=NULL){ for(int i=0; i<5; i++){ *(B+i) = i; printf("*(B+%d)=%d\t",i,*(B+i)); printf("B[%d]=%d\n",i,B[i]); } } else{ printf("Alokasi memori gagal."); } free(A); A = NULL; free(B); B = NULL; system("pause"); return 0; Gambar 3.1
Pada contoh di atas dua buah pointer dideklarasikan kemudian dialokasikan memori yang ditunjuk oleh mereka. Perhatikan baris ke-7 dan ke-16, lalu amati perbedaan dari keduanya. Ambil kesimpulan dari perbedaan tersebut dan bandingkanlah dengan gambar 3.2 di bawah ini.
28
Gambar 3.2 Perhatikan juga pernyataan kondisional pada baris ke-7 sampai ke-15, dan pada baris ke-16 sampai ke-22. Pikirkanlah kenapa pernyataan kondisional ini diperlukan. Cobalah amati baris ke-20 dan ke-21 berikut keluarannya pada monitor. Ambillah kesimpulan dari kedua pernyataan pada baris-baris tersebut.
3.2.2 Operator new Operator new adalah operator dalam C++ (bukan C) yang memiliki kegunaan yang sama dengan malloc. Operator ini memiliki sintaksis penggunaan yang lebih singkat daripada milik malloc, seperti berikut: namapointer = new tipedata; atau namapointer = new tipedata[jumlahelemen]; Cobalah untuk mengubah kode pada gambar 3.1 untuk alokasi memori dinamik dari malloc menjadi new.
3.3 Dealokasi atau Pembebasan Memori Memori yang dialokasikan dengan malloc atau new dapat bertahan sampai program usai. Selama itu, walaupun sudah tidak digunakan lagi, memori tersebut tidak dibebaskan secara otomatis oleh program atau sistem operasi. Pemrogram harus membebaskan memori itu secara eksplisit. C/C++ menyediakan sarana ini dengan fungsi free (C) dan operasi delete (C++) yang akan dijelaskan sebentar lagi. Sebagai catatan, kebanyakan program menggunakan memori untuk keperluan singkat. Suatu saat dia dibutuhkan, setelah itu tidak lagi. Karena kapasitas memori terbatas, sebaiknya memori yang sudah tidak dipakai dibebaskan agar dapat digunakan untuk operasi yang lainnya. 29
3.3.1 Fungsi free Protitpe fungsi free memiliki bentuk sebagai berikut: void free(void * ptr); Parameter: Parameter fungsi ini (ptr) adalah pointer yang menunjuk pada blok memori yang akan dibebaskan dan sebelumnya dialokasikan dengan malloc atau fungsi lainnya, . Jika ptr bernilai NULL, maka fungsi ini tidak melakukan apa-apa. Kembalian: Fungsi ini tidak memberikan nilai kembalian. Catatan: Walaupun fungsi ini membebaskan blok memori yang ditunjuk oleh ptr, nilai ptr tetap sama dengan sebelumnya (alamat memori yang sudah dibebaskan), bukan NULL. Blok memori bebas sudah dapat digunakan oleh operasi lain. Mungkin ini agak membingungkan, tetapi seperti inilah implementasi fungsi free pada bahasa C. Untuk mencegah kesalahan dalam penggunan ptr berikutnya (agar ptr tidak dikira bernilai NULL), setelah operasi free tuliskan: ptr = NULL; atau ptr = 0; Contoh penggunaan fungsi ini dapat dilihat pada baris ke-27 sampai ke-30 pada gambar 3.1 sebelumnya.
3.3.2 Operator delete Operator ini memiliki fungsi yang sama dengan free pada C. Sintaksis penggunaan operator ini adalah: /* untuk pointer yang menunjuk pada satu blok memori yang berisi 1 unit tipe data tertentu */ delete ptr; atau /* untuk pointer yang menunjuk pada satu blok memori yang berisi lebih dari 1 unit tipe data tertentu*/ delete [] ptr; 30
Cobalah untuk mengubah kode pada gambar 3.1 untuk pembebasan memori dinamik, dari free menjadi delete.
3.4 Realokasi Memori Sebuah pointer yang sudah dialokasikan dengan malloc atau fungsi lainnya dapat direalokasikan kembali untuk mengubah ukuran dari memori yang ditunjuknya. Fungsi yang dapat digunakan adalah realloc dan bentuk dari prototipenya adalah sebagai berikut: void * realloc(void * ptr, size_t size); Parameter: ptr: Pointer yang akan direalokasikan dan sebelumnya menunjuk pada blok memori yang dialokasikan dengan malloc atau fungsi lainnya. Jika ptr bernilai NULL, maka fungsi ini akan mengalokasikan blok memori baru dan pointer yang menunjuk pada blok ini dikembalikan oleh fungsi ini. size: Ukuran baru memori yang akan dialokasikan dalam bytes. Jika size bernilai 0, maka blok memori yang sebelumnya dialokasikan untup ptr akan dibebaskan dan sebuah NULL pointer akan dikembalikan oleh fungsi ini. Kembalian: Fungsi ini mengembalikan sebuah pointer yang menunjuk pada blok memori yang telah direalokasikan. Blok memori ini mungkin berlokasi sama dengan yang sebelumnya atau berada pada lokasi baru. Tipe pointer ini adalah void*, yang dapat di-cast (dikonversi paksa) ke tipe data pointer yang diinginkan agar dapat didereferensi. Isi dari blok memori yang lama akan dipertahankan sampai sejumlah ukuran blok memori yang baru. Jika ukuran blok memori yang baru lebih besar dari yang lama, isi dari bagian memori yang baru ini tidak terdefinisi. Jika Jika fungsi ini gagal merealokasikan blok memori yang diminta, sebuah NULL pointer akan dikembalikan dan ptr tetap menunjuk pada blok memori yang lama. Sebagai contoh aplikasi fungsi realloc, cobalah untuk menambahkan kode di antara baris ke-26 dan ke-27 pada gambar 3.1 yang merealokasikan pointer A dan B masing-masing ke 5 unit memori baru sesuai tipe data masing-masing. Selanjutnya isikan blok memori baru dari A dengan 5 bilangan ganjil mulai dari bilangan 1 dan blok memori baru dari B dengan 5 bilangan genap mulai dari bilangan 2. Tampilkan seluruh isi blok memori dari A dan B ke layar monitor. 31
3.5 Tugas
Tugas yang relevan dengan topik bab ini diberikan oleh dosen/asisten praktikum.
Tugas bab ini dapat dikombinasikan dengan tugas bab lain atas arahan dan ijin dosen/asisten praktikum.
Tugas bab ini dikumpulkan kepada dosen/asisten sesuai format yang diminta.
32
4 BAB 4: String Pokok bahasan: Array of characters Deklarasi string Inisialisasi dan pendefinisian string Penanganan string
33
4.1 Pendahuluan Sebuah string pada dasarnya adalah serangkaian karakter yang disusun berurutan. Sementara itu karakter sendiri adalah simbol yang diambil dari satu set alphabet tertentu. Dalam bahasa C, string ini dapat diimplementasikan dalam array yang berlemen karakter alias array of characters dengan serangkaian karakter yang diakhiri dengan karakter null. Selanjutnya untuk mendeklarasikan dan mendefinisikan string, ada beberapa cara yang dapat dilakukan, seperti yang akan dibahas dalam sub bab berikutnya. Dalam bahasa C++, string sudah diimplementasikan sebagai sebuah tipe data tersendiri yang didefinisikan dalam pustaka standar C++. String dengan model ini berada di luar bahasan bab ini.
4.2 Deklarasi String 4.2.1 Deklarasi via Array of Characters Sebagai array of characters, string dapat dideklarasikan dengan sintaksis sebagai berikut: tipedatachar namastring[jumlahelemen]; Sebagai contoh, untuk mendeklarasikan sebuah string bernama str yang akan memiliki maksimum 15 buah karakter (termasuk karakter null), pernyataannya adalah: char str[5]; Saat pernyataan ini dijalankan, str dialokasikan 5 unit memori bertipe char seperti diilustrasikan pada gamber 4.1 berikut. Cara menginisialisasi string ini akan dibahas pada sub bab berikutnya.
str str[0] char
str[1] char
str[2] Char
str[3] char
str[4] char
Gambar 4.1
4.2.2 Deklarasi via Pointer Seperti telah diketahui, array adalah pointer konstan. Variabel bertipe array menyimpan alamat dari elemennya yang pertama (indeks ke-0) sejak dideklarasikan sampai seterusnya. Selain itu, array juga memiliki ‘ekivalensi’ dengan pointer umumnya (sub bab 2.2.4). Karena kedekatan ini, sebuah pointer to character dapat dideklarasikan untuk menunjuk pada sebuah string dengan cara referensi ke array string yang ada maupun dengan alokasi memori dinamik. 34
Referensi Proses ini sebenarnya tidak sepenuhnya merupakan deklarasi sebuah string. Hal ini karena string yang ditunjuk oleh pointer yang bersangkutan sebenarnya sudah dideklarasikan sebelumnya via array of characters. Di bawah ini adalah contoh deklarasi sebuah pointer yang kemudian diset untuk menunjuk pada sebuah string yang belum terdefinisi. char str[5]; char * p; p = str; str[0]
char str[5];
str[1] str[2] str[3] str[4]
char *p;
p
p = str;
p
str[0] str[1] str[2] str[3] str[4]
Gambar 4.2 Inisialisasi string ini akan dibahas pada sub bab selanjutnya. Alokasi memori dinamik Untuk mengalokasi memori secara dinamik, kita dapat menggunakan fungsi malloc atau fungsi lain yang relevan (lihat bab 3). Memori yang dialokasikan akan digunakan untuk menampung karakter pada string yang bersangkutan. Berikut contoh deklarasi pointer to char and pengalokasian memorinya untuk membuat sebuah string. char * p; p = (char*)malloc(5*sizeof(char)); Proses ini dapat diilustrasikan sebagai berikut:
35
char *p;
p
p=(char*)malloc(5*sizeof(char)); p // jika alokasi memori berhasil
p[0] p[1] p[2] p[3] p[4]
Gambar 4.3 Inisialisasi string ini akan dibahas pada sub bab selanjutnya.
4.3 Inisialisasi dan Pendefinisian String Setelah dideklarasikan, string dapat diinisialisasikan atau didefinisikan dengan beberapa cara berikut.
4.3.1 Inisialisasi via Array 1. Inisialisasi dalam satu pernyaatan dengan deklarasi Contoh: char s1[] = {‘h’,’i’,’\0’}; char s2[8] = {‘h’,’e’,’l’,’l’,’o’,’\0’}; char s3[] = “salam”; Inisialisasi s1 dan s2 adalah inisialisasi dengan cara yang ‘primitif’, yaitu dengan menuliskan setiap karakter satu per satu dalam {}. Aturan array untuk penulisan jumlah elemen array (lihat bab 2) tetap berlaku. Yang harus diingat juga adalah bahwa sebuah string (yang valid) harus diakhiri dengan karakter null atau karakter nol (‘\0’).
36
Gambar 4.4 Perhatikan hasil inisialisasi s2 pada gambar 4.4. Di sini s2 diinisialisi dengan 6 karakter tertulis (termasuk karakter null), tetapi sebenarnya s2 tetap memiliki 8 karakter seperti deklarasinya. Sisa karakter yang tidak didefinisikan menempati s2[6] dan s2[7] digambarkan dengan kotak yang diarsir, biasanya diisikan dengan nilai default karakter null. Sementara itu, s3 diinisialisasi dengan konstanta atau literal string “salam”. Inisialisasi ini ekivalen dengan pernyataan berikut: char s3[] = {‘s’,’a’,’l’,’a’,’m’,’\0’}; Jika diilustrasikan, inisialisasi ini akan menghasilkan kondisi berikut:
Gambar 4.5 2. Inisialisasi elemen-elemen array secara individu melalui indeksnya masing-masing Cara ini juga masih dianggap cara yang ‘primitif’. Sintaksis pengisian nilai ke dalam indeks tertentu dari sebuah array string: namastring[indeksarray] = nilai; Contoh: char str[3]; // deklarasi string via array str[0] = ‘h’; // inisialisasi elemen dari str pada indeks ke-0 str[1] = ‘i’; // inisialisasi elemen dari str pada indeks ke-1 str[2] = ‘\0’; // inisialisasi elemen dari str pada indeks ke-2 37
3. Inisialisasi dengan menyalin dari sebuah konstanta string atau isi string lainnya dengan fungsi strcpy Cara ini lebih sering dipakai karena lebih praktis. Contoh penyalinan string: char str1[15]; char str[] = “Selamat pagi”; strcpy(str1,”Hello World”); // Coba cek isi str1 printf(“str1: %s\n”, str1); strcpy(str1,str2); // Coba cek isi str1 printf(“str1: %s\n”, str1); Untuk membuat program yang aman, panjang string yang akan disalin ke array target haruslah sama dengan atau lebih kecil daripada ukuran array target penyalinan dikurangi satu. Hal ini untuk menghindari inisialisasi atau pendefinisian nilai yang melewati batas array. Perlu diingat, ukuran array selalu konstan sejak dideklarasikan. strcpy tidak dapat menambah ukuran array target penyalinan untuk menampung string yang lebih panjang daripada batas array ini.
4.3.2 Inisialisasi via Pointer Inisialisasi string via pointer digunakan untuk array string yang ditunjuk oleh pointer dengan referensi atau yang dideklarasikan melalui pointer dinamik. Referensi Inisialisasi dengan cara ini melanjutkan deklarasi string via pointer yang sudah dibahas sebelumnya (sub bab 4.2.2). Jika terdapat pernyataan berikut: char str[5]; char * p; p = str; maka kita dapat menggunakan pointer p untuk mengisikan string str baik secara per karakter melalui indeksnya atau per string dengan fungsi strcpy. Contoh: // pengisian per karakter untuk membuat string “you” p[0]=’y’; p[1]=’o’; p[2]=’u’; p[3]=’\0’; // Coba cek isi p dan str printf("p: %s, str: %s\n", p,str); for(int i=0; i<5; i++){ printf("p[%d]:%c, \t", i,p[i]); printf("str[%d]:%c\n", i,str[i]); }
38
// mengganti isi string str dengan “kamu” via strcpy strcpy(p,”kamu”); // Coba cek lagi isi p dan str printf("p: %s, str: %s\n", p,str); for(int i=0; i<5; i++){ printf("p[%d]:%c, \t", i,p[i]); printf("str[%d]:%c\n", i,str[i]); } str[0]
char str[5];
str[1] str[2] str[3] str[4]
char *p;
p
p = str;
p
str[0] str[1] str[2] str[3] str[4]
p[0] p[1] p[2] p[3]
= = = =
‘y’; ‘o’; ‘u’; ‘\0’;
p
str[0]
‘y’
str[1]
‘o’
str[2]
‘u’
str[3]
‘\0’
str[4]
strcpy(p,”kamu”);
p
str[0]
‘k’
str[1]
‘a’
str[2]
‘m’
str[3]
‘u’
str[4]
‘\0’
Gambar 4.6 Alokasi memori dinamik Setelah sebuah pointer dialokasikan memori secara dinamik dengan cara berikut: char * p; 39
p = (char*)malloc(5*sizeof(char)); maka, seperti juga pada cara referensi, kita bisa mengisikan mengisikan string str baik secara per karakter melalui indeksnya atau per string dengan fungsi strcpy. Contoh: // pengisian per karakter untuk membuat string “you” p[0]=’y’; p[1]=’o’; p[2]=’u’; p[3]=’\0’; // Coba cek isi p printf(“p: %s\n”, p); for(int i=0; i<5; i++){ printf("p[%d]:%c, \t", i,p[i]); printf("str[%d]:%c\n", i,str[i]); } // mengganti isi string str dengan “saya” via strcpy strcpy(p,”saya”); // Coba cek lagi isi p printf(“p: %s\n”, p); for(int i=0; i<5; i++){ printf("p[%d]:%c, \t", i,p[i]); printf("str[%d]:%c\n", i,str[i]); } char *p;
p
p=(char*)malloc(5*sizeof(char)); p // jika alokasi memori berhasil
p[0] p[1] p[2] p[3] p[4]
p[0] p[1] p[2] p[3]
= = = =
‘y’; ‘o’; ‘u’; ‘\0’;
p
p[0]
‘y’
p[1]
‘o’
p[2]
‘u’
p[3]
‘\0’
p[4]
strcpy(p,”saya”);
p
p[0]
‘s’
p[1]
‘a’
p[2]
‘y’
p[3]
‘a’
p[4]
‘\0’
Gambar 4.7 40
4.4 Penanganan String Ada banyak fungsi dalam pustaka standar C yang dapat dipakai untuk menangani dan memanipulasi string. Di antaranya adalah strlen, strcmp, strcpy, strncpy, strcat.
4.4.1 Fungsi strlen Deklarasi: size_t strlen(const char *str); Fungsi ini menghitung panjang string str, mengembalikan jumlah karakter pada string, tapi tidak termasuk karakter null pemberhenti string. size_t adalah unsigned int. Contoh: int len; char *string; len = strlen (string);
4.4.2 Fungsi strcmp Deklarasi: int strcmp(const char *str1, const char *str2); Fungsi ini membandingkan dua buah string, yang ditunjuk oleh str1dan yang ditunjuk oleh str2. Jika str1 and str2 memiliki nilai string yang sama (equal, bukan identical), fungsi ini mengembalikan 0. Jika str1 < str2 (sesuai urutan alfabetiknya pada tabel ASCII), fungsi ini mengembalikan nilai kurang dari 0. Jika str1 > str2 (sesuai urutan alfabetiknya pada tabel ASCII), fungsi ini mengembalikan nilai lebih dari 0. Contoh: int value; char *s1,*s2; value = strcmp(s1,s2);
4.4.3 Fungsi strcpy Deklarasi: 41
char *strcpy(char *str1, const char *str2); Fungsi ini menyalin string yang ditunjuk oleh str2 ke str1. Penyalinan ini sampai dan mencakup karakter null pemberhenti pada str2. Jika str1 dan str2 overlap , perilakunya tidak terdefinisi. Fungsi ini mengembalikan argument str1. Contoh: char *to,*from; to = strcpy (to,from);
4.4.4 Fungsi strncpy Deklarasi: char *strncpy(char *str1, const char *str2, size_t n); Fungsi ini menyalin sampai sejumlah n karakter dari string yang ditunjuk oleh str2 ke str1. Penyalinan berhenti saat n karakter tersalin atau karakter null pemberhenti pada str2 dicapai. Jika karakter null dicapai, karakter null akan terus disalin ke str1 sampai sejumlah n karakter tersalin. Fungsi ini mengembalikan argumen str1. Contoh: char *to,*from; int n; to = strncpy (to,from,n);
4.4.5 Fungsi strcat Deklarasi: char *strcat(char *str1, const char *str2); Fungsi ini menyambungkan (concatenate) string yang ditunjuk oleh str2 ke akhir dari string yang ditunjuk oleh str1. Karakter null pemberhenti dari str1 ditumpuki (overwritten). Penyalinan berhenti ketika karakter pemberhenti dari str2 telah tersalin. Jika terjadi overlapping, hasilnya tidak terdefinisi. Fungsi ini mengembalikan argumen str1. Contoh: char *s1 = "string one"; char *s2 = "string two"; int main (){ 42
}
char buffer[255]; strcat(buffer,s1); strcat(buffer,s2);
4.4.6 Fungsi strncat Deklarasi: char *strncat(char *str1, const char *str2, size_t n); Fungsi ini menyambungkan (concatenate) string yang ditunjuk oleh str2 ke akhir dari string yang ditunjuk oleh str1 sampai sepanjang n karakter. Karakter null pemberhenti dari str1 ditumpuki (overwritten). Penyalinan berhenti ketika sejmlah n karakter telah tersalin atau karakter pemberhenti dari str2 telah tersalin. Sebuah karakter null pemberhenti selalu ditambahkan ke str1. Jika terjadi overlapping, hasilnya tidak terdefinisi. Fungsi ini mengembalikan argumen str1. Contoh: char *onto,*new,*this; new = strncat(onto,this,n);
4.4.7 Fungsi atof Deklarasi: double atof(const char *str); Fungsi ini mengonversi string yang ditunjuk oleh str ke tipe double (floating-point number). Karakter whitespace yang ada di awal string diabaikan (space, tab, carriage return, new line, vertical tab, atau formfeed). Konversi berhenti saat karakter tak dikenal dicapai. Jika sukses, fungsi ini akan mengembalikan angka hasil konversi. Jika konversi tidak dapat dilakukan, fungsi ini akan mengembalikan 0. Jika nilai yang dikonversi di luar jangkauan tipe double, HUGE_VAL akan dikembalikan dengan tanda yang sesuai dan ERANGE disimpan ke variable errno. Jika nilai yang dikonversi terlalu kecil untuk dikembalikan sesuai dengan tipe double, maka nol akan dikembalikan dan ERANGE disimpan dalam variabel errno. Contoh: double x; char *stringptr; x = atof(stringptr);
43
4.4.8 Fungsi atoi Deklarasi: int atoi(const char *str); Fungsi ini mengonversi string yang ditunjuk oleh str ke tipe double (floating-point number). Karakter whitespace yang ada di awal string diabaikan (space, tab, carriage return, new line, vertical tab, atau formfeed). Konversi berhenti saat karakter tak dikenal dicapai. Jika sukses, fungsi ini akan mengembalikan angka hasil konversi. Jika konversi tidak dapat dilakukan, fungsi ini akan mengembalikan 0. Jika nilai yang dikonversi di luar jangkauan tipe double, HUGE_VAL akan dikembalikan dengan tanda yang sesuai dan ERANGE disimpan ke variable errno. Jika nilai yang dikonversi terlalu kecil untuk dikembalikan sesuai dengan tipe double, maka nol akan dikembalikan dan ERANGE disimpan dalam variabel errno. Fungsi ini mengonversi string yang ditunjuk oleh str ke integer (type int). Any initial whitespace characters are skipped (space, tab, carriage return, new line, vertical tab, or formfeed). The number may consist of an optional sign and a string of digits. Conversion stops when the first unrecognized character is reached. On success the converted number is returned. If the number cannot be converted, then 0 is returned. Contoh: int i; char *stringptr; i = atoi(stringptr);
4.4.9 Fungsi atol Declaration: long int atol(const char *str); The string pointed to by the argument str is converted to a long integer (type long int). Any initial whitespace characters are skipped (space, tab, carriage return, new line, vertical tab, or formfeed). The number may consist of an optional sign and a string of digits. Conversion stops when the first unrecognized character is reached. On success the converted number is returned. If the number cannot be converted, then 0 is returned. long i; char *stringptr; 44
i = atol(stringptr);
4.5 Tugas
Tugas yang relevan dengan topik bab ini diberikan oleh dosen/asisten praktikum.
Tugas bab ini dapat dikombinasikan dengan tugas bab lain atas arahan dan ijin dosen/asisten praktikum.
Tugas bab ini dikumpulkan kepada dosen/asisten sesuai format yang diminta.
45
5 BAB 5: Struct Pokok bahasan: Struktur (struct) Deklarasi tipe struct dan variabel bertipe struct Inisialisasi dan akses anggota struct Keyword typedef Union (union) Deklarasi tipe struct dan variabel bertipe struct Inisialisasi dan akses anggota struct
46
5.1 Pendahuluan Seiring dengan semakin kompleksnya sebuah program, data yang berada di dalamnya pun juga menjadi bertambah kompleks. Variabel dari tipe data primitif dan array sudah tidak lagi dapat memenuhi kebutuhan program ini. Yang dibutuhkan adalah sebuah struktur data, yang dapat mengombinasikan berbagai tipe data dalam sebuah struktur yang bermakna. Dari sinilah diperkenalkan sebuah tipe data baru yaitu tipe struct, atau dalam bahasa lain record. Tipe struct ini biasanya dibahas bersama dengan tipe lain yaitu union. Sebenarnya kedua tipe ini memiliki tujuan yang berbeda. Bab ini akan membahas keduanya.
5.2 Struktur atau struct struct adalah sebuah paket yang terdiri dari satu atau lebih variabel yang dikelompokkan di bawah sebuah nama. struct berbeda dengan array dalam hal tipe data anggotanya; sebuah struct dapat memiliki anggota-anggota dari tipe data yang berbeda, sementara array hanya berisi elemen-elemen yang bertipe data sama.
5.3 Deklarasi tipe struct dan variabel bertipe struct Bentuk umum deklarasi tipe struct adalah sebagai berikut: struct tipestruktur{ tipeanggota1 namaanggota1; tipeanggota2 namaanggota1; ... };
Kemudian jika kita hendak membuat sebuah variabel bertipe struct di atas, bentuk penulisannya adalah: struct tipestruktur namavariabel; Contoh deklarasi sebuah tipe struct dengan nama tertentu dan deklarasi variabel dari tipe struct tersebut adalah sebagai berikut: // deklarasi tipe struct Employee struct Karyawan{ char id[15]; char nama[20]; char alamat[50]; double gaji; }; // deklarasi variabel emp1 dan emp2 bertipe struct Employee 47
struct Karyawan k1; struct Karyawan k2; Perlu ditekankan bahwa struct adalah sebuah kategori tipe data yang para anggotanya dapat memiliki tipe data berbeda-beda sesuai kebutuhan kita. Oleh karena itu, sebelum kita dapat menggunakan sebuah struct sebagai tipe data sebuah variabel, tipe struct ini perlu kita deklarasikan dahulu. Pada contoh di atas struct Karyawan digunakan untuk memodelkan tipe data seorang karyawan, yaitu memiliki id, nama, alamat, dan gaji. Selanjutnya struct Karyawan ini dapat digunakan untuk membuat data seorang karyawan tertentu. Pada contoh di atas, k1 dan k2, masing-masing mewakili data seorang karyawan. Id, nama, alamat, dan gaji masing-masing karyawan ini dapat diisikan atau diakses kemudian. Proses inisialisasi dan akses data ini akan dibahas pada sub bab berikutnya. Bandingkanlah metode di atas dengan jika kita memodelkan data karyawan di atas menggunakan array. Betapa terbatasnya data karyawan yang dapat kita modelkan dengan array karena array hanya dapat berisi anggota atau elemen yang berasal dari tipe data yang sama. Penulisan deklarasi di atas dapat juga disingkat menjadi: struct Karyawan{ char id[15]; char nama[20]; char alamat[50]; double gaji; } k1, k2; Dalam C++ deklarasi sebuah variabel yang bertipe data struct boleh menghilangkan keyword struct, sehingga untuk contoh di atas: struct Karyawan k1;
cukup dituliskan menjadi: Karyawan k1;
5.4 Keyword typedef typedef digunakan untuk mengasosiasikan suatu tipe dengan simbol tertentu untuk menghindari penulisan tipe data yang berulang. Perhatikan contoh berikut: typedef int bilbulat; struct Karyawan{ char id[15]; char nama[20]; char alamat[50]; 48
double gaji; }; typedef Karyawan Pegawai; typedef struct { char nim[15]; char nama[20]; int angkatan; } Mahasiswa; // sekarang bilbulat adalah suatu tipe data // x dideklarasikan sebagai bilbulat (i.e. integer) bilbulat x; x = 10; // sekarang Pegawai adalah suatu tipe data // p1 dideklarasikan sebagai Pegawai (i.e. struct Karyawan) Pegawai p1; // sekarang Mahasiswa adalah suatu tipe data // m1 dideklarasikan sebagai Pegawai Mahasiswa Mahasiswa m1; Dengan typedef di atas kita mendenisikan tiga tipe data: bilbulat, Pegawai, dan Mahasiswa. Setelah itu kita menggunakannya seperti tipe data biasa untuk mendeklarasikan variabel.
5.5 Inisialisasi dan Akses Anggota struct Anggota struct dapat diinisialisasi atau diakses dengan memanggil nama variabel struct tersebut diikuti dengan simbol tertentu (i.e. operator titik (.) atau operator panah (->), tergantung bagaimana variabel struct dideklarasikan) dan nama anggota yang bersangkutan. Jika variabel struct tidak dedeklarasikan melalui pointer, maka anggotanya dapat diakses dengan sintaksis ekspresi berikut: namavariabelstruct.anggotastruct Perhatikan contoh deklarasi struct Karyawan dan k1 sebelumnya. Untuk menginisialisasi anggota k1, penulisannya adalah: struct Karyawan k1; strcpy(k1.id, “20110521”); strcpy(k1.nama, “Lin Dan”); strcpy(k1.alamat, “Jl RRC Jakpus”); k1.gaji = 5250000;
49
5.6 Ruang Lingkup struct struct mengikuti aturan ruang lingkup sebagaimana tipe data lainnya. Sebuat tipe struct dapat menjadi global atau lokal tergantung pada di mana dia dideklarasikan. Demikian juga variabel bertipe struct tersebut; jika dia dideklarasikan secara lokal, dia hanya dapat diakses di dalam blok (yang dilingkupi oleh tanda {}) tempat dia dideklarasikan. void buatVariabelStruct(){ /* Pernyatan di bawah ini ilegal, karena A adalah struct lokal terhadap fungsi main*/ struct A a; } int main(){ struct A{ int x; float y; }; }
struct A a;
5.7 Pointer to struct Selain mendeklarasikan variabel bertipe struct, kita juga dapat membuat sebuah pointer yang menunjuk pada obyek atau data struct, yang disebut dengan pointer to struct. Sebagai contoh, dengan menggunakan contoh struct Karyawan di atas, kita bisa mendeklarasikan sebuah pointer pk1 sebagai berikut: struct Karyawan *pk1;
Kemudian, sebelum kita dapat menginisialisasi para anggota pk1, kita harus mengalokasikan dulu kepada pk1 memori dengan jumlah yang memadai. Jika kita hanya menginginkan pk1 untuk menunjuk pada satu (unit) struct Karyawan, maka alokasi memori untuk pk1 dapat dituliskan sebagai berikut: pk1 = (struct Karyawan*)malloc(sizeof(struct Karyawan)); Perhatikan bahwa untuk pk1 hanya dialokasikan 1 x sizeof(struct Karyawan) bytes. Selanjutnya jika alokasi ini berhasil, para anggota pk1 dapat diinisialisasikan sebagai berikut: strcpy(pk1->id, “007”); strcpy(pk1->nama, “James Bond”); strcpy(pk1->alamat, “London, UK”); pk1->gaji = 1000;
50
Perhatikan penggunaan simbol panah (->) di atas. Simbol ini digunakan untuk mengakses anggota struct yang ditunjuk oleh pointer. Tanda ini bisa juga digantikan dengan kombinasi tanda bintang (*) dan titik (.) seperti pada contoh di bawah ini. strcpy((*pk1).id, “007”); strcpy((*pk1).nama, “James Bond”); strcpy((*pk1).alamat, “London, UK”); (*pk1).gaji = 1000;
5.8 Union Seperti struct, union dapat digunakan untuk mendeklarasikan sekelompok data yang memiliki tipe berlainan. Bedanya, pada union semua data anggota tersebut menduduki lokasi memori yang sama. Karena sebenarnya pada satu saat sebuah alamat hanya dapat menyimpan satu macam tipe data, maka pada satu saat union hanya dapat berisi salah satu dari anggotanya. Jadi, union ini berperilaku seperti sebuah kontainer berukuran tertentu yang dapat menyimpan berbagai macam tipe data. Kegunaan utama dari union adalah untuk menghindari fragmentasi memori dengan membuat sebuah ukuran data standar pada memori. Dengan demikian kita dapat memastikan bahwa setiap bagian memori yang telah dibebaskan dari alokasi dinamik dapat dialokasikan lagi untuk variabel yang memiliki tipe union sama. Ini adalah strategi umum pemrograman sistem yang memiliki banyak variabel yang berkaitan dan dialokasikan secara dinamik. Deklarasi, inisialisasi, akses, ruang lingkup, dan penggunaan pointer serta array pada union memiliki sintaksis yang sama dengan pada struct. Untuk mengilustrasikan persamaan dan perbedaan antara struct dan union, jalankanlah kode di bawah ini. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
#include <stdio.h> #include <stdlib.h> #include <string.h> // deklarasi tipe struct SAkun struct SAkun{ int id; char nama[15]; }; // deklarasi tipe union UAkun union UAkun{ int id; char nama[15]; }; 51
16 int main(){ 17 // deklarasi variabel bertipe struct SAkun 18 struct SAkun akun1; 19 // inisialissi data anggota akun1 20 akun1.id = 11; 21 strcpy(akun1.nama,”Bachdim”); 22 23 // cetak data anggota akun1 24 printf("akun1.id: %d\n", akun1.id); 25 printf("akun1.nama: %s\n\n", akun1.nama); 26 27 // cetak alamat akun1 dan alamat anggota akun1 28 printf("&akun1: %d\n", &akun1); 29 printf("&akun1.id: %d\n", &akun1.id); 30 printf("&akun1.nama:%d\n\n", &akun1.nama); 31 32 // cetak ukuran akun1 dan ukuran data anggota akun1 33 printf("sizeof(akun1): %d\n",sizeof(akun1)); 34 printf("sizeof(akun1.id): %d\n",sizeof(akun1.id)); 35 printf("sizeof(akun1.nama): %d\n\n\n",sizeof(akun1.nama)); 36 37 // deklarasi variabel bertipe union UAkun 38 union UAkun akun2; 39 // inisialissi data anggota akun2 40 akun2.id = 22; 41 strcpy(akun2.nama,”Gonzales”); 42 43 // cetak data anggota akun2 44 printf("akun2.id: %d\n", akun2.id); 45 printf("akun2.nama: %s\n\n", akun2.nama); 46 47 // cetak alamat akun2 dan alamat anggota akun2 48 printf("&akun2: %d\n", &akun2); 49 printf("&akun2.id: %d\n", &akun2.id); 50 printf("&akun2.nama:%d\n\n", &akun2.nama); 51 52 // cetak ukuran akun2 dan ukuran data anggota akun2 53 printf("sizeof(akun2.id): %d\n",sizeof(akun2.id)); 54 printf("sizeof(akun2.nama): %d\n",sizeof(akun2.nama)); 55 printf("sizeof(akun2): %d\n\n",sizeof(akun2)); 56 57 system("PAUSE"); 58 return 0; 59 } Gambar 5.1
52
Secara khusus, perhatikanlah beberapa hal berikut. Amati baris ke-27sampai ke-30 dan hasil eksekusinya. Hal ini menunjukkan bahwa lokasi anggota struct berbeda-beda dan alamat dari struct tersebut sama dengan alamat dari anggota struct yang dideklarasikan pertama. Bandingkanlah hasil ini dengan hasil eksekusi baris ke-47 sampai ke-50. Kemudian, amati baris ke-32 sampai ke-35 dan hasil eksekusinya. Hal ini menunjukkan bahwa ukuran dari struct sama dengan atau lebih besar sedikit daripada jumlah ukuran data anggotanya. Bandingkan hasil ini dengan hasil eksekusi baris ke-52 sampai ke-55. Perhatikan juga baris ke-19 sampai ke-25 dan hasil eksekusinya. Bandingkan dengan baris ke-39 sampai ke-45. Ambillah kesimpulan dari perbedaan hasil ini.
5.9 Tugas
Tugas yang relevan dengan topik bab ini diberikan oleh dosen/asisten praktikum.
Tugas bab ini dapat dikombinasikan dengan tugas bab lain atas arahan dan ijin dosen/asisten praktikum.
Tugas bab ini dikumpulkan kepada dosen/asisten sesuai format yang diminta.
53
6 BAB 6: Operasi File Pokok bahasan: Pengertian file Saluran I/O standar: stdin dan stdout Penanganan file: tingkat tinggi vs tingkat rendah Posisi program pada file Membuka file Menutup file Membaca file Menulis file Contoh kasus
54
6.1 Pendahuluan Bab ini akan membahas konsep file dan penggunaannya dalam bahasa C/C++. Pertama, pengertian file akan diuraikan secara singkat. Penanganan file dibagi menjadi penanganan tingkat tinggi dan tingkat rendah, dan ini akan dijelaskan selanjutnya. Beberapa fungsi pada pustaka standar C untuk mengoperasikan files juga aka dibahas setelah itu. Seperti pada bab-bab sebelumnya, bab ini akan diakhiri dengan tugas tentang yang operasi file, yang dapat dikombinasikan dengan tugas dalam bab lainnya.
6.2 File C/C++ memperlakukan informasi yang masuk ke atau keluar dari program sebagai ‘aliran’ data (steam of data/bytes/bits). Dikatakan aliran karena pada proses ini data disusun secara berurutan seperti sebuah aliran, untuk dipindahkan dari satu lokasi ke lokasi lainnya. Aliran bytes inilah yang disebut juga sebagai file. Dengan bantuan sistem operasi, sebuah program melihat files melalui sejumlah saluran atau ‘portal’ (masuk dan keluar). Untuk dapat membaca atau menulis pada sebuah file, program tersebut harus membuka dulu salah satu saluran/portal yang sesuai. Dengan demikian saluran/portal ini menyembunyikan hal-hal teknis yang terlalu detil yang bergantung pada sistem operasi dari pemrogram. Untuk membaca data dari sebuah file, sebuah program cukup membacanya dari portal file-nya. Dia tidak perlu berurusan dengan bagaimana data tersebut sampai kepadanya. Begitu juga sebuah program yang menulis informasi pada sebuah file cukup menyampaikannya pada salah satu portal ini. Sisa pekerjaannya akan dilakukan oleh sistem operasi. Kesimpulannya, untuk menggunakan sebuah file, sebuah program harus melakukan langkah-langkah berikut: 1. Membuka file untuk membaca atau menulis (i.e. memesan sebuah portal untuk mencari file tersebut pada disk atau lokasi lainnya). 2. Membaca atau menulis pada file itu menggunakan fungsi-fungsi untuk menangani file yang disediakan oleh pustaka standar. 3. Menutup file untuk membebaskan portal yang telah dipakai agar dapat digunakan oleh program atau file lain. Sebuah program membuka file dengan memanggil sebuah fungsi pada pustaka standar yang mengembalikan sebuah pointer yang menunjuk pada file. Program tersebut dapat menggunakan pointer ini untuk mengolah file tersebut dan untuk membedakannya dari file-file lainnya. Tipe pointer to file ini dituliskan dalam bentuk FILE *, di mana FILE adalah sebuah struct khusus yang didefinisikan pada <stdio.h>. FILE ini bukanlah file yang dimaksud sebenarnya, melainkan
55
sebuah struct yang dapat membantu kita untuk mengakses dan memanipulasi file yang sebenarnya. Pointer to FILE inilah yang dapat diibaratkan sebagai sebuah saluran/portal.
6.3 Saluran I/O Standar: stdin dan stdout Selama ini kita sudah sering menggunakan masukan standar dan keluaran standar pada sebuah program. Masukan standar sering diasosiasikan dengan ‘keyboard’ dan keluaran standar dengan monitor. Untuk membaca data yang dimasukkan melalui keyboard, sebuah program cukup memanfaatkan saluran/portal masukan standar, yaitu stdin. Fungsi scanf yang sering kita pakai itu sebenarnya membaca input dari user yang telah masuk ke dalam buffer (pada memori) yang bisa dibayangkan sebagai bagian dari stdin. Sebaliknya, untuk menulis ke layar monitor, sebuah program menggunakan saluran/portal keluaran standar stdout. Fungsi printf yang juga sering kita gunakan itu hanya mengalirkan data yang kita hantarkan sebagai parameter fungsi tersebut ke stdout. stdin dan stdout bertipe pointer to FILE (FILE *). Berbeda dengan saluran/portal lainnya yang harus dibuka dulu sebelum digunakan dan ditutup setelah dipakai, stdin dan stdout selalu terbuka secara otomatis ketika program yang bersangkutan dijanlankan.
6.4 Penanganan File: Tingkat Tinggi vs Tingkat Rendah C menyediakan dua tingkat penanganan file: penanganan file tingkat tinggi dan tingkat rendah. File tingkat tinggi diperlakukan sebagai file teks. Data yang masuk ke file ini adalah data teks sebagaimana yang nampak pada layar monitor, karakter per karakter. File yang ditulis dengan fungsi penanganan file tingkat tinggi akan menghasilkan file teks yang dapat diedit dengan program pengedit teks. File tingkat tinggi juga dibaca sebagai file teks, sebagaimana input dari keyboard yang dibaca oleh program. Dengan demikian, fungsi penanganan file tingkat tinggi bekerja dengan konsep yang sama dengan fungsi I/O standar. Penanganan file tingkat rendah, sebaliknya, mengelola data dalam format yang lebih rendah (lebih dekat dengan mesin atau piranti keras) tanpa mengonversinya terlebih ke dalam format teks. Karena itu fungsi-fungsi untuk menangani file level rendah ini kurang ‘programmer-friendly’, tetapi dapat bekerja lebih cepat dan efisien. Dalam modul ini kita akan memfokuskan bahasan pada penanganan file level tinggi. Kebanyakan fungsi penanganan file tingkat tinggi mudah dikenali dari namanya yang dimulai dengan huruf ‘f’, seperti: fopen() fclose() 56
fprintf() fscanf() fgets() fputs()
6.5 Posisi Program pada File Saat data dibaca dari sebuah file, sistem operasi mencatat posisi terkini program pada file tersebut. Program tersebut cukup memanggil fungsi standar tertentu untuk ‘membaca bagian file berikutnya’ dan sistem operasi akan menjalankan perintah ini dengan membaca bagian file berikutnya dan memajukan posisi program pada file. Begitu seterusnya sampai posisi pada file mencapai akhir dari file. Setiap karakter yang dibaca akan menyebabkan posisi program pada file maju satu langkah. Walaupun sistem operasi secara otomatis akan menangani banyak hal tentang posisi pada file, sebuah program juga dapat mengontrol perubahan posisi pada file tersebut melalui beberapa fungsi, seperti ungetc() misalnya, jika dibutuhkan. Tetapi pada kebanyakan kasus, hal ini tidak diperlukan dan lebih baik dihindari. Pergerakan posisi yang kompleks pada file dapat menyebabkan pergereakan kompleks dari mekanisme disk drive, yang pada akhirnya bisa mengakibatkan kerusakan pada disk dan munculnya kesalahan.
6.6 Membuka File Seperti telah dijelaskan pada sub bab 6.2 di atas, langkah pertama untuk menangani file adalah membuka file tersebut. Pada C fungsi untuk membuka file adalah fopen, dengan deklarasi berikut: FILE *fopen(const char *filename, const char *mode); Fungsi ini membuka file ditunjuk oleh filename. Paremeter mode dapat bernilai salah satu dari string berikut: r
Mode baca file teks (r=read)
w
Mode tulis file teks (w=write, memotong ukuran/panjang file menjadi 0, atau membuat file baru)
a
Mode sambung file teks untuk menulis (a = append, membuka atau membuat file dan mengeset pointer file pada akhir file, end-of-file)
rb
Mode baca file biner (rb=read binary)
57
wb
Mode tulis file biner (wb=write binary, memotong ukuran/panjang file menjadi 0, atau membuat file baru)
ab
Mode sambung file biner untuk menulis (ab=append binary, membuka atau membuat file dan mengeset pointer file pada akhir file, end-of-file)
r+ atau +r
Mode baca dan tulis file teks
w+ atau +w
Mode baca dan tulis file teks (memotong ukuran/panjang file menjadi 0, atau membuat file baru)
a+ atau +a
Mode baca dan tulis file teks (membuka atau membuat file dan mengeset pointer file pada akhir file, end-of-file)
r+b atau rb+ Mode baca dan tulis file biner atau +rb w+b atau wb+ Mode baca dan tulis file biner (memotong ukuran/panjang file menjadi 0, atau atau +wb membuat file baru) a+b atau ab+ Mode baca dan tulis file biner (membuka atau membuat file dan mengeset atau +ab pointer file pada akhir file, end-of-file) Jika file yang dimaksud tidak ada dan dibuka dengan mode baca (r), maka operasi buka file akan gagal. Jika file dibuka dengan mode sambung (append, a), maka seluruh operasi tulis akan dimulai dari akhir dari file tersebut tanpa memedulikan posisi file saat ini. Jika file dibuka dengan mode update (+), maka keluaran (tulis) tidak dapat langsung diikuti dengan masukan (baca) tanpa diintervensi oleh fseek, fsetpos, rewind, atau fflush. Jika operasi membuka file sukses, sebuah pointer to *FILE akan dikembalikan . Jika operasi ini gagal, maka null pointer akan dikembalikan.
6.7 Menutup File Untuk menutup file kita dapat menggunakan fungsi fclose dari pustaka standar C, dengan deklarasi berikut: int fclose(FILE *stream); 58
Fungsi ini menutup file yang ditunjuk oleh stream sekaligus mem-flush buffer yang ada. Proses flush ini akan menyebabkan data yang tersisa pada buffer, jika ada, akan tertulis pada file. Jika sukses menutup file, fungsi ini mengembalikan nilai 0. Sedangkan jika gagal, fungsi ini akan mengembalikan EOF.
6.8 Menulis File Ada beberapa fungsi dari pustaka standar C yang dapat digunakan untuk menulis file, diantaranya adalah:
fprintf fputc fputs
6.8.1 Fungsi fprintf Deklarasi: int fprintf(FILE *stream, const char *format, ...); Fungsi ini menuliskan keluaran yang terformat dalam bentuk string pada parameter format ke dalam stream. Jika sukses, fungsi ini akam mengembalikan jumlah karakter yang tertulis pada file. Jika ada kesalahan, -1 dikembalikan. fprintf() ini memiliki kesamaan dengan printf().Parameter yang digunakan hanya berbeda pada satu hal, yaitu pada fprint terdapat tambahan satu parameter: sebuah pointer to file. Sebenarnya fprintf() berhubungan dengan printf() dengan cara yang sederhana. Dua pernyataan berikut adalah identik. printf ("Hello world %d", 1); fprintf (stdout,"Hello world %d", 1);
6.8.2 Fungsi fputc Deklarasi: int fputc(int character, FILE *stream); Fungsi ini menulis satu karakter (unsigned char) yang dispesifikasikan oleh parameter character ke file yang ditunjuk oleh stream dan menggerakkan maju indikator posisi file pada stream tersebut. 59
Jika sukses, fungsi ini mengembalikan karakter yang ditulis. Jika ada kesalahan, indikator kesalahan untuk stream diset dan EOF dikembalikan.
6.8.3 Fungsi fputs Deklarasi: int fputs(const char *str, FILE *stream); Fungsi ini menulis sebuah string pada stream sampai pada (tetapi tidak termasuk) karakter null. Jika sukses, fungsi ini mengembalikan nilai non negatif. Jika ada kesalahan, EOF dikembalikan.
6.9 Membaca File Untuk membaca file beberapa fungsi di bawah ini dapat digunakan:
fscanf fgetc fgets
6.9.1 Fungsi fscanf Deklarasi: int fscanf(FILE *stream, const char *format, ...); Fungsi ini membaca input field dari sebuah stream sesuai dengan format format yang berisi conversion specifier. Conversion specifier ini menentukan bagaimana input akan disimpan dalam variabel yang sesuai. Setiap variabel yang menjadi parameter setelah format haruslah bertipe pointer. Pembacaan input field berhenti saat karakter yang ditemukan gagal untuk dikonversi dengan conversion specifier atau tidak ada lagi input field yang dibaca. Jika sukses, fungsi ini akam mengembalikan jumlah input field yang berhasil dikonversikan dan disimpan dari stream. Jika ada kesalahan, EOF dikembalikan. fscanf() ini analogis dengan scanf() sebagaimana frpintf() dengan printf().
6.9.2 Fungsi fgetc Deklarasi:
60
int fgetc(FILE *stream); Fungsi ini membaca dan mendapatkan karakter berikutnya (unsigned char) dari stream dan memajukan indikator posisi dalam stream . Jika sukses, fungsi ini mengembalikan karakter yand didapat. Jika end-of-file dicapai, EOF dikembalikan dan indikator end-of-file diset. Jika kesalahan terjadi, maka indikator kesalahan untuk stream tersebut diset dan EOF dikembalikan.
6.9.3 Fungsi fgets Deklarasi: char *fgets(char *str, int n, FILE *stream); Fungsi ini membaca satu baris dari stream dan menyimpannya ke dalam string str. Fungsi ini berhenti saat (n-1) karakter dibaca, karakter pada baris baru dibaca, atau end-of-file dicapai. Karakter pada baris baru disalin ke string str. Sebuah karakter null ditambahkan pada akhir string tersebut. Jika sukses, fungsi ini mengembalikan string. Jika ada kesalahan, sebuah null pointer dikembalikan. Jika end-of-file muncul sebelum ada karakter yang dibaca, string tidak berubah.
6.9.4 Fungsi feof Deklarasi: int feof(FILE *stream); Fungsi ini mengecek indikator end-of-file pada stream. Jika end-of-file dicapai, fungsi ini akan mengembalikan nilai bukan 0. Jika end-of-file belum dicapai, fungsi ini mengembalikan 0.
6.10
Contoh Aplikasi
Perhatikan contoh di bawah ini: 1 #include <stdio.h> 2 #include <stdlib.h> 3 4 int main(){ 5 FILE *fp; 6 7 int i = 12; 8 float x = 2.356; 9 char ch = 's'; 61
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
char str[20] = "Selamat Datang!\n"; // Membuka file untuk menulis fp = fopen("latihan.txt", "w"); if (fp != NULL){ printf("Sukses membuka file\n"); // Menulis file dengan fprintf fprintf (fp, "%d %f %c %s", i, x, ch, str); //Menutup file if(fclose(fp)==0){ printf("Sukses menutup file\n"); } else{ printf("Tidak sukses menutup file\n"); }
} else{ printf("Tidak sukses membuka file\n"); } int j,n; float y; char c; char s[20]; // Membuka file untuk membaca fp = fopen("latihan.txt", "r"); if (fp != NULL){ printf("Sukses membuka file\n");
// Membaca file dengan fscanf n = fscanf (fp, "%d %f %c %s", &j, &y, &c, s); printf("Jumlah masukan yang sukses dibaca: %d\n", n); printf ("j = %d, y = %f, c = %c, s = %s\n", j, y, c, s); //Menutup file if(fclose(fp)==0){ printf("Sukses menutup file\n"); } else{ printf("Tidak sukses menutup file\n"); }
} else{ printf("Tidak sukses membuka file\n"); }
62
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 }
// Membuka file untuk membaca fp = fopen("latihan.txt", "r"); if (fp != NULL){ printf("Sukses membuka file\n"); // Membaca file dengan getc while (!feof(fp)) { c = getc(fp); printf("%c",c); } //Menutup file if(fclose(fp)==0){ printf("Sukses menutup file\n"); } else{ printf("Tidak sukses menutup file\n"); }
} else{ printf("Tidak sukses membuka file\n"); } system("pause"); return 0; Gambar 6.1
Analisis dan kerjakan kode di atas. Amati penggunaan fungsi-fungsi untuk menangani file. Lakukanlah eksperiemen mandiri untuk lebih memahami operasi file. Cobalah menggunakan fungsi-fungsi operasi file lain (yang tidak terdapat pada kode di atas) untuk membaca dan menulis file.
6.11
Tugas
Tugas yang relevan dengan topik bab ini diberikan oleh dosen/asisten praktikum.
Tugas bab ini dapat dikombinasikan dengan tugas bab lain atas arahan dan ijin dosen/asisten praktikum.
Tugas bab ini dikumpulkan kepada dosen/asisten sesuai format yang diminta.
63
7 Daftar Pustaka Arnold, K., Gosling, J., and Holmes, J. The Java Programming Language, 4th Edition. Addison Wesley Professional, 2005. Burgess, M. C Programming Tutorial (K&R version 4), 1999. Diakses 18 Oktober 2011, dari situs Mark Burgess, Faculty of Engineering, Oslo University College: http://www.iu.hio.no/~mark/CTutorial/CTutorial.html C Language Tutorial. Diakses 18 Oktober 2011, dari Computational Physics, Department of Physics, Drexel University: http://www.physics.drexel.edu/courses/Comp_Phys/General/C_basics/ Dodrill, G. 1997. C Language Tutorial. Diakses 18 Oktober 2011, dari Institute of Robotics, Faculty of Electrical Engineering and Computer Science, University of Maribor: http://www.ro.feri.unimb.si/predmeti/mik_si/C_prir/CLIST.HTM Herianto, T. Tuntunan Praktis Pemrograman C++. PT Elex Media Komputindo Kelompok Gramedia, Jakarta, 1995. Laboratorium Komputer. Modul Praktikum Dasar Pemrograman Komputer. Program Studi Teknik Informatika, Fakultas Teknik, Universitas Brawijaya, Malang, 2011. Pranata, A. Pemrograman Borland C++ 4.x. Jilid 1. Penerbit Andi, Yogyakarta, 1996. Soulie, J. C++ Language Tutorial. Diakses 18 Oktober 2011 dari cplusplus.com: http://www.cplusplus.com/doc/tutorial/
64