PEMROGRAMAN TINGKAT RENDAH
PEMROGRAMAN TINGKATRENDAH Pemrograman tingkat rendah adalah pemrograman pada tingkat ekspresi, statemen,dan fungsi.Sebaliknyapemrogramantingkat rendah adalahpada tingkat modul compiledan sis~m. Booini meneruskanpembahasanpraktekpemrograman C, yang menekankanpemrogramanpadatingkatrendah ; bab berikutnyamembahas tentang pemrograman tingkat tinggi.
5. 1. PARAMETERISASI Program tergantungpada nilai seperti nama file, search string, ukuran tabel, fount dan sebagainya. Nilai semacam ini dapat dilakukan hard code kedalam program ataudapat dikirimkankedalamprogramsebagaiparameter.Parameterisasi memiliki keuntungan yang besar : Kode yang diberi parameter lebih mudah perawatannya, bersifat portable dan dapat dipakai kembali karena kita dapat meningkatkandan menyesuaikannyadenganmengubahparameter.Kelemahannya adalah bahwa pa~ameterisasi memerlukan usaha yang lebih banyak pada . permulaannya, serta mungkin dapat masalah unjuk kerja. Proses ini memerlukan usaha yang lebih banyak untuk memilih parameter argoritma serta struktur data dap kemudian menempatkannya kedalam kode. Mungkin terdapat unjuk kerja bila diperlukan komputasi tambahan untuk mendapatkan atau membuat nilai yang \ '
berparameter. Parameterisasi memiliki manfaat; meskipun diperlukan pekerjaan yang banyak pada tahap awal, usaha yang banyak dapat dihindari kemudian dalam tahap-tahap pembuatan sistem, serta pinalti unjuk kerja biasanya keeil.
5.1.1. APA YANG HARUS DI PARAMETERISASI Para disainer bahasa pemrograman kadang-kadang mengatakan bahwa bilangan yang akan muncul dalam bahasa ialah 0 dan 1 serta tak terhingga (infinity). Hal yang sama dapat dikatakan dari kode sumber, dimana idealnya bilangan yang digunakan seharusnya hanyalah 0 dan 1 (dan bahkan 0 dan 1 sebaiknya dihindari untuk kepentingankemudahanbaca pada nama-namaseperti NOLL, 1RUE dan FALSE).Namun sayangnyakode sumber sering menimbulkan permasalahandengan"bilanganmagic"yang menunjukkanciri lingkungan,aplikasi 88
dan implementasi. Bilangan magic yang umum dalam program C meliputi :
. . .
Nilai aplikasi khusus. Nilai ini meliputi kode wama, ukuran field, nilai maksimum dan minimum yang diperbolehkan,dan sebagainya. Nilai lingkungankhusus. Nilai ini.meliputi ukuran.kata, ukuran tipe, ukuran blok file dan nilai lain yang tergantung pada mesin atau compiler khusus. Nilai tuning. Nilai ini meliputi ukuran baffer, ukuran blok faktor loading taOOldan sebagainya.
.
Batas tipe data. Batas ini meliputi-32768 dan 32767 sebagai nilai minimum dan maksimum dari tipe short, sebagai contoh :
.
Batas struktur data. Batas ini meliputi $ array dan ukuran taOO!.
.
Kode internal. Kode ini digunakan untuk menyajikan obyek khusus atau menyatakan secara khusus pada implementasi. Contoh dari kode internal meliputi nilai pilihan menu, tipe token yang dikembalikanoleh parsel serta kode kembalianfungsi.Nilai-nilaiini khususnyaOOrsifatmagicsebab mereka tidak memiliki makna diluar program tempat mereka muncu!.
. .
Mask bit. Mask bit ini dipakai untuk mengolah $ bit. Offset. Offset merupakan lokasi field dalam file, baffer, taOOlatau struktur.
BILANGAN MAGIC MERUPAKAN CALON UNTUK PARAMETERISASI Nilai aplikasi khusus hendaknya dibuat kedalam parameter untuk mempermudah perawatan. BeOOrapabilangan magic yang dijamin tidak akan mengubah lokasitampilan output,kode karakterASCn dan sebagainya,mungkin tidak perlu di parameterisasikan( meskipun sulit untuk menjarnin bahwa nilai tersebut tidak akan OOrubah). Kita hendaknya selalu membuat parameter dari nilai-nilai lingkungan khusus batas struktur data, offset, serta nilai lainnya yang cenderung akan OOrubahjika program akan dipindah. Mask bit, kode internal, batas tipe data dan nilai-nilailain yang dapat membingungkanpembaca program hendaknya di parameterisasi sehingga mudah dibaca. Merupakan hal yang baik untuk memparameterisasitipe data. UNIX dan C terdapat pada mesin yang OOrbedajenisnya dari mikro komputer sampai super komputer. Tipe-tipe data harns sering diubah untuk menyesuaikan dengan lingkungan tersebut misalnya, program untuk mikro komputer deng~ memori yang terbatas dan bus 8 bit mungkin memerlukan variabel integer yang di deklarasikan Unsigned Char bukan sort atau Int agar program menjadi cepat 89
dan keeil. Pada mesin yang lebih besar beberapa variabel mungkin harns berupa short atau lot untuk memeeahkan masalah yang lebih besar. Operasi juga dapat di parameterisasi. Sebuah sistem dapat menggunakan algoritma yang secara komputasional mahal bila alat flowating point tersedia, atau algoritma yang lebih murah tetapi kurang akurat bila tidak ada pilihan. Pilihan diantara dua tersebut hendaknya di kodekan sebagai sebuah parameter. Demikian pula operasi untuk intervace pemakai, optimisasi, ukuran masalah, fonnat data dsb semuanya dapat di parameterisasi.
5.1.2. BERBAGAI CARA PARAMETERISASI C memiliki banyak cara untuk melakukan parameterisasi. Cara yang paling umum adalah argumen fungsi. Sebagai contoh merupakan hal yang umum untuk menemukan sort atau search dengan batas struktur data hard coded, fungsi pesan pemakai dengan lokasi tampilan hard coded, format dan kode nilai, fungsi analisis data dengan operasi analisis hard coded dan sebagainya. Argumen fungsi merupakan hal yang baik untuk membuat parameter nilai aplikasi khusus, tipe data dan, batasan struktur data serta operasi. Pada operasi-operasi khusus dilakukan pemeriksaan yang lebih karena sangat sedikit programer yang terbiasa memberikan fungsi-fungsi sebagai program serta mekanisme yang handal. Sebagai contoh, sort array dapat digeneralisasi dengan memberikan operasi perbandingan serta ukuran elemen array sebagai argumen. Hasil ini merupakan jenis yang bekerja pada semua jenis array dan sort pada semua order. Pelajarilah sort sisipan general
purpose void
:
Insertion_Sort(
array,
char *array; int element_size, array_size;
element_size,
array_size,
cOIIIp_func
)
/* in/out:pointerto an arbitraryarray */ /* in: bytes used to make up an element */ /* in: elements in the array */
int (*caIIp_func){); /* in: TRUE
iff arg1 bears order relation
to arg2
*/
{ char
/* buffer
*tmp;
register
int
i,j;
for swapping
/* loop variables:
array elements
i walks
forward,
*/ j walks
back
/* Part 1: Allocate a swap buffer */ tmp
= malloc(
(unsigned)element_size
);
/* Part 2: Swap the smallest for
(
value
i = element_size, j = 0; i < (array_size*element_size);
i if
II
+=
element_size
)
( (*cOlllp_func) (array+i,
array+j)
) j
= i;
into
first place
*/
*/
if(O<j) { memcpy( trnp, array, element_size) ; memcpy( array, array+j, element_size ); memcpy( array+j, trrp, element_size ); }
for
(
/* Part 3: Insert array elements i (2*element_size); i < (array_size*element_size); i += element_size )
starting
from the third */
=
( memcpy(
trnp, arrray+i,
element_size);
j = i; while ( (*comp_func) (trnp,array+j-element_size) ) ( memcpy( array+j, array+j-element_size, element_size ); j -= element_size; } memcpy( array+j, trnp, element_size ); } /* Part 4: Deallocate free ( tmp
the swap buffer
*/
);
} /* Insertion_Sort
*/
Fungsi perbandingan menutup informasi tentang elemen array serta menentukan cara memesan mereka. Oleh karena sort tidak harus mengetahui rincian ini maka dia akan bekerja baik dengan hasil yang dikembalikan oleh fungsi komparasi.Kami telah mengujisort ini pada sistem karni serta menemukan bahwa 4 jam lebih lambat dari versi non-generik. Turunnya unjuk kerja berasal dari panggilan fungsi diatas dalam perbandingan bergeraknya data, akan tetapi bila jenis insersi mendekati (array dengan tidak lebih dari 30 elemen), turunnya efisiensi ini mungkin tidak begitu penting atau dilihat. Makrosi merupakan parameterisasi serta untuk .membuatkode yang lebih portable. Makro untuk nilai lingkungan serta nilai aplikasi khusus, nilai tuning, mask bit, offset serta bilangan magic yang non-portable dapat dipakai kedalam header files. Kemudian bila port, fix, perubahan atau perluasan memerlukan suatu perubahan, merupakan hal yang mudah untuk mengubah definisi makro' dan mengkompile kembali. Kadang-kadang makro dipakai untuk kode internal atau nilaidata bernamaflag ataukeadaaninternal.Misalnyaculamsebuahkompiler tipe token dikembalikan oleh piranti analisa lexical dap>!1diberi kode khusus. 91
Kode token dapat didifinisikan sebagai makro berikut: #define IDENTIFIER #define #define #define #define #define
0
1 SEMI_COLON 2 LEFT_BRANCE 3 RIGHT_BRACE INTEGER_LITERAL 4 5 REAL_LITERAL
etc.
Meskipun hal ini dapat diterima namun merupakan hal yang lebih baik untuk menggunakan tipe dan anumerisasi berikut: enum
token_type
{IDENTIFIER,
SEMI_COLON, LEFT_BRACE, RIGHT_BRACE, INTEGER_LITERAL, REAL_LITERAL, etc. };
Tipe anumerasi mempunyai dua keuntungan : Kita tidak harus mengisi kembali bilangan kode bila daftar tersebut berubah dan kompiler dapat melakukannya lebih baik dengan eara melakukan pemeriksaan. Difmisi tipe merupakan mekanisme parameterisasiyang lain. Seperti halnya macro. Difinisi tipe dapat ditempatkan pada beberapa file header, berubah bila diperlukan dan sistem tersebut akan di kompil ulang kita hendaknya selalu menggunakan definisi tipe bukan macro pada saat parameterisasi tipe. Pelajari
contohberikut
:
#define CHAR_PTR CHAR_PTR
92
p,
q;
char *
Bila macro ini diperluas deklarasi tersebut membaca char
*p,
:
qi
Program ini mendeklarasikanp sebagai pointer bagi char tetapi q sebagai sebuah char dengan menggunakan type de! untuk CHAR_PTR memecahkan masalah ini.
IIIJ
Merupakan ringkasan pembahasan parameterisasi. Nilai aplikasi khusus Nilai lingkungan khusus Nilai Tuning Batas struktur dan tipe data Masks bit Offsets Kode Intemal
Argumen fungsi, makro Makro Makro
Operasi
Argumen fungsi
Tipe
Definisi tipe
Argumen Fungsi Makro Makro Enumerasi
5.2. PREPROSESOR C Preposesor C merupakan prosesor macro yang dijalankan pada kode sumber C sebagai tahap pertama kompilasi. Preprosesor tersebut akan menarik isi file lainnya kedalam file sumber sesuai dengan #inc1ude directiv, memperluas macro dan secara kondisional menyimpan atau menghapus bagian file sumber tersebut. Kita telah membahas bagaimana macro tanpa parameter meningkatkan portabilitas dan kemudahan perawatan. Bagian ini memberikan petunjuk lebih lanjut dalam menggunakan preprosesor. Satu kekeliruan pemakaian preprosesor C adalah
93
menggunakannya preprosesor
untuk menyamarkan
berikut
C sebagai bahasa lain.Misalnya difinisi
dibuat.
#define if #define THEN #define REPEAT
#define UNTIL Menggunakan
if do ( b
macro
} while
)
(! (b)
ini, perhatikan potongan
kode
berikut sebelum
preprosesing.
REPEAT
=
ch
getch
() ;
IF ch < BLANK THEN ch = ESe; ~IL
Kode
( ch == BLANK );
ini diubah
do { ch
oleh
=
preprosesor
getch
kedalam
berikut.
() ;
if ( ch < BLANK ) ch = ESC; while (! (ch -- BLANK) );
Potongan
pertama
operator persamaan. instruksi lengkap
Mula-mula kepandaian
kelihatan
Potongan
untuk
seperti pascal kecuali
kedua
menyamarkan
ide menyamarkan
tertentu
adalah
padanya
pekeIjaan
C
C
kode
untuk
pengisian
dan
C. Artikel ini menyediakan
sebagai pascal.
kelihatan tidak begitu buruk ini tidak banyak
mengganggu
-
unjuk
ada keIja
kompiler, dan dapat memberikan dukungan untuk programer C yang barn yang mengetahui bahasa pemrograman lainnya. Akan tetapi dengan beberapa alasan menyamarkan C merupakan hal yang buruk. Pertama bagaimanapun juga C memiliki model tersendiri dan penyamaran tersebut tidak akan pemah sempuma. Menyusun bahasa C dari bahasa lainnya akan selalu tidak tepat dan idiom C serta operasi akan mengalami keterbatasan dimana-mana. Misalnya tidak terdapat cara untuk menirukan operasi tipe Z pascal dengan menggunakan preprosesor C sebab tidak ada cara untuk membuat operator "+", "-", "*", dan "=" akan berlaku 94
sebagaimana mestinya dalam pascal. Sebagai akibatnya kode yang disamarkan membingungkandua bahasa yang idiosenkratik,tidak terdokumentasiserta tanpa dukungan. Kedua, menyamarkan C bagi programer C barn temyata akan lebih merepotkan daripada membantu, oleh karena programer barn tersebut tidak mengetahui C dia tidak dapat menyebutkan C dari penyamaran tersebut. Oleh karena itu pada pelajaran C programer bam hams juga memilih ciri-ciri bahasa C dari ciri-ciriyang disamarkan,dan bagianyang tidakterpelajaripada penyamaran tersebut akan disalahkan sebagai C. Ketiga, C dapat disamarkan bila dia tertulis tetapi penyamaranitu akan terungkapselamakompilasi : Programyang dijalankan tersebut program C, selama pembetulan, kita hams mengetahui secara tepat apa yang dilakukan oleh program kita, maka kita hams memasukisemua penyamaran untuk melihat apa yang sebetulnya dijalankan oleh program C. Hal ini akan membuat pekerjaan lebih sulit. Akhirnya penyamaran program C akan mengakibatkan program tersebut sulit dibaca. Tidak terdapat penyamaran standard untuk C bila programer C membaca program yang disamarkan mereka terpaksa untuk mengetahui arti kode orang lain. Masalah lain dapat digambarkan untuk mengecoh perbedaan antara macro dan fungsi.Macrobukanlahobyekprogrammelainkankependekanuntuk potongan kode. Bila preprosesor dikerjakan dengan sebuah file maka semua macro akan lenyap hubungannya dengan definisi mereka. Sebaliknya fungsi tetap ada pada seluruh terjemahanprogram dan berfungsi sebagaientitas lain dalam kode obyek. Hal ini membuat perbedaan yang besar. Argumen fungsi dievaluasi selama pelaksanaan program dan hasilnya diberikan ke fungsi tersebut tetapi argumen macro seca("atekstural digantikan selama preprosesing. Macro dapat interaksi dengan kode sekitamya dimana hal ini tidak dapat dilakukan oleh fungsi hal ini sering mengejutkanpara programer.Sebagai contoh macro berikut dipakai untuk menghitung makna geometrik. #define G_MEAN(a,
b)
sqrt (a * b)
Perluasan dari G_MEAN ( X+4, Y+2 ) adalah sqrt ( x+4 * y+2 ) Ekspansi ini tidak menghitung MEAN geometrik dan mungkin tidak menghitung apa yang dikehendaki masalah ini dapat dengan mudah dibetulkan.
Tempatkanparameterdalamdefinisimacrosebagaiberikut #define
G_MEAN(a,b)
(sqrt«a)
*
:
(b»)
95
Aturannya, macro dan parametemya harns dipisahkan dengan (dalam definisi mereka). Masalah lain mungkin timbul dari perbedaan argumen macro dan fungsi.
Pelajari definisi berikut untuk maksimum dua nilai #define
MAX(a,
b)
« (a)
>
:
(b))
?
(a)
:
(b))
Definisi ini kelihatannya baik tetapi perhatikan MAX ( i++, j++) kita akan mengharapkan i dan j akan ditambah dan nilai dari ekspresi yang lebih besar dari nilai aslinya i dan j. Lihat kode yang telah diperluas sbb. « (i++)
<
(j++))
?
(i++)
(j++)
)
Ekspresi ini menambah satu untuk i dan dua kali untuk j, dan nilainya lebih besar dari dua plus satu. Akibat sampingan yang menyebabkankejutan ini tidak dapat terjadi dengan parameter fungsi. Masalah akibat sampingan terdapat pada banyak macro;hindaripenggunaanmacrodengancarnmemakaifungsisemaksimal mungkin. Macro dengan definisi yang besar dapat menyebabkan masalah lain kode obyek besar yang tidak diharapkan. Masalah ini tidak akan terjadi untuk fungsi karena hanya ada satu copy body fungsitidakperduli berapa kali fungsi tersebut dipanggiI. Satu-satunya alasan yang baik dalam menggunakan macro sebagai pengganti fungsi adalah kecepatan : Oleh karena macro diperluas pada tempat kemunc..dan mereka dan tidak memiliki konteks lokal, maka macro tersebut tidal< memiliki panggilan dan kembali seperti yang ada pada fungsi. Pada sebagian implementasi C panggilan fungsi dan kembalinya overhead adalah kecil namun kadang-kadang menggantikan fungsi dengan macro diperlukan untuk mempercepat kode dalam memenuhi persyaratan. Bila demikian kembangkan dan koreksi program dengan fungsi serta menggantikan fungsi dengan macro sebelum pengujian unit terakhir.
96
5.1.PUSTAKAFUNGSIC Tidak seperti bahasa-bahasa lain C tidak mempunyai operasi yang sudah dibuat dari pabrik untuk input dan output, manipulasi $ dan beberapa operasi aritmatik yang umum sepertipangkat. Konsekuensinyahampir semua program C hams menggunakan pustaka fungsi C. Banyak dari fungsi pustaka tersebut telah berstandard tinggi, khususnya sekarang dimana terdapat standard C ANSI, tetapi lainnya tidak begitu standard, hanya terdapat dalam lingkungantertentu. Pustaka fungsi C secara khusus meliputi hal-hal sebagai berikut :
.
.
Fungsi input dan output. Fungsi ini untuk membuka dan menutup file, memeriksa status file, mencari dan mengulang file, serta membaca dan menulis file. Semua implementasi C menyediakan fungsi core dalam pustaka I/O standard, yang sering diperlruat dengan fungsi lainnya pada lingkungan khusus.
Fungsi manipulasi $. Fungsi ini adalah untuk mengcopy, menghubungkan, membandingkan, mencari, dan menemukan panjang $. Fungsi ini telah . berstandard tinggi dan terdapat dimana-.mana.
.
Fungsi manipulasi dan alokasi memori. Fungsi ini adalah untuk mengalokasikan dan membebaskan memori dari banyaknya tempat penyimpanan yang kosong dan telah standard. Sebagian besar implementasi juga memberikanrutinuntuk mengcopydan mengosongkanmemori,menguji memori kosong yang masih tersisa dan sebagai.nya.
.
Fungsi matematis.Fungsiini merupakankumpulandari berbagaimacam fungsi matematis dalam katagori ini tidak kelihatan begitu banyak standarisasi untuk fungsi akar kuadrat, logaritma dan pemangkatan.
.
Fungsi conversi data. Fungsi ini adlah untuk mengirimkan data numerik diantara penyajian karakter dan biner. Dalam pustaka ini terdapat sedikit uniformity (kurangnya informasi).
.
Fungsi pelayanan sistem operasi. Fungsi ini memberikan akses untuk input tingkat rendah serta output tingkat rendah control piranti periperal, control proses, dan tanggal, waktu serta informasi status sistem. Rutin ini telah standard dalam lingkungan UNIX tetapi sangat beragam dalam lingkungan lainnya.
Beberapa programer menghinch!ri fungsi pustaka karena ingin menggunakan fungsi yang dikembangkan sendiri. cara ini merupakan praktek yang tidak baik.
97
Porsi standard dari pustaka fungsi C khbususnya fungsi input output, telah dikenal oleh semua programer bahasa C yang berpengalaman. Kode yang menggunakannya lebih mudah dibaca karena berisikan elemen yang telah dikenal. Fungsi pustaka didokumentasikan oleh suplier compiler dan didukung bersama compiler tersebut. Bagian dari dokumentasi program serta perawatan diberikan oleh suplier compiler pada saat program menggunakan fungsi pustaka. Pustaka fungsi C memberikan pelayanan tingkat tinggi yang umum. Pelayanan ini yang membantu memisahkan program dari lingkungannya. Program yang tidak menggunakan fasilitas ini akan kurang portable. Fungsi pustaka C diuji secara luas, sehingga fungsi-fungsi tersebut lebih reliable daripada fungsi yang ditulis programer untuk mereka sendiri. Lebih Ianjut fungsi pustaka telah dibuat untuk kepentingan efisiensi yang baik, sering oleh para ahli yang berpengalaman dengan menggunakan bahasa rakitan untu,k mendapatkan unjuk kerja yang optimal. Akhirnya dengan menggunakan fungsi pustaka C dapat meningkatkan produktifitas programer. Tidak terdapat alasan untuk menulis, mendokumentasikan, mencoba, memperbaiki, memperluas, dan merawat puluhan, ratusan atau ribuan baris kode C bila fungsi yang sarna telah ada yang disediakan oleh compiler C. Disamping pustaka fungsi C yang terdapat bersama compiler, masih ada lagi pustaka lainnya yang dapat dipakai. Terdapat pasar substansial dalam pustaka fungsi C khusus, terutama untuk micro komputer. Banyak produk ini memiliki kualitas yang tinggi. Untuk kepeintingan tertentu pustaka-pustaka ini sesuai dengan standard seperti grafik kernel sistem atau sistem kernel grafik (GKS), standard grafik nasional. Pustaka yang sesuai d~ngan standard terencana dengan baik serta terdokumentasi baik pula serta dapat dipindahkan oleh pustaka lain yang sesuai dengan staridard tersebut.
5.4. EKSPRESI Oleh karena ekspresi digunakan lebih banyak dalam C daripada bahasa pemrogramannya yang semacam maka ekspresi C bersifat kompleks, ekspresi merupakan sumber masalah dalam pemrograman C tanda kurung. C memiliki satu operator ternary, 34 operatorbiner dan 9 operator unary yang tersusun dalam 15 tingkat hirarki presenden. Meskipun sebagian besar dari operator ini jarang dipakaiyang tingkat-tingkatpresendensepertitingkat-tingkatdalambahasa lainnya, susunan evaluasi dalam ekspresi yang kompleks masih dapat menyebabkan kesalahan dan membingungkan. Cara terbaik untuk menghindari hal yang membingungkan dan untuk menjarnin bahwa ekspresi dievaluasi sebagaimana 98
yang kita kehendaki adalah dengan menggunakan tanda kurung. Tanda kurung yang banyak tidak mempengaruhi kompilasi serta efisiensi waktu, tetapi meningkatkan kemampuan baca dan reliabilitas. Side effects. C mempunyai beberapa operator dengan side effects : operatorpenambahandan pengurangandan operatorpengisian(assignent).Banyak programer C menganggap side .effects ekspresi sebagai salah satu kemampuan tertinggi pada bahasa C; tetapikita menganhggaphal ini sebagaisuatu kelemahan. Meskipun kode sumber dapat diperpendek dengan ekspresi side effects, kemamapuan baca biasanya akan menurun dan kesalahan akan mengungkap oleh karena itu disarankanbahwaside effectse,kspresinyahendaknyadigunakandengan hati-hati. Operasi Bitwise. Sebagaimanapendahulunyasebagaibahasapemrograman sistem, C menawarkan banyak variasi operasi untuk mengolah bit. Beberapa programer tertarik untuk menggunakan bit untuk bekerja dengan bit meskipun sebepamya tidak perlu, dengan menggunakan penggantian untuk perkalian dan pembagian integer, menyimpangflag jamak atau counter dalam kata tunggal dan sebagainya. Kecuali pada saat aplikasiatau tuntutanunjuk kerja memerlukannya,praktek seperti itu sebaiknya dihindari. Ekspresi Boolean Kompleks. Sebagian besar orang bahkan programer mudah bingung dengan operasi boolean kompleks. Kemudahanbaca, releabilitas serta kemudahan perawatan cenderung untuk naik bila operasi boolean disederhanakan. Metode untuk menyederhanakan ekspresi boolean didasarkan pada hukum DeMorgan sebagai berikut : ! ( ! (
A A
&& B
II
B
is equivalent to is equivalent to
!A
II
!A
&&
!B !B
Misalnya pelajari ekspresi boolean !«4<=A) && (A<=6». Ekspresi ini terbaca: benar bila ekspresi tidak dalam kasus bahwa baik A paling tidak 4 dan A tidak lebih dari 6. Dengan menggunakanhukum DeMorgan kita mendapatkan «A<4) II (6
pertama), merupakan tabel-tabel keputusan. Karnough memetakan pohon keputusan, dan Marquand memetakandiagram.Piranti-pirantiinijuga bermanfaat dalam memahami kondisionar yang kompleks. Ekspresi boolean yang kompleks kadang-kadang merupakan sumber dari kesalahan bit effects. Operator boolean C merupakan operator sirkuit pendek, sehingga evaluasi ekspressi akan terhenti secepat mungkin. Oleh karena itu bia A lebih kecil dari 4 dan operasi dari ekspresi boolean «A<4) II (6= Y ). orang-orang secara mental "flip" re1asike ( X >= Y ). Ibaratnya melakukan senam mental bersama-sama dengan melihat apa yang terjadi pada suatu program adalah merupakan pekerjaan yang sukar. Dengan menghindari operator rasional > dan >= dan menggantikanoperator < dan <= dapat memecahkan masalah ini dengan menghilangkansemua keperluansemua operasiflip, dan lebih tepat dengan notasi matematis standard. Banyak programer C yang berpengalaman menunjukkan satu masalah yang menjengkelkan : salah ketik operator = sebagai operator pengisian = pada ekspresi relasi rasional. Program dengan masalah ini akan
menunjukkan tingkah laku yang aneh, meskipun kode sumber akan kelihatan betul - kita cenderung uniuk membaca kesalahan yang lalu, dengan melihat operatorpengisiansebagaioperatorpersamaan.Satuprakteksederhanamengurangi frekuensi kesalahan ini ( menambahkan kemampuan baca kode ): Bilamana perbandingan persamaan dibuat dengan konstanta, tempatkan konstanta tersebut pada sebelah kiri. Karena sebuah konstanta tidak dapat diisi dengan sebuah nilai maka compiler akan menangkap operator persamaan yang diketik salah tersebut sebagai operator pengisian (assgignment operator). Ekspresi penghentian loop. Ekspresi ini diuji untuk menentukanapakah loop harns berhenti.Ekspresiboolean dalam header dan loop while adalah operasi penghentian loop. Ekspresi penghentian loop secara umum menguji apakah variabel control loop telah mencapai nilai tertentu. Sebagai contoh : for
( {
100
i
...
=
START_VALUE;
loop
body
...
i
}
! = STOP_VALUE;
1++
)
Hal ini dapat memenuhi kehendak programer serta dapat dibaca, tetapi kurang aman. Tidak ada jaminan bahwa i tidak berubah dalam bodi loop untuk melompat kebelakang STOP _VALUE, yang menyebabkan loop tak terhingga. Lebih baik sebagai berikut : for
( {
i . 0°
=
START_VALUE;
loop
body
° 00
i
<=
STOP_VALUE;
i++
}
Loop ini akan berhenti pada semua nilai dari i, paling tidak sebesar STOP_VALUE, baik nilai yang dikendaki°tersebuttercapai atau tidak. Hal ini lebih aman dengan memberikanekspresi penghentianloop kemungkinanterbesar untuk gagal, yaitu, dengan membuatnyaselemah mungkin. HAl ini menyarankan aturan praktek lainnya membuat operasi penghentian loop selemah mungkin. Ekspresipenghentianloop hendaknyajuga harns lengkap,dalam arti bahwa semua test untuk penghentianloop hendaknyateIjadidalam ekspresiloop tersebut,bukan dalam bodi. Loop lebih mudah dipahami bila semua kondisi penghentiannya ditampilkan bersama-sama dan tempat konvensional untuk melakukan hal ini adalah ekspresi penghentian loop tersebut.
5.5. TIPE-TIPEDALAMC Pemakaian tipe data yang buruk dapat membingungkan, kesalahan, dan masalah portabilitas dalam program C. Terdapat beberapa sumber masalah : seperti yang disebutkan dalam Bab 4, C adalah tipe kaya bukan strongly typed dan memiliki banyak memilikitipe konversi. KeduaC memberikan akses tingkat 'rendah kedalam komputer, yang menyebabkan masalah portabilitas tipe. Ketiga difinisi bahasa C memberikanciri kunci khusus (seperti ukuran tipe data) untuk implementasi, sekali lagi dapat menyebabkan masalah portabilitas. Kita mempertimbangkan semua masalah ini dalam mempelajari praktek-praktek penggunaan tipe-tipe dalam pemrograman C. C memiliki beberapa tipe integral dalam rane nilai mereka. Pilihan diantara tipe-tipe ini tergantungpada memori yang diperlukanuntuk menyimpannilai tipe tersebut, kecepatan pelaksanaanoperasi akan digunakan pada nilai tipe tersebut, keamanan dan portabilitaskode yang menggunakantipe tersebut, dan range nilai dimungkinkanolehtipe ini.Hal yangpalingpentingadalahyang terakhirkhususnya 101
karena range nilai yang diperbolehkan oleh suatu tipe membawa sebagian besar infonnasi kedalam pembaca program tentang peran obyek program. Aturan berikut ini dapat membantu membuat pilihan diantara tipe-tipe integral dalam C :
.
.
Gunakan tipe signed dari tipe unsigned. C tidak dapat memberikan pemeriksaan kesalahan yang baik untuk tipe unsigned : Nilai negatif dapat berubah kedalam konteks unsigned, dan dapat diinterprestasikan sebagai kuantitas unsigned, serta menyebabkankesalahan. Bila variabel atau fungsi dideklarasikandengan tipe signed, meskipun bila aturan mereka dianggap unsigned, pemeriksaan nilai-nilai negatif dapat disisipkan oleh programer, dan indikasi lain (sepertioutput negatit) lebihcenderung untuk menunjukkan kesalahan. Kekecualian untuk aturan ini dibuat hanya bila menggunakan string bit atau bila range nilai memerlukannya. Gunakan tipe char untuk data karakter. Bila kita mendeklarasikan variabel atau fungsi sebagaichar sedangkandata yang terlibat sebenarnyabukan data karakter maka hal ini akan membingungkan. Oleh karena itu tipe char hendaknya hanya dipakai bila datanya adalah data karakter.
.
Bila nilai yang diberikan dapat disajikan dalam bit yang kUrangdari 16, gunakan tipe INT, jika tidak gunakan tipe LONG. Temyata bahwa nilai !NT hampir selalu disajikan paling tidak dalam 16 bit, dan nilai LONG hampir disajikan dalam bit yang lebih dari 16. Jadi pemindahan dari INT ke LONG pada 16 bit lebih baik dipakai untuk menghindari maSalahporting.
.
Jangan gunakan tipeshort kecualijika keterbatasanmemori memerlu~annya. Nilai tipe short diubah kedalam nilai int dalam ekspresi integer yang melibatkan data int dan dalam argumen fungsi. Pengubahan ini tidak selalu dilakukan dengan baik oleh compiler.
Cara lain untuk menanganimasalahportabilitasdari tipe integraladlah dengan carn menggunakan file header pedefinisian tipe standard. Dalam file ini tipe integral diberikan nama khusus yang menunjukkanukuran dan isinya. Bila sistem ditempatkan pada lingkungan yang bam, maka hanya typedef dalam file header yang perlu dirubah. Sebagai contoh file header semacam ini dapat berisikan typedef seperti berikut.
102
typedef S_INI'_8
char;
/* signed
typedef U_INI'_8
unsigned char;
*/ unsigned
typedef S_INI'_16
short;
8 bit integer 8 bit integer
*/ */
/* signed 16 bit integer
*/
typedef U_INI'_16 unsigned short;
/* unsigned
*/
typedef S_INI'_32
int;
/* signed 32 bit integer
*/
typedef
U_INI'_32
unsigned;
/* unsigned
*/
typedef
S_INI'_64
long;
/* signed
typedef
U_INI'_64
unsigned long;
/* unsigned
16 bit integer
32 bit
integer
64 bit integer 64 bit integer
*/ */
Kesulitan dengan pemecahan cara ini adalah bahwa beberapa tipe mungkin tidak ada, sehingga masalah portabilitas masih akan tetap muncul. e menyediakan dua tipe floating point : float dan double, tetapi semua operasi floating point, dan semua oilai floating point yang diberikan sebagai parameter, menggunakan nilai double. Variabel float menempati ruang yang lebih sedikit bila dibandingkan double, tetapi nilai variabel ini selalu diubah kedalam double sebelum digunakannya.Kode yang diperlukan untuk mengubah float kedalam double memerlukan waktu untuk melaksanakannya, dan sering menggunakan tempat yang lebih banyak daripada bila disave dengan mendeklarasikan beber.apa variable float sebagal pengganti double. Konsekuensinya semua variable dan floating point hams dideklarasikan sebagai double. Oleh karena e memiliki pemeriksaan tipe yang lemah, tipe pOinterdapat diubah dengan bebas, pointer dapat menunjukkandata pada saat setiap saat, dan pointer dapat diubah kedalamtipe integraldan sebaliknya.Semua praktektersebut tidak tepat sehingga membuat kode lebih sulit dipahami serta menyebabkan maslaah portabilitasyang serius. Pengubahanantara tipe pointer serta khususnya antara tipe integral dan pointer hendaknyajangan sampai teIjadi dalam kode e yang baik. Bila hal ini teIjadi, maka dia hams ditandai dengan operator yang jelas. Sumber lain dari kesalahantipe kecil adalahargumenfungsi.Argumen fungsi mempunyai dua kelompok kesalahan (bug) tipe. Kelompok pertama teIjadi bila compiler e tidak memeriksa tipe argumen fungsi. Pencegahan yang terbaik agar tidak teIjadi kesalahan ini adalah dengan menggunakan prototipe fungsi, yang merupakan tipe fungsi e ANSI yang memeriksa mekanisme. eara lain yang terbaik adalah lint, piranti pemerlksaan sintac e yang handal (lihat Bab 7). 103
Kelompok kesalahan tipe argumen fungsi yang kedua muncul dari perubahan (konversi) yang digunakan pada argumen fungsi pada saat fungsi tersebut dipanggil. Argumen fungsi diubah menurut "konversi aritmatik biasa" pada saat pemanggilan: Nilai short dan char diubah kedalam int, nilai Ooat diubah ke double. Kesalahan ringan kadang-kadang terjadi pada saat parameter fungsi memiliki tipe yang berbeda dari target konversi aritmatik biasa. Biasanya kesalahan ini merupakan kesalahan compiler (kita telah menemukannya dalam beberapa compiler). Cara terbaik untuk menghindari kesalahan seperti ini adalah dengan tidak mendeklarasikan parameter fungsi dengan tipe yang bukan merupakan target dari konversi aritmatik biasa. Secara khusus parameter fungsi hendaknya tidak dideklarasikan sebagai char, short atau Ooat.
5.6. PENINGKATAN EFISIENSI Program sering memiliki keterbatasan unjuk kerja dalam spesiftkasinya sehingga program hams dapat membuat kode menjadi efisien serta betul, tepat, reliabledan dapat diperbaiki. Optimisasiadalahkegiatansetelah mengubahsistem sehingga lebih cepat dan lebih kecil. Salah satu alasan terkenalnya C adalah bahwa bah~a ini memberikan fasilitas yang dapat membuat optimisasi kode lebih mudah.Optimisasirriemangsulit.Usahayang tidaktepat seringmenyebabkan masalah yang serius. Pada bagian ini kita akan menjelaskan beberapa kesulitan optimisasi kode C dan menerangkan cara-cara menghindarinya. Aturan pertama pada optimisasi program dengan menghindari optimisasi bila mungkin. Kode yang ditulis untuk kepentingan efisiensi biasanya kurang mudah dibaca, kurang tepat, kurang portable, dan lebih susah perawatannya dibandingkan yang sebaliknyayaitu mudah dibaca, tepat, portabilitas,dan mudah dirawat yang merupakan hal yang lebih penting daripada kecepatan. Optimisasi memerlukanbanyak waktudan dapat menimbulkankesalahan-kesaIahan.Biasanya program dapat berjalan cukup cepat sebagaimana yang tertulis. Bila terdapat masalah unjuk kerja, pertimbangkanlah alternatif untuk optimisasi. Salah satu alternatif adalah dengan mengupgrade perangkat hardware atau sistem operasi. Dengan menambahkan chip memori atau coprocessor floating point dapat rneningkatkanunjuk kerja. Sistem operasi yang bam sering menampilkanbanyak kemajuan dalam sistem secara keseluruhan. Alternatif lainnya adalah dengan mengupgrade compiler kita. Versi bam compiler atau compiler optimisasi dapat memberikan kode yang lebih cepat dan lebih kecil. 104
Dari piranti hardware atau software yang telah ditingkatkan tidak ada maka mungkin kita hams menggunakan teknik optimisasi. Pencapaian terbesar berasal dari peningkatan stmktur data dan algoritma sistem. Topik ini yang mempakan bagian inti dari ilmu komputer dengan literatur yang banyak, berada diluar pembahasan kita. Bila sistem yang menggunakan algoritma, dan stmktur data yang baik masih gagal memenuhi tujuan unjuk kerja maka peningkatan dapat dibuat dengan menggunakan teknik yang disebut oleh Bentley sebagai writing efficient code. Teknik ini menysun kembali kode sumber yang diterjemahkan kedalam kode obyek yang lebih efisien. Praktek yang baik mempakan hal yang penting bila digunakan teknik optimisasi ini. Studi-studi empiris menunjukkan bahwa program-program khusus, sebagian waktunya digunakan untuk melaksanakan bagian kecil dari kode tersebut. Oleh karena itu unjuk kerja secara keselumhan ditingkatkan hanya bila bagian yang benar dari sebuah sistem ditingkatkan. Namun sayangnya para programer kurang memiliki kemampuan dalam menentukan bagian sistem yang mempakan penghambat. Banyak usaha yang dilakukan dengan sia-sia karena mengoptimalkan bagian sistem yang salah. Lebih Ianjut para programer juga kurang memiliki kemampuan dalam memperkirakan efisiensi konstruksi program altematif - kadang-kadang kode lebih lamban setelah programer meningkatkannya. Sehubungan dengan itu kita menyarankan untuk menggunakan metode yang dibahas oleh Bentley. Pertama kali buatlah sistem dengan menggunakan teknik engineering software yang baik tanpa mencoba untuk mengoptimisasi \code,dengan menekankan pada pengembangan : Untuk mencapai kemudahan b~ ~a,realibilitas kebenaran dan sebagainya. Apabila sistem telah selesai ukurlah Ijuk kerjanya. Bila belum. memenuhi tuntutan maka pelajari sistem untuk. menemukan penghambatnya. Oleh karena programer tidak memiliki kemampuan yang baik dalam menentukan keberadaan hambatan, maka unjuk keIja sistem harus diukur dengan menggunakan piranti analisis unjuk keIja, atau profilers. Beberapa piranti profiling UNIX dibahas dalam Bab 7. Pecahkanlah masalah program secara sistimatis dengan membuat perubahan secara langsung. Ukurlah sistem setelah diadakannya sistem untuk mengetahui bahwa pembahan tersebut benar-benar merupakan peningkatan. Oleh karena optimisasi sering menimbulkan kesalahan juga, maka sistem hams juga mempakan regresi yang diuji setelah tiap-tiap perubahan dibuat ( lihat Bab 8 ). Bila unjuk kerja sistem telah meningkat sehingga dapat memenuhi tuntutan yang dikehendaki, maka optimisasi hendaknya dihentikan. Semua program sudstansial dapat ditingkatkan, tetapi peningkatan yang bermanfaat biasanya dibuat lebih awal. Bentley membahas beberapa teknik optimisasi. Berikut adalah sebagian'dari kemungkinan-kemungkinan : 105
. . . .
. .
Simpanlah hasil yang telah dihitung sebelumnya untuk menurunkan biaya komputasi ulang serta fungsi yang mahal. Cache biasanya memerlukandata untuk mengurangi waktu akses dan waktu search. Bila mungkin lakukanlahlazy evaluationadalah merupakancarn untuk tidak menghitung suatu hasil sampai hasil itu diperlukan. Kodekan pergerakan loop. Tempatkan kalkulasi yang tidak perlu lagi dikerjakan pada masing-masingiterasi loop diluar loop, dengan menghindari komputasi ulang atas nilai yang diketahui. Bukalah loop. Sebagai pengganti pemakaian loop, tulislah steatment yang akan dilakukan loop dengan menghindari overhead control loop. Gabungkan loop. Bila dua loop saling berdekatan dan memiliki data yang sarna, maka gabungkanlah mereka sehingga dapat dihindari loop extra.
Teknik optimisasi kode ini adalah bahasa pemrograman yang independen. Teknik ini yang biasanya berguna bagi C meliputi : Compilelah tanpa flag pemeriksa path, didebugger, dan profiler. Hal ini dapat mengurangi waktu eksekusi dalam ukuran program. . Deklarasikanvariabelyang seringdiakses,sepertivariabelcontrolloopdengan kelas penyimpangan register. . Gunakanlah macro ditempat fungsi penghambat yang pendek. Hal ini secara umum dapat meningkatkan waktu eksekusi, tapi juga dapat menimbulkan bahaya. ( lihat bab sebelumnya ). Gantilah operasi index array dengan operasi poiQterdengan menghindari aritmatik pengindeksian loop. Teknik optimisasi ini dapat mencapai hasil yang luar biasa.Bentley memberikanbanyakcontoh dimana kecepatantinggi berkisar dar 2 sampai 200.
.
.
5.7. KESIMPULAN Seperti yang ditunjukkan dalam bab ini terdapat rincian yang sangat banyak tentang praktek pemrograman C yang hams dipelajari, khususnya pada tingkat steatment, ekspresi dan macro. Meskipun kita belum menyebutkan semuanya dengan alat apapun kita mempelajari bagian yang terpenting. Daftar ini terdapat dalam apendix d yang mengumpulkan sebagian besar yang telah kita bahas. 106