Anda di halaman 1dari 174

Halaman 1

2
Kata Pengantar ................................................. .................................................. ................................. 6
Kata pengantar untuk edisi pertama ............................................. .................................................. ......... 8
Bab 1 - Pendahuluan Tutorial ............................................ ............................................. 9
1.1 Memulai ................................................... .................................................. ............. 9
1.2 Variabel dan Ekspresi Aritmatika ............................................. ............................. 11
1.3 Pernyataan untuk .................................................. .................................................. ......... 15
1.4 Konstanta Simbolik ............................................... .................................................. ..... 17
Input dan Output 1,5 Karakter ............................................. ............................................ 17
1.5.1 Menyalin File ............................................. .................................................. ........... 18
1.5.2 Penghitungan Karakter ............................................. .................................................. 19
1.5.3 Penghitungan Garis ............................................. .................................................. ........ 20
1.5.4 Penghitungan Kata ............................................. .................................................. ...... 21
1.6 Array .................................................... .................................................. ........................ 23
1.7 Fungsi ................................................ .................................................. ................... 25
1.8 Argumen - Panggilan berdasarkan Nilai ............................................ ............................................... 28
1.9 Susunan Karakter ................................................... .................................................. ......... 29
1.10 Variabel dan Cakupan Eksternal ................................................. ........................................ 31
Bab 2 - Jenis, Operator dan Ekspresi .......................................... ............................. 35
2.1 Nama Variabel ............................................... .................................................. ........... 35
2.2 Jenis dan Ukuran Data ............................................. .................................................. .... 35
2.3 Konstanta ................................................ .................................................. ................... 36
2.4 Deklarasi ................................................ .................................................. ............... 38
2.5 Operator Aritmatika ............................................... .................................................. .. 39
2.6 Operator Relasional dan Logis ............................................. .................................... 39
2.7 Ketik Konversi ............................................... .................................................. ....... 40
2.8 Operator Peningkatan dan Penurunan ............................................. ............................... 43
2.9 Operator Bitwise ............................................... .................................................. ....... 45
2.10 Operator dan Ekspresi Penugasan ................................................. ......................... 46
2.11 Ekspresi Bersyarat ............................................... .............................................. 47
2.12. Presedensi dan Urutan Evaluasi ............................................ .............................. 48
Bab 3 - Aliran Kontrol ............................................. .................................................. ........ 50
3.1 Pernyataan dan Blok .................................................. .................................................. . 50
3.2 If-Else .................................................. .................................................. .......................... 50
3.3 Lain-Jika .................................................. .................................................. .......................... 51
3.4 Beralih ................................................ .................................................. ........................ 52
3.5 Loops - While dan For ............................................ .................................................. ... 53
3,6 Loops - Do-While ............................................ .................................................. .......... 56
3.7 Istirahat dan Lanjutkan .................................................. .................................................. ..... 57
3.8 Goto dan label .............................................. .................................................. ............ 57
Bab 4 - Fungsi dan Struktur Program ........................................... ............................. 59
4.1 Dasar-dasar Fungsi .............................................. .................................................. ...... 59
4.2 Fungsi Pengembalian Non-bilangan bulat ............................................ .................................... 61
4.3 Variabel Eksternal ................................................... .................................................. ....... 63
4.4 Aturan Lingkup ............................................... .................................................. ................ 68
4.5 Header Files ............................................... .................................................. ................ 69
4.6 Variabel Statis ............................................... .................................................. ........... 70
4.7. Mendaftar Variabel ............................................... .................................................. ....... 71
4.8 Struktur Blok ................................................... .................................................. ........... 71
4.9 Inisialisasi ................................................ .................................................. ............... 72
4.10 Rekursi ................................................ .................................................. ................. 73
4.11 Preprosesor C .................................................. .................................................. .... 74
4.11.1 Inklusi File ............................................. .................................................. ........ 75
4.11.2 Substitusi Makro ............................................. ................................................. 75

Halaman 2

3
4.11.3 Inklusi Bersyarat ............................................. ............................................. 77
Bab 5 - Pointer dan Array ............................................ ................................................. 78
5.1 Pointer dan Alamat .................................................. .................................................. 78
5.2 Pointer dan Argumen Fungsi ............................................. .................................... 79
5.3 Pointer dan Array .................................................. .................................................. ...... 81
5.4 Aritmatika Alamat ............................................... .................................................. ..... 84
5.5 Pointer Karakter dan Fungsi ............................................. .................................... 87
5.6 Array Pointer; Pointer ke Pointer ............................................... .............................. 89
5.7 Array Multi-dimensi ............................................. ................................................ 92
5.8 Inisialisasi Array Pointer ................................................. ........................................ 93
5.9 Pointer vs. Array Multi-dimensi .............................................. ................................ 94
5.10 Argumen Baris Perintah ............................................. ............................................. 95
5.11 Pointer ke Fungsi .................................................. .................................................. . 98
5.12 Deklarasi Rumit ............................................... .......................................... 100
Bab 6 - Struktur .................................................. .................................................. .......... 105
6.1 Dasar-Dasar Struktur .................................................. .................................................. ... 105
6.2 Struktur dan Fungsi .................................................. .............................................. 107
6.3 Susunan Struktur .............................................. .................................................. ... 109
6.4 Petunjuk untuk Struktur .............................................. .................................................. . 112
6.5 Struktur Referensi-Diri ................................................. .............................................. 113
6.6 Pencarian Tabel ............................................... .................................................. ........... 117
6.7 Typedef ................................................ .................................................. .................... 119
6.8 Serikat Pekerja ................................................ .................................................. ..................... 120
6.9 Bit-fields .................................................. .................................................. .................... 121
Bab 7 - Input dan Output ............................................ .................................................. . 124
7.1 Input dan Output Standar ............................................. ............................................ 124
7.2 Output Terformat - printf ............................................. .............................................. 125
7.3 Daftar Argumen Panjang Variabel ............................................ .......................................... 127
7.4 Input Terformat - Scanf ............................................. ................................................ 128
7.5 Akses File ................................................... .................................................. ................ 130
7.6 Penanganan Kesalahan - Stderr dan Keluar ........................................... ..................................... 132
7.7 Line Input dan Output ............................................. .................................................. . 134
7.8 Fungsi Lain-lain ............................................... ............................................. 135
7.8.1 Operasi Tali ............................................. .................................................. . 135
7.8.2 Pengujian dan Konversi Kelas Karakter .......................................... .................... 135
7.8.3 Lupakan .................................................. .................................................. ................ 135
7.8.4 Eksekusi Perintah ............................................. .............................................. 135
7.8.5 Manajemen Penyimpanan ............................................. ............................................. 136
7.8.6 Fungsi Matematika ............................................. .......................................... 136
7.8.7 Pembuatan Angka Acak ............................................ .................................... 136
Bab 8 - Antarmuka Sistem UNIX ........................................... ................................... 138
8.1 Penjelas File ............................................... .................................................. ......... 138
8.2 I / O Tingkat Rendah - Baca dan Tulis ........................................ ........................................ 139
8.3 Buka, Buat, Tutup, Putuskan Tautan .......................................... .............................................. 140
8,4 Akses Acak - Lseek ............................................. ................................................ 142
8.5 Contoh - Implementasi Fopen dan Getc ......................................... .............. 142
8.6 Contoh - Daftar Direktori ............................................. ........................................ 145
8.7 Contoh - Alokasi Penyimpanan ............................................ ...................................... 149
Apendiks A - Manual Referensi ............................................. ............................................. 154
A.1 Pengantar .................................................. .................................................. .............. 154
A.2 Konvensi Lexical ............................................. .................................................. ... 154
A.2.1 Token .................................................. .................................................. ............... 154
A.2.2 Komentar .................................................. .................................................. .......... 154

Halaman 3

4
A.2.3 Pengidentifikasi .................................................. .................................................. ........... 154
A.2.4 Kata Kunci .................................................. .................................................. ........... 154
A.2.5 Konstanta .................................................. .................................................. ........... 155
A.2.6 String Literals ............................................. .................................................. ...... 156
A.3 Notasi Sintaksis ............................................. .................................................. ......... 156
A.4 Arti Pengidentifikasi ............................................ .................................................. . 157
A.4.1 Kelas Penyimpanan ............................................. .................................................. ...... 157
A.4.2 Jenis Dasar ............................................. .................................................. ......... 157
A.4.3 Jenis turunan ............................................. .................................................. ...... 158
A.4.4 Jenis Kualifikasi ............................................. .................................................. .... 158
A.5 Objek dan Nilai-nilai ................................................ .................................................. .... 158
A.6 Konversi .................................................. .................................................. .............. 159
A.6.1 Promosi Integral ............................................. ................................................ 159
A.6.2 Konversi Integral ............................................. .............................................. 159
A.6.3 Integer dan Floating ............................................ ............................................... 159
A.6.4 Jenis Apung ............................................. .................................................. ..... 159
A.6.5 Konversi Aritmatika ............................................. ......................................... 159
A.6.6 Pointer dan Integer ............................................ .............................................. 160
A.6.7 Void .................................................. .................................................. ................... 160
A.6.8 Petunjuk untuk Membatalkan ............................................ .................................................. ... 161
A.7 Ekspresi .................................................. .................................................. ............... 161
A.7.1 Konversi Pointer ............................................. ................................................ 161
A.7.2 Ekspresi Utama ............................................. .............................................. 161
A.7.3 Ekspresi Postfix ............................................. ............................................... 162
A.7.4 Operator Unary ............................................. .................................................. .. 164
A.7.5 Pemain .................................................. .................................................. .................. 165
A.7.6 Operator Multiplikatif ............................................. ........................................ 165
A.7.7 Operator Aditif ............................................. ................................................ 166
A.7.8 Pergeseran Operator ............................................. .................................................. .... 166
A.7.9 Operator Relasional ............................................. .............................................. 167
A.7.10 Operator Kesetaraan ............................................. .............................................. 167
A.7.11 Bitwise DAN Operator ............................................ ......................................... 167
A.7.12 Eksklusif Bitwise ATAU Operator ........................................... ............................. 167
A.7.13 Bitwise Inclusive OR Operator ........................................... .............................. 168
A.7.14 Logis DAN Operator ............................................ ......................................... 168
A.7.15 Logika ATAU Operator ............................................ ............................................ 168
A.7.16 Operator Bersyarat ............................................. ........................................... 168
A.7.17 Ekspresi Penugasan ............................................. .......................................... 169
A.7.18 Operator Koma ............................................. ................................................. 169
A.7.19 Ekspresi Konstan ............................................. .......................................... 169
A.8 Deklarasi .................................................. .................................................. .............. 170
A.8.1 Penentu Kelas Penyimpanan ............................................ ......................................... 170
A.8.2 Jenis Penentu ............................................. .................................................. .... 171
A.8.3 Struktur dan Deklarasi Serikat Pekerja ........................................... ............................ 172
A.8.4 Pencacahan .................................................. .................................................. ..... 174
A.8.5 Deklarator .................................................. .................................................. ......... 175
A.8.6 Arti Deklarator ............................................ .......................................... 176
A.8.7 Inisialisasi .................................................. .................................................. ....... 178
A.8.8 Ketikkan nama ............................................. .................................................. ......... 180
A.8.9 Typedef .............................................. .................................................. .............. 181
A.8.10 Jenis Kesetaraan ............................................. ................................................ 181
A.9 Pernyataan .................................................. .................................................. ................ 181
A.9.1 Pernyataan Berlabel ............................................. ................................................ 182

Halaman 4

5
A.9.2 Pernyataan Ekspresi ............................................. ............................................ 182
A.9.3 Pernyataan Gabungan ............................................. ............................................ 182
A.9.4 Pernyataan Seleksi ............................................. ............................................. 183
A.9.5 Pernyataan Iterasi ............................................. .............................................. 183
A.9.6 Pernyataan Langsung ............................................. .................................................. .. 184
A.10 Deklarasi Eksternal ............................................. ................................................. 184
A.10.1 Definisi Fungsi ............................................. ............................................ 185
A.10.2 Deklarasi Eksternal ............................................. .......................................... 186
A.11 Lingkup dan Keterkaitan ............................................ .................................................. .... 186
A.11.1 Lingkup Leksikal ............................................. .................................................. .... 187
A.11.2 Linkage .................................................. .................................................. ............ 187
A.12 Pemrosesan Ulang .................................................. .................................................. .......... 187
A.12.1 Urutan Trigraph ............................................. ............................................. 188
A.12.2 Penyambungan Garis ............................................. .................................................. ..... 188
A.12.3 Definisi dan Perluasan Makro ........................................... ............................ 188
A.12.4 Inklusi File ............................................. .................................................. ..... 190
A.12.5 Kompilasi Bersyarat ............................................. .......................................... 191
A.12.6 Kontrol Saluran ............................................. .................................................. ...... 192
A.12.7 Pembuatan Kesalahan ............................................. ................................................. 192
A.12.8 Pragmas .................................................. .................................................. ............ 192
A.12.9 Arahan null ............................................. .................................................. ..... 192
A.12.10 Nama yang ditentukan sebelumnya ............................................. ............................................... 192
A.13 Tata Bahasa .............................................. .................................................. ................. 193
Lampiran B - Perpustakaan Standar ............................................. ............................................... 199
B.1 Input dan Output: <stdio.h> ...................................... ................................................ 199
B.1.1 Operasi File ............................................. .................................................. .... 199
B.1.2 Output Terformat ............................................. .................................................. 200
B.1.3 Input Terformat ............................................. .................................................. ... 202
B.1.4 Fungsi Input dan Output Karakter .............................................. ...................... 203
B.1.5 Fungsi Input dan Output Langsung .............................................. ............................ 204
B.1.6 Fungsi Penentuan Posisi File ............................................ ....................................... 204
B.1.7 Fungsi Kesalahan ............................................. .................................................. ... 205
B.2 Tes Kelas Karakter: <ctype.h> ...................................... ......................................... 205
B.3 Fungsi String: <string.h> ....................................... ............................................... 205
B.4 Fungsi Matematika: <math.h> ....................................... ..................................... 206
B.5 Fungsi Utilitas: <stdlib.h> ....................................... ............................................... 207
B.6 Diagnostik: <assert.h> ........................................ .................................................. .... 209
B.7 Daftar Argumen Variabel: <stdarg.h> .......................................... ................................... 209
B.8 Lompatan Non-Lokal: <setjmp.h> ..................................... ............................................... 210
B.9 Sinyal: <signal.h> ........................................ .................................................. .......... 210
B.10 Fungsi Tanggal dan Waktu: <time.h> ..................................... .................................... 210
B.11 Batas yang ditentukan implementasi: <Limit.h> dan <float.h> ............................... .......... 212
Lampiran C - Ringkasan Perubahan ............................................ ......................................... 214

Halaman 5

Kata pengantar
Dunia komputasi telah mengalami revolusi sejak penerbitan The C Programming
Bahasa pada tahun 1978. Komputer besar jauh lebih besar, dan komputer pribadi memiliki kemampuan
saingan itu mainframe satu dekade lalu. Selama ini, C telah berubah juga, meski hanya
sederhana, dan telah menyebar jauh melampaui asalnya sebagai bahasa operasi UNIX
sistem.
Semakin populernya C, perubahan bahasa selama bertahun-tahun, dan penciptaan
kompiler oleh kelompok-kelompok yang tidak terlibat dalam desainnya, digabungkan untuk menunjukkan kebutuhan akan lebih banyak
definisi bahasa yang lebih tepat dan kontemporer dari pada edisi pertama buku ini
disediakan. Pada tahun 1983, Institut Standar Nasional Amerika (ANSI) membentuk komite
yang tujuannya adalah untuk menghasilkan `definisi yang tidak ambigu dan mandiri dari mesin
bahasa C '', sambil tetap mempertahankan semangatnya. Hasilnya adalah standar ANSI untuk C.

Standar meresmikan konstruksi yang diisyaratkan tetapi tidak dijelaskan dalam edisi pertama,
khususnya penugasan dan enumerasi struktur. Ini menyediakan bentuk fungsi baru
deklarasi yang memungkinkan pemeriksaan silang definisi dengan penggunaan. Ini menentukan perpustakaan standar,
dengan serangkaian fungsi untuk melakukan input dan output, manajemen memori,
manipulasi string, dan tugas serupa. Itu membuat perilaku fitur yang tidak tepat
dijabarkan dalam definisi asli, dan pada saat yang sama menyatakan secara eksplisit aspek mana dari
bahasa tetap bergantung pada mesin.

Edisi Kedua Bahasa Pemrograman C ini menggambarkan C sebagaimana didefinisikan oleh ANSI
standar. Meskipun kami telah mencatat tempat-tempat di mana bahasa telah berevolusi, kami telah memilih
untuk menulis secara eksklusif dalam bentuk baru. Sebagian besar, ini tidak membuat perbedaan yang signifikan;
perubahan yang paling terlihat adalah bentuk baru dari deklarasi dan definisi fungsi. Modern
kompiler sudah mendukung sebagian besar fitur standar.

Kami telah mencoba mempertahankan singkatnya edisi pertama. C bukan bahasa yang besar, dan itu tidak baik
disajikan oleh sebuah buku besar. Kami telah meningkatkan eksposisi fitur-fitur penting, seperti pointer,
yang merupakan pusat pemrograman C. Kami telah menyempurnakan contoh asli, dan telah menambahkan yang baru
contoh dalam beberapa bab. Misalnya, pengobatan deklarasi yang rumit adalah
ditambah dengan program yang mengubah deklarasi menjadi kata-kata dan sebaliknya. Seperti sebelumnya, semuanya
contoh telah diuji langsung dari teks, yang dalam bentuk yang dapat dibaca mesin.

Lampiran A, manual referensi, bukanlah standar, tetapi upaya kami untuk menyampaikan hal-hal yang penting
dari standar di ruang yang lebih kecil. Ini dimaksudkan untuk pemahaman yang mudah oleh programmer, tetapi tidak
sebagai definisi untuk penulis kompiler - peran yang seharusnya dimiliki oleh standar itu sendiri.
Lampiran B adalah ringkasan dari fasilitas perpustakaan standar. Itu juga dimaksudkan untuk referensi
oleh programmer, bukan pelaksana. Lampiran C adalah ringkasan singkat dari perubahan dari
versi asli.

Seperti yang kami katakan dalam kata pengantar untuk edisi pertama, C `` sesuai dengan pengalaman seseorang yang tumbuh ''.
Dengan pengalaman lebih dari satu dekade, kami masih merasakan hal itu. Kami berharap buku ini akan membantu Anda
belajar C dan menggunakannya dengan baik.
Kami sangat berhutang budi kepada teman-teman yang membantu kami memproduksi edisi kedua ini. Jon Bently,
Doug Gwyn, Doug McIlroy, Peter Nelson, dan Rob Pike memberi kami komentar perseptif tentang
hampir setiap halaman naskah konsep. Kami bersyukur untuk membaca dengan cermat oleh Al Aho, Dennis
Allison, Joe Campbell, GR Emlin, Karen Fortgang, Allen Holub, Andrew Hume, Dave
Kristol, John Linderman, Dave Prosser, Gene Spafford, dan Chris van Wyk. Kami juga menerima
saran bermanfaat dari Bill Cheswick, Mark Kernighan, Andy Koenig, Robin Lake, Tom

Halaman 6

7
London, Jim Reeds, Clovis Tondo, dan Peter Weinberger. Dave Prosser menjawab banyak
pertanyaan rinci tentang standar ANSI. Kami menggunakan penerjemah C ++ Bjarne Stroustrup
ekstensif untuk pengujian lokal dari program kami, dan Dave Kristol memberi kami ANSI C
kompiler untuk pengujian akhir. Rich Drechsler sangat membantu dalam mengatur huruf.

Terima kasih tulus kami untuk semua.

Brian W. Kernighan
Dennis M. Ritchie

Halaman 7

8
Kata pengantar untuk edisi pertama
C adalah bahasa pemrograman tujuan umum dengan fitur ekonomi ekspresi, modern
kontrol aliran dan struktur data, dan sekumpulan operator yang kaya. C bukan `` level yang sangat tinggi ''
bahasa, atau bahasa `` besar '', dan tidak dikhususkan untuk area aplikasi tertentu. Tapi itu
tidak adanya batasan dan sifat umumnya membuatnya lebih mudah dan efektif untuk banyak tugas
dari bahasa yang seharusnya lebih kuat.
C pada awalnya dirancang untuk dan diimplementasikan pada sistem operasi UNIX pada DEC
PDP-11, oleh Dennis Ritchie. Sistem operasi, kompiler C, dan pada dasarnya semua UNIX
program aplikasi (termasuk semua perangkat lunak yang digunakan untuk menyiapkan buku ini) ditulis dalam bahasa
C. Kompiler produksi juga ada untuk beberapa mesin lain, termasuk Sistem IBM / 370,
Honeywell 6000, dan Interdata 8/32. C tidak terikat pada perangkat keras atau sistem tertentu,
Namun, dan mudah untuk menulis program yang akan berjalan tanpa perubahan pada mesin apa pun itu
mendukung C.

Buku ini dimaksudkan untuk membantu pembaca mempelajari cara memprogram dalam C. Buku ini berisi tutorial
perkenalan untuk mendapatkan pengguna baru dimulai sesegera mungkin, pisahkan bab pada masing-masing utama
fitur, dan manual referensi. Sebagian besar perawatan didasarkan pada membaca, menulis dan
merevisi contoh, bukan hanya pada pernyataan aturan. Sebagian besar, contohnya adalah
lengkap, program nyata daripada fragmen yang terisolasi. Semua contoh telah diuji secara langsung
dari teks, yang dalam bentuk yang bisa dibaca mesin. Selain itu menunjukkan cara membuat penggunaan yang efektif
bahasa, kami juga telah mencoba jika memungkinkan untuk menggambarkan algoritma dan prinsip yang berguna
gaya dan desain suara yang bagus.

Buku ini bukan manual pemrograman pengantar; itu mengasumsikan beberapa keakraban dengan dasar
konsep pemrograman seperti variabel, pernyataan tugas, loop, dan fungsi.
Meskipun demikian, seorang programmer pemula harus dapat membaca bersama dan mengambil bahasa,
meskipun akses ke kolega yang lebih berpengetahuan akan membantu.

Dalam pengalaman kami, C telah terbukti menjadi bahasa yang menyenangkan, ekspresif, dan serbaguna untuk semua orang
berbagai program. Ini mudah dipelajari, dan ia dipakai dengan baik saat pengalaman dengan itu tumbuh. Kita
Semoga buku ini membantu Anda menggunakannya dengan baik.

Kritik dan saran bijaksana dari banyak teman dan kolega telah menambahkan sangat
buku ini dan untuk kesenangan kita dalam menulisnya. Secara khusus, Mike Bianchi, Jim Blue, Stu Feldman,
Doug McIlroy Bill Roome, Bob Rosin dan Larry Rosler semua membaca banyak volume dengan hati-hati.
Kami juga berhutang budi kepada Al Aho, Steve Bourne, Dan Dvorak, Chuck Haley, Debbie Haley,
Marion Harris, Rick Holt, Steve Johnson, John Mashey, Bob Mitze, Ralph Muha, Peter
Nelson, Elliot Pinson, Bill Plauger, Jerry Spivack, Ken Thompson, dan Peter Weinberger untuk
komentar bermanfaat di berbagai tahap, dan untuk Mile Lesk dan Joe Ossanna untuk yang tak ternilai
bantuan pengaturan huruf.

Brian W. Kernighan
Dennis M. Ritchie

Halaman 8

Bab 1 - Pendahuluan Tutorial


Mari kita mulai dengan pengantar cepat dalam C. Tujuan kami adalah untuk menunjukkan elemen-elemen penting dari
bahasa dalam program nyata, tetapi tanpa terjebak dalam detail, aturan, dan pengecualian.
Pada titik ini, kami tidak mencoba untuk menjadi lengkap atau bahkan tepat (kecuali bahwa contohnya adalah
dimaksudkan untuk menjadi benar). Kami ingin membuat Anda secepat mungkin ke titik di mana Anda bisa
menulis program yang bermanfaat, dan untuk itu kita harus berkonsentrasi pada dasar-dasar: variabel dan
konstanta, aritmatika, aliran kontrol, fungsi, dan dasar-dasar input dan output. Kita
sengaja meninggalkan bab ini fitur C yang penting untuk menulis lebih besar
program. Ini termasuk pointer, struktur, sebagian besar set kaya operator C, beberapa kontrol-
pernyataan aliran, dan pustaka standar.
Pendekatan ini dan kelemahannya. Yang paling penting adalah bahwa cerita lengkap tentang hal tertentu
fitur tidak ditemukan di sini, dan tutorial, dengan menjadi singkat, juga dapat menyesatkan. Dan karena
contoh tidak menggunakan kekuatan penuh C, mereka tidak sesingkat dan seanggun mungkin
menjadi. Kami telah mencoba meminimalkan efek ini, tetapi berhati-hatilah. Kelemahan lainnya adalah nanti
bab tentu akan mengulang beberapa bab ini. Kami berharap pengulangan ini akan membantu Anda
lebih dari itu mengganggu.

Bagaimanapun, pemrogram berpengalaman harus dapat mengekstrapolasi dari materi dalam hal ini
bab untuk kebutuhan pemrograman mereka sendiri. Pemula harus melengkapinya dengan menulis kecil,
program serupa mereka sendiri. Kedua kelompok dapat menggunakannya sebagai kerangka kerja untuk menggantung
deskripsi lebih rinci yang dimulai pada Bab 2 .

1.1 Memulai
Satu-satunya cara untuk mempelajari bahasa pemrograman baru adalah dengan menulis program di dalamnya. Pertama
program untuk menulis adalah sama untuk semua bahasa:
Cetak kata-katanya
Halo Dunia

Ini adalah rintangan besar; untuk melompati itu Anda harus dapat membuat teks program di suatu tempat,
kompilasi dengan sukses, muat, jalankan, dan cari tahu kemana output Anda pergi. Dengan ini
Rincian mekanis dikuasai, semuanya relatif mudah.

Di C, program untuk mencetak ` hello, world '' adalah

#termasuk <stdio.h>

utama()
{
printf ("hello, world \ n");
}

Cara menjalankan program ini tergantung pada sistem yang Anda gunakan. Sebagai contoh spesifik, pada
sistem operasi UNIX Anda harus membuat program dalam file yang namanya berakhiran `` .c '',
seperti hello.c , lalu kompilasi dengan perintah
cc hello.c

Jika Anda belum merusak apa pun, seperti menghilangkan karakter atau salah mengeja sesuatu, the
kompilasi akan dilanjutkan secara diam-diam, dan membuat file yang dapat dieksekusi disebut a.out . Jika Anda menjalankan a.out
dengan mengetikkan perintah
keluar
itu akan mencetak

Halaman 9

10

Halo Dunia
Pada sistem lain, aturannya akan berbeda; hubungi ahli setempat.
Sekarang, untuk beberapa penjelasan tentang program itu sendiri. Program AC, berapapun ukurannya, terdiri
dari fungsi dan variabel . Fungsi berisi pernyataan yang menentukan komputasi
operasi yang harus dilakukan, dan variabel menyimpan nilai yang digunakan selama perhitungan. Fungsi C adalah
seperti subrutin dan fungsi dalam Fortran atau prosedur dan fungsi Pascal. Kami
Contohnya adalah fungsi bernama main . Biasanya Anda bebas memberikan fungsi apa pun
nama yang Anda suka, tetapi ` utama 'adalah istimewa - program Anda mulai dijalankan pada awal
utama. Ini berarti bahwa setiap program harus memiliki tempat utama .

main biasanya akan memanggil fungsi lain untuk membantu melakukan tugasnya, beberapa yang Anda tulis, dan lainnya
dari perpustakaan yang disediakan untuk Anda. Baris pertama dari program ini,

#termasuk <stdio.h>
memberitahu kompiler untuk memasukkan informasi tentang pustaka input / output standar; garis
muncul di awal banyak file sumber C. Perpustakaan standar dijelaskan pada Bab 7
dan Lampiran B .
Salah satu metode untuk mengkomunikasikan data antar fungsi adalah untuk menyediakan fungsi panggilan a
daftar nilai, disebut argumen , ke fungsi yang dipanggilnya. Tanda kurung setelah nama fungsi
mengelilingi daftar argumen. Dalam contoh ini, main didefinisikan sebagai fungsi yang mengharapkan no
argumen, yang ditunjukkan oleh daftar kosong () .

#termasuk <stdio.h> termasuk informasi tentang standar


Perpustakaan
utama() mendefinisikan fungsi yang disebut main
yang tidak menerima nilai argumen
{ pernyataan utama terlampir dalam kawat gigi
printf ("hello, world \ n"); fungsi perpustakaan panggilan utama printf
untuk mencetak urutan karakter ini
} \ n mewakili karakter baris baru

Program C pertama

Pernyataan suatu fungsi terlampir dalam kurung {} . Fungsi utama hanya berisi satu
pernyataan,

printf ("hello, world \ n");


Suatu fungsi dipanggil dengan menamainya, diikuti oleh daftar argumen yang dipatenkan, jadi ini memanggil
fungsi printf dengan argumen "halo, dunia \ n" . printf adalah fungsi perpustakaan itu
mencetak output, dalam hal ini string karakter di antara tanda kutip.
Urutan karakter dalam tanda kutip ganda, seperti "halo, dunia \ n" , disebut karakter
string atau string konstan . Untuk saat ini, satu-satunya penggunaan string karakter adalah sebagai
argumen untuk printf dan fungsi lainnya.

Urutan \ n dalam string adalah notasi C untuk karakter baris baru , yang ketika dicetak
memajukan output ke margin kiri pada baris berikutnya. Jika Anda meninggalkan \ n (yang berharga
Percobaan), Anda akan menemukan bahwa tidak ada garis muka setelah output dicetak. Kamu harus
gunakan \ n untuk memasukkan karakter baris baru dalam argumen printf ; jika Anda mencoba sesuatu seperti

printf ("halo, dunia


");

Halaman 10

11
kompiler C akan menghasilkan pesan kesalahan.
printf tidak pernah memasok karakter baris baru secara otomatis, sehingga beberapa panggilan dapat digunakan untuk membangun
sebuah garis output secara bertahap. Program pertama kami bisa saja ditulis

#termasuk <stdio.h>

utama()
{
printf ("halo,");
printf ("dunia");
printf ("\ n");
}
untuk menghasilkan output yang identik.
Perhatikan bahwa \ n hanya mewakili satu karakter. Sebuah urutan escape seperti \ n memberikan
mekanisme umum dan diperluas untuk mewakili karakter yang sulit diketik atau tidak terlihat. Antara
yang lain yang disediakan C adalah \ t untuk tab, \ b untuk backspace, \ " untuk kutipan ganda dan \\ untuk
backslash itu sendiri. Ada daftar lengkap di Bagian 2.3 .

Latihan 1-1. Jalankan program `` hello, world '' di sistem Anda. Bereksperimenlah dengan keluar
bagian dari program, untuk melihat pesan kesalahan apa yang Anda dapatkan.

Latihan 1-2. Eksperimen untuk mengetahui apa yang terjadi ketika string argumen cetakan berisi
\ c , di mana c adalah beberapa karakter yang tidak tercantum di atas.

1.2 Variabel dan Ekspresi Aritmatika


Program selanjutnya menggunakan rumus o C = (5/9) ( o F-32) untuk mencetak tabel Fahrenheit berikut
suhu dan celcius atau setara Celcius:
Halaman 11

12

1 -17
20 -6
40 4
60 15
80 26
100 37
120 48
140 60
160 71
180 82
200 93
220 104
240 115
260 126
280 137
300 148
Program itu sendiri masih terdiri dari definisi fungsi tunggal bernama main . Itu lebih lama
dari yang dicetak " halo, dunia ", tetapi tidak rumit. Ini memperkenalkan beberapa yang baru
ide, termasuk komentar, deklarasi, variabel, ekspresi aritmatika, loop, dan
output yang diformat.
#termasuk <stdio.h>

/ * cetak tabel Fahrenheit-Celsius


untuk fahr = 0, 20, ..., 300 * /
utama()
{
int fahr, celsius;
int lebih rendah, atas, langkah;

lebih rendah = 0; / * batas bawah skala suhu * /


atas = 300; /* batas atas */
langkah = 20; / * ukuran langkah * /

fahr = lebih rendah;


while (fahr <= upper) {
celsius = 5 * (fahr-32) / 9;
printf ("% d \ t% d \ n", fahr, celsius);
fahr = fahr + step;
}
}
Dua garis
/ * cetak tabel Fahrenheit-Celsius
untuk fahr = 0, 20, ..., 300 * /
adalah komentar , yang dalam hal ini menjelaskan secara singkat apa yang dilakukan oleh program. Karakter apa saja
antara / * dan * / diabaikan oleh kompiler; mereka dapat digunakan secara bebas untuk membuat program
lebih mudah dimengerti. Komentar dapat muncul di mana saja di mana kosong, tab, atau baris baru dapat.
Dalam C, semua variabel harus dideklarasikan sebelum mereka digunakan, biasanya di awal
berfungsi sebelum pernyataan yang dapat dieksekusi. Sebuah deklarasi mengumumkan sifat-sifat
variabel; itu terdiri dari nama dan daftar variabel, seperti

int fahr, celsius;


int lebih rendah, atas, langkah;
Tipe int berarti bahwa variabel yang terdaftar adalah bilangan bulat; berbeda dengan float , yang artinya
floating point, yaitu angka yang mungkin memiliki bagian pecahan. Kisaran int dan float
tergantung pada mesin yang Anda gunakan; 16-bit int s, yang terletak di antara -32768 dan +32767,
adalah umum, seperti halnya 32-bit int . Angka float biasanya kuantitas 32-bit, dengan setidaknya enam
digit dan besarnya yang signifikan umumnya antara sekitar 10 -38 dan 10 38 .
C menyediakan beberapa tipe data lain selain int dan float , termasuk:

Halaman 12
13
arang karakter - satu byte
pendek integer pendek
panjang bilangan bulat panjang
titik mengambang presisi ganda ganda
Ukuran benda-benda ini juga tergantung pada mesin. Ada juga susunan , struktur dan
serikat ini tipe dasar, pointer kepada mereka, dan fungsi yang mengembalikan mereka, semua yang kita
akan bertemu pada waktunya.

Perhitungan dalam program konversi suhu dimulai dengan pernyataan penugasan

lebih rendah = 0;
atas = 300;
langkah = 20;
yang mengatur variabel ke nilai awal mereka. Pernyataan individual diakhiri oleh
titik koma.
Setiap baris tabel dihitung dengan cara yang sama, jadi kami menggunakan loop yang berulang sekali per output
baris; ini adalah tujuan dari while loop

while (fahr <= upper) {


...
}
The sedangkan lingkaran beroperasi sebagai berikut: Kondisi di kurung diuji. Jika itu benar ( fahr
kurang dari atau sama dengan atas ), badan loop (tiga pernyataan terlampir dalam kurung) adalah
dieksekusi. Kemudian kondisinya diuji kembali, dan jika benar, tubuh dieksekusi lagi. Saat ujian
menjadi false ( fahr melebihi atas ) loop berakhir, dan eksekusi berlanjut pada pernyataan
yang mengikuti loop. Tidak ada pernyataan lebih lanjut dalam program ini, jadi itu berakhir.
Tubuh sebentar dapat menjadi satu atau lebih pernyataan terlampir di kawat gigi, seperti pada suhu
konverter, atau satu pernyataan tanpa kawat gigi, seperti pada

sementara (i <j)
i = 2 * i;
Dalam kedua kasus, kami akan selalu membuat indentasi pernyataan yang dikontrol oleh sementara oleh satu tab stop
(yang kami tunjukkan sebagai empat spasi) sehingga Anda dapat melihat sekilas pernyataan mana yang ada di dalamnya
putaran. Lekukan menekankan struktur logis dari program. Meskipun C
kompiler tidak peduli tentang bagaimana suatu program terlihat, lekukan yang tepat dan spasi sangat penting
dalam membuat program mudah dibaca orang. Kami merekomendasikan menulis hanya satu pernyataan per
baris, dan menggunakan kosong di sekitar operator untuk memperjelas pengelompokan. Posisi kawat gigi kurang
penting, meskipun orang memiliki keyakinan yang kuat. Kami telah memilih salah satu dari beberapa yang populer
gaya. Pilih gaya yang sesuai dengan Anda, lalu gunakan secara konsisten.
Sebagian besar pekerjaan dilakukan dalam tubuh loop. Suhu Celsius dihitung dan
ditugaskan ke variabel celsius oleh pernyataan itu

celsius = 5 * (fahr-32) / 9;
Alasan untuk mengalikan dengan 5 dan membaginya dengan 9 bukan hanya mengalikannya dengan 5/9 adalah bahwa dalam
C, seperti dalam banyak bahasa lain, pembagian integer terpotong : setiap bagian pecahan dibuang.
Karena 5 dan 9 adalah bilangan bulat. 5/9 akan terpotong ke nol dan semua suhu Celcius
akan dilaporkan sebagai nol.
Contoh ini juga menunjukkan sedikit lebih banyak tentang cara kerja printf . printf adalah tujuan umum
fungsi format output, yang akan kami jelaskan secara rinci dalam Bab 7 . Argumen pertamanya adalah a
string karakter yang akan dicetak, dengan masing-masing % menunjukkan di mana salah satu dari yang lain (kedua, ketiga,

Halaman 13

14
...) argumen harus diganti, dan dalam bentuk apa harus dicetak. Misalnya, % d
menentukan argumen integer, demikian pernyataan itu

printf ("% d \ t% d \ n", fahr, celsius);


menyebabkan nilai dua bilangan bulat fahr dan celsius dicetak, dengan tab ( \ t ) di antaranya
mereka.
Setiap % konstruksi dalam argumen pertama printf dipasangkan dengan yang sesuai kedua
argumen, argumen ketiga, dll .; mereka harus cocok dengan benar berdasarkan nomor dan jenis, atau Anda akan melakukannya
dapatkan jawaban yang salah.

Omong-omong, printf bukan bagian dari bahasa C; tidak ada input atau output yang didefinisikan dalam C
diri. printf hanyalah fungsi yang berguna dari fungsi standar library yang biasanya
dapat diakses oleh program C. Perilaku printf didefinisikan dalam standar ANSI, namun,
jadi propertinya harus sama dengan kompiler dan pustaka yang sesuai dengan
standar.

Untuk berkonsentrasi pada C itu sendiri, kita tidak banyak bicara tentang input dan output sampai bab 7 .
Secara khusus, kami akan menunda input yang diformat hingga saat itu. Jika Anda harus memasukkan angka, baca
diskusi fungsi scanf di Bagian 7.4 . scanf seperti printf , kecuali membaca
input alih-alih menulis output.

Ada beberapa masalah dengan program konversi suhu. Yang lebih sederhana adalah
bahwa outputnya tidak terlalu cantik karena jumlahnya tidak dibenarkan. Itu mudah diperbaiki; jika
kami menambah setiap % d dalam pernyataan printf dengan lebar, angka-angka yang dicetak akan tepat-
dibenarkan di bidangnya. Sebagai contoh, kita dapat mengatakan

printf ("% 3d% 6d \ n", fahr, celsius);


untuk mencetak angka pertama dari setiap baris dalam bidang yang lebarnya tiga digit, dan yang kedua di bidang yang enam
digit lebar, seperti ini:

0 -17
20 -6
40 4
60 15
80 26
100 37
...
Masalah yang lebih serius adalah karena kita telah menggunakan bilangan bulat aritmatika, Celcius
suhu tidak terlalu akurat; misalnya, 0 o F sebenarnya sekitar -17.8 o C, bukan -17. Mendapatkan
jawaban yang lebih akurat, kita harus menggunakan aritmatika floating-point bukan bilangan bulat. Ini
membutuhkan beberapa perubahan dalam program. Ini adalah versi kedua:

#termasuk <stdio.h>

/ * cetak tabel Fahrenheit-Celsius


untuk fahr = 0, 20, ..., 300; versi floating-point * /
utama()
{
float fahr, celsius;
mengapung lebih rendah, atas, langkah;

lebih rendah = 0; / * batas bawah skala temperatuire * /


atas = 300; /* batas atas */
langkah = 20; / * ukuran langkah * /

fahr = lebih rendah;


while (fahr <= upper) {
celsius = (5.0 / 9.0) * (fahr-32.0);
printf ("% 3.0f% 6.1f \ n", fahr, celsius);

Halaman 14

15
fahr = fahr + step;
}
}
Ini hampir sama dengan sebelumnya, kecuali bahwa fahr dan celsius dinyatakan mengambang dan
rumus untuk konversi ditulis dengan cara yang lebih alami. Kami tidak dapat menggunakan 5/9 di
versi sebelumnya karena pembagian integer akan memotongnya ke nol. Titik desimal dalam a
konstanta menunjukkan bahwa itu adalah titik apung, jadi 5.0 / 9.0 tidak terpotong karena itu
rasio dua nilai floating-point.
Jika operator aritmatika memiliki operan bilangan bulat, operasi bilangan bulat dilakukan. Jika
operator aritmatika memiliki satu operan titik-mengambang dan satu operan bilangan bulat, namun
integer akan dikonversi ke floating point sebelum operasi dilakukan. Kalau kita sudah menulis
(fahr-32) , 32 akan secara otomatis dikonversi ke floating point. Meski begitu, menulis
konstanta floating-point dengan titik desimal eksplisit bahkan ketika mereka memiliki nilai integral
menekankan sifat titik apung mereka untuk pembaca manusia.

Aturan terperinci untuk kapan bilangan bulat dikonversi ke titik mengambang ada di Bab 2 . Untuk sekarang,
perhatikan tugas itu

fahr = lebih rendah;


dan ujiannya

sementara (fahr <= atas)


juga bekerja dengan cara alami - int dikonversi menjadi float sebelum operasi dilakukan.
The printf konversi spesifikasi % 3.0f mengatakan bahwa angka floating-point (disini fahr ) adalah untuk
dicetak setidaknya tiga karakter lebar, tanpa titik desimal dan tanpa angka pecahan. % 6.1f
menjelaskan nomor lain ( celsius ) yang akan dicetak setidaknya enam karakter, dengan 1
digit setelah titik desimal. Outputnya terlihat seperti ini:
0 -17,8
20 -6,7
40 4.4
...
Lebar dan presisi dapat dihilangkan dari suatu spesifikasi: % 6f mengatakan bahwa nomornya adalah pada
lebar setidaknya enam karakter; % .2f menentukan dua karakter setelah titik desimal, tetapi lebarnya
tidak dibatasi; dan % f hanya mengatakan untuk mencetak angka sebagai titik mengambang.
% d cetak sebagai bilangan bulat desimal
% 6d cetak sebagai bilangan bulat desimal, lebar minimal 6 karakter
% f cetak sebagai titik mengambang
% 6f cetak sebagai titik apung, lebar sedikitnya 6 karakter
% .2f cetak sebagai titik mengambang, 2 karakter setelah titik desimal
% 6.2f cetak sebagai titik mengambang, setidaknya 6 lebar dan 2 setelah titik desimal

Antara lain, printf juga mengenali % o untuk oktal, % x untuk heksadesimal, % c untuk karakter, % s
untuk string karakter dan %% untuk dirinya sendiri.

Latihan 1-3. Ubah program konversi suhu untuk mencetak judul di atas tabel.

Latihan 1-4. Tulis program untuk mencetak tabel Celsius hingga Fahrenheit yang sesuai.

1.3 Pernyataan untuk


Ada banyak cara berbeda untuk menulis program untuk tugas tertentu. Mari kita coba variasi
pada konverter suhu.

#termasuk <stdio.h>

Halaman 15

16
/ * cetak tabel Fahrenheit-Celsius * /
utama()
{
int fahr;

untuk (fahr = 0; fahr <= 300; fahr = fahr + 20)


printf ("% 3d% 6.1f \ n", fahr, (5.0 / 9.0) * (fahr-32));
}
Ini menghasilkan jawaban yang sama, tetapi tentu saja terlihat berbeda. Satu perubahan besar adalah
penghapusan sebagian besar variabel; hanya fahr yang tersisa, dan kami telah membuatnya menjadi int . Itu
batas bawah dan atas dan ukuran langkah hanya muncul sebagai konstanta dalam pernyataan for , itu sendiri a
konstruksi baru, dan ekspresi yang menghitung suhu Celcius sekarang muncul sebagai
argumen ketiga printf bukannya pernyataan penugasan terpisah.
Perubahan terakhir ini adalah contoh dari aturan umum - dalam konteks apa pun di mana ia diizinkan untuk digunakan
nilai beberapa jenis, Anda dapat menggunakan ekspresi yang lebih rumit dari jenis itu. Sejak yang ketiga
argumen printf harus merupakan nilai titik-mengambang yang cocok dengan % 6.1f , titik apung
Ekspresi dapat terjadi di sini.

The untuk pernyataan loop, generalisasi dari sementara . Jika Anda membandingkannya dengan yang sebelumnya
sementara , operasinya harus jelas. Di dalam tanda kurung, ada tiga bagian, dipisahkan oleh
titik koma. Bagian pertama, inisialisasi

fahr = 0
Halaman 16

17
dilakukan sekali, sebelum loop yang tepat dimasukkan. Bagian kedua adalah
tes atau kondisi yang mengontrol loop:

fahr <= 300


Kondisi ini dievaluasi; jika itu benar, badan loop (di sini satu ptintf ) adalah
dieksekusi. Kemudian langkah kenaikannya

fahr = fahr + 20
dieksekusi, dan kondisi dievaluasi kembali. Loop berakhir jika kondisinya telah menjadi
Salah. Seperti halnya while , badan loop dapat berupa pernyataan tunggal atau grup
pernyataan terlampir dalam kurung kurawal. Inisialisasi, kondisi dan penambahan dapat berupa apa saja
ekspresi.
Pilihan antara sementara dan untuk itu sewenang-wenang, berdasarkan yang tampaknya lebih jelas. The untuk adalah
biasanya sesuai untuk loop di mana inisialisasi dan kenaikan adalah pernyataan tunggal dan
terkait secara logis, karena lebih kompak daripada sementara dan menjaga pernyataan kontrol loop
bersama di satu tempat.

Latihan 1-5. Ubah program konversi suhu untuk mencetak tabel dalam urutan terbalik,
yaitu, dari 300 derajat ke 0.

1.4 Konstanta Simbolik


Pengamatan terakhir sebelum kita meninggalkan konversi suhu selamanya. Adalah praktik buruk untuk mengubur
`` angka ajaib '' seperti 300 dan 20 dalam suatu program; mereka menyampaikan sedikit informasi kepada seseorang yang
mungkin harus membaca program nanti, dan mereka sulit untuk berubah secara sistematis. Satu
cara untuk berurusan dengan angka ajaib adalah dengan memberi mereka nama yang bermakna. Garis #define mendefinisikan a
nama simbolik atau konstanta simbolik untuk menjadi string karakter tertentu:
# tentukan daftar penggantian nama

Setelah itu, setiap kejadian nama (bukan dalam tanda kutip dan bukan bagian dari nama lain) akan menjadi
diganti dengan teks pengganti yang sesuai . The nama memiliki bentuk yang sama sebagai variabel
name: urutan huruf dan angka yang dimulai dengan huruf. The teks pengganti bisa
urutan karakter apa pun; itu tidak terbatas pada angka.

#termasuk <stdio.h>

#define RENDAH 0 / * batas bawah tabel * /


#define UPPER 300 / * batas atas * /
#definisikan LANGKAH 20 / * ukuran langkah * /

/ * cetak tabel Fahrenheit-Celsius * /


utama()
{
int fahr;

untuk (fahr = RENDAH; fahr <= UPPER; fahr = fahr + LANGKAH)


printf ("% 3d% 6.1f \ n", fahr, (5.0 / 9.0) * (fahr-32));
}
Kuantitas RENDAH , UPPER , dan LANGKAH adalah konstanta simbolik, bukan variabel, jadi tidak
muncul di deklarasi. Nama konstanta simbolis secara konvensional ditulis dalam huruf besar demikian
mereka dapat dengan mudah dibedakan dari nama variabel huruf kecil. Perhatikan bahwa tidak ada
titik koma di akhir garis #define .

1.5 Input dan Output Karakter


Kami akan mempertimbangkan sekumpulan program terkait untuk memproses data karakter. Kamu akan
menemukan bahwa banyak program hanyalah versi perluasan dari prototipe yang kami diskusikan di sini.
Halaman 17
18
Model input dan output yang didukung oleh perpustakaan standar sangat sederhana. Input teks atau
output, terlepas dari mana asalnya atau ke mana ia pergi, ditangani sebagai aliran
karakter. Sebuah aliran teks adalah urutan karakter dibagi menjadi garis; setiap baris terdiri dari
nol atau lebih karakter diikuti oleh karakter baris baru. Ini adalah tanggung jawab perpustakaan untuk
membuat setiap input atau aliran output mengkonfirmasi model ini; programmer C menggunakan kebutuhan perpustakaan
tidak khawatir tentang bagaimana garis diwakili di luar program.

Perpustakaan standar menyediakan beberapa fungsi untuk membaca atau menulis satu karakter sekaligus,
dimana getchar dan putchar adalah yang paling sederhana. Setiap kali dipanggil, getchar membaca selanjutnya
masukan karakter dari aliran teks dan kembalikan itu sebagai nilainya. Itu setelah

c = getchar ();
variabel c berisi karakter input selanjutnya. Karakter biasanya berasal dari
papan ketik; input dari file dibahas dalam Bab 7 .
Fungsi putchar mencetak karakter setiap kali namanya:

putchar (c);
mencetak isi variabel integer c sebagai karakter, biasanya di layar. Panggilan untuk
putchar dan printf dapat disisipkan; output akan muncul dalam urutan panggilan
dibuat untuk.
1.5.1 Menyalin File
Diberi getchar dan putchar , Anda dapat menulis sejumlah kode berguna yang mengejutkan tanpa
mengetahui lebih banyak tentang input dan output. Contoh paling sederhana adalah program yang menyalin
inputnya ke outputnya satu karakter pada satu waktu:

baca karakter
sementara ( karakter bukan indikator akhir file )
Keluarkan karakter yang baru saja dibaca
baca karakter
Mengubah ini menjadi C memberi:

#termasuk <stdio.h>

/ * salin input ke output; Versi 1 * /


utama()
{
int c;

c = getchar ();
while (c! = EOF) {
putchar (c);
c = getchar ();
}
}
Operator relasional ! = Berarti `` tidak sama dengan ''.
Apa yang tampak sebagai karakter pada keyboard atau layar tentu saja, seperti yang lainnya,
disimpan secara internal hanya sebagai pola bit. Jenis char secara khusus dimaksudkan untuk menyimpannya
data karakter, tetapi semua tipe integer dapat digunakan. Kami menggunakan int untuk yang halus tapi penting
alasan.

Masalahnya adalah membedakan input dari data yang valid. Solusinya adalah getchar itu
mengembalikan nilai khusus ketika tidak ada lagi input, nilai yang tidak dapat dikacaukan
karakter nyata. Nilai ini disebut EOF , untuk `` end of file ''. Kita harus mendeklarasikan c sebagai tipe
cukup besar untuk menampung nilai apa pun yang didapat getchar . Kita tidak bisa menggunakan char karena c harus besar
cukup untuk menahan EOF selain dari char yang mungkin . Karena itu kami menggunakan int .

Halaman 18

19

EOF adalah integer yang didefinisikan dalam <stdio.h>, tetapi nilai numerik tertentu tidak masalah selama
itu tidak sama dengan nilai char . Dengan menggunakan konstanta simbolis, kami yakin akan hal itu
tidak ada dalam program tergantung pada nilai numerik tertentu.

Program untuk menyalin akan ditulis lebih ringkas oleh programmer C yang berpengalaman. Di
C, tugas apa pun, seperti

c = getchar ();
adalah ekspresi dan memiliki nilai, yang merupakan nilai dari sisi kiri setelah penugasan.
Ini berarti bahwa tugas dapat muncul sebagai bagian dari ekspresi yang lebih besar. Jika penugasan a
karakter ke c dimasukkan ke dalam bagian uji loop sementara , program salin dapat ditulis ini
cara:
#termasuk <stdio.h>

/ * salin input ke output; Versi 2 * /


utama()
{
int c;

while ((c = getchar ())! = EOF)


putchar (c);
}
The sementara mendapat karakter, memberikan ke c , dan kemudian menguji apakah karakter itu akhir-
sinyal dari file. Jika tidak, badan sementara dieksekusi, mencetak karakter. Itu
sementara kemudian diulang. Ketika akhir input akhirnya tercapai, sementara berakhir dan seterusnya
tidak utama .
Versi ini memusatkan input - sekarang hanya ada satu referensi untuk getchar - dan menyusut
program. Program yang dihasilkan lebih kompak, dan, setelah idiom dikuasai, lebih mudah
untuk membaca. Anda akan sering melihat gaya ini. (Mungkin terbawa suasana dan membuat tidak bisa ditembus
kode, bagaimanapun, kecenderungan bahwa kami akan mencoba untuk mengekang.)

Tanda kurung di sekitar tugas, dalam kondisi yang diperlukan. The diutamakan dari
! = lebih tinggi daripada = , yang berarti bahwa dengan tidak adanya tanda kurung tes relasional ! =
akan dilakukan sebelum penugasan = . Demikian pernyataannya

c = getchar ()! = EOF


setara dengan

c = (getchar ()! = EOF)


Ini memiliki efek pengaturan c ke 0 atau 1 yang tidak diinginkan , tergantung pada panggilan atau tidak
getchar mengembalikan file. (Lebih lanjut tentang ini di Bab 2. )
Laksanakan 1-6. Verifikasi bahwa ungkapan getchar ()! = EOF adalah 0 atau 1.

Latihan 1-7. Tulis program untuk mencetak nilai EOF .

1.5.2 Penghitungan Karakter


Program selanjutnya menghitung karakter; ini mirip dengan program salin.

#termasuk <stdio.h>

/ * hitung karakter yang dimasukkan; Versi 1 * /


utama()
{
panjang nc;

nc = 0;
while (getchar ()! = EOF)

Halaman 19

20
++ nc;
printf ("% ld \ n", nc);
}
Pernyataan
++ nc;
menyajikan operator baru, ++ , yang berarti bertambah satu . Anda bisa menulis nc = nc
+1 tetapi ++ nc lebih ringkas dan seringkali lebih efisien. Ada operator yang sesuai - untuk
decrement oleh 1. Operator ++ dan - dapat berupa operator awalan ( ++ nc ) atau postfix
operator ( nc ++ ); dua bentuk ini memiliki nilai ekspresi yang berbeda, seperti yang akan ditunjukkan pada
Bab 2 , tetapi ++ nc dan nc ++ keduanya menambah nc . Untuk saat ini kita akan tetap berpegang pada
formulir awalan.
Program penghitungan karakter mengakumulasikan penghitungannya dalam variabel panjang dan bukan int.
bilangan bulat panjang setidaknya 32 bit. Meskipun pada beberapa mesin, int dan panjang memiliki ukuran yang sama,
pada yang lain int adalah 16 bit, dengan nilai maksimum 32767, dan itu akan relatif sedikit
masukan untuk meluap penghitung int . Spesifikasi konversi % ld memberitahu printf bahwa
argumen yang sesuai adalah bilangan bulat panjang .

Dimungkinkan untuk mengatasi angka yang bahkan lebih besar dengan menggunakan ganda (presisi ganda
mengapung ). Kami juga akan menggunakan pernyataan for alih-alih sementara , untuk menggambarkan cara lain untuk menulis
putaran.

#termasuk <stdio.h>

/ * hitung karakter yang dimasukkan; Versi 2 * /


utama()
{
nc ganda;
untuk; (nc = 0; gechar ()! = EOF; ++ nc)
printf ("%. 0f \ n", nc);
}
printf menggunakan % f untuk float dan double ; % .0f menekan pencetakan titik desimal
dan bagian pecahan, yaitu nol.
Tubuh ini untuk lingkaran kosong, karena semua pekerjaan dilakukan dalam tes dan kenaikan
bagian. Tetapi aturan tata bahasa C mensyaratkan bahwa pernyataan for memiliki tubuh. Yang terisolasi
titik koma, disebut pernyataan nol , ada untuk memenuhi persyaratan itu. Kami meletakkannya di tempat yang terpisah
baris untuk membuatnya terlihat.

Sebelum kita meninggalkan program penghitungan karakter, amati bahwa jika inputnya berisi no
karakter, saat atau untuk pengujian gagal pada panggilan pertama untuk getchar , dan program
menghasilkan nol, jawaban yang benar. Ini penting. Salah satu hal yang menyenangkan tentang sementara dan untuk
adalah bahwa mereka menguji di bagian atas loop, sebelum melanjutkan dengan tubuh. Jika tidak ada
lakukan, tidak ada yang dilakukan, bahkan jika itu berarti tidak pernah melalui loop body. Program harus
bertindak cerdas saat diberi input panjang nol. Pernyataan sementara dan untuk membantu memastikan hal itu
program melakukan hal-hal yang wajar dengan syarat batas.

1.5.3 Penghitungan Baris


Program selanjutnya menghitung jalur input. Seperti yang kami sebutkan di atas, perpustakaan standar memastikan hal itu
aliran input teks muncul sebagai urutan baris, masing-masing diakhiri oleh baris baru. Karenanya,
menghitung baris hanya menghitung baris baru:
#termasuk <stdio.h>

/ * hitung baris dalam input * /


utama()

Halaman 20

21
{
int c, nl;

nl = 0;
while ((c = getchar ())! = EOF)
if (c == '\ n')
++ nl;
printf ("% d \ n", nl);
}
Tubuh sementara sekarang terdiri dari if , yang pada gilirannya mengontrol kenaikan ++ nl . Itu
jika pernyataan menguji kondisi kurung, dan jika kondisinya benar, jalankan
pernyataan (atau kelompok pernyataan dalam kurung) yang mengikuti. Kami lagi-lagi ingin menunjukkan
apa yang dikendalikan oleh apa.
Tanda sama dengan ganda == adalah notasi C untuk `` sama dengan '' (seperti Pascal tunggal = atau Fortran
.EQ. ). Simbol ini digunakan untuk membedakan uji kesetaraan dari tunggal = yang digunakan C untuk
tugas. Kata hati-hati: pendatang baru ke C sesekali menulis = ketika artinya == . Sebagai
kita akan lihat di Bab 2 , hasilnya biasanya ekspresi hukum, sehingga Anda tidak akan mendapat peringatan.

Karakter yang ditulis di antara tanda kutip tunggal mewakili nilai integer yang sama dengan angka
nilai karakter di set karakter mesin. Ini disebut konstanta karakter ,
meskipun itu hanya cara lain untuk menulis bilangan bulat kecil. Jadi, misalnya, 'A' adalah karakter
konstan; dalam karakter ASCII set nilainya 65, representasi internal karakter
A . Tentu saja, 'A' lebih disukai daripada 65 : artinya jelas, dan tidak tergantung pada a
set karakter tertentu.

Urutan escape yang digunakan dalam konstanta string juga legal dalam konstanta karakter, jadi '\ n'
singkatan dari nilai karakter baris baru, yaitu 10 dalam ASCII. Anda harus perhatikan dengan seksama
bahwa '\ n' adalah karakter tunggal, dan dalam ekspresi hanyalah bilangan bulat; di sisi lain, '\ n' adalah
konstanta string yang hanya berisi satu karakter. Topik string versus
karakter dibahas lebih lanjut dalam Bab 2 .

Latihan 1-8. Tulis sebuah program untuk menghitung kosong, tab, dan baris baru.

Latihan 1-9. Tulis program untuk menyalin inputnya ke outputnya, mengganti setiap string dari satu atau
lebih banyak kosong dengan satu kosong.

Latihan 1-10. Tulis sebuah program untuk menyalin inputnya ke outputnya, mengganti masing-masing tab dengan \ t , masing-masing
backspace by \ b , dan masing-masing backslash oleh \\ . Ini membuat tab dan spasi mundur terlihat dalam
cara yang jelas.

1.5.4 Penghitungan Kata


Yang keempat dalam rangkaian program kami yang bermanfaat menghitung garis, kata-kata, dan karakter, dengan longgar
Definisi bahwa kata adalah urutan karakter apa pun yang tidak mengandung blank, tab, atau
garis baru. Ini adalah versi sederhana dari program UNIX wc .
#termasuk <stdio.h>

#define IN 1 / * di dalam kata * /


#define OUT 0 / * di luar kata * /

/ * hitung baris, kata, dan karakter dalam input * /


utama()
{
int c, nl, nw, nc, state;

state = OUT;
nl = nw = nc = 0;
while ((c = getchar ())! = EOF) {

Halaman 21

22
++ nc;
if (c == '\ n')
++ nl;
if (c == '' || c == '\ n' || c = '\ t')
state = OUT;
selain itu jika (status == OUT) {
state = IN;
++ nw;
}
}
printf ("% d% d% d \ n", nl, nw, nc);
}
Setiap kali program menemukan karakter pertama dari sebuah kata, ia menghitung satu kata lagi. Itu
keadaan variabel mencatat apakah program saat ini dalam kata atau tidak; awalnya itu `` bukan di
kata '', yang diberi nilai OUT . Kami lebih suka konstanta simbolik IN dan OUT daripada
nilai literal 1 dan 0 karena mereka membuat program lebih mudah dibaca. Dalam sebuah program sekecil
ini, itu membuat sedikit perbedaan, tetapi dalam program yang lebih besar, peningkatan kejelasan sepadan
usaha ekstra sederhana untuk menulis seperti ini dari awal. Anda juga akan menemukan bahwa itu lebih mudah
buat perubahan ekstensif dalam program di mana angka ajaib hanya muncul sebagai konstanta simbolik.
Halaman 22

23

Garis

nl = nw = nc = 0;
set ketiga variabel menjadi nol. Ini bukan kasus khusus, tetapi konsekuensi dari kenyataan bahwa suatu
tugas adalah ekspresi dengan nilai dan tugas yang terkait dari kanan ke kiri. Itu seperti
jika kita telah menulis

nl = (nw = (nc = 0));


Operator || berarti ATAU, jadi garis
if (c == '' || c == '\ n' || c = '\ t')
mengatakan `` jika c kosong atau c adalah baris baru atau c adalah tab ''. (Ingat bahwa urutan escape \ t adalah a
representasi yang terlihat dari karakter tab.) Ada operator yang sesuai && untuk DAN; -nya
diutamakan hanya lebih tinggi dari || . Ekspresi yang dihubungkan oleh && atau || dievaluasi dari kiri ke
benar, dan dijamin bahwa evaluasi akan berhenti segera setelah kebenaran atau kepalsuan diketahui.
Jika c kosong, tidak perlu menguji apakah itu baris baru atau tab, jadi tes ini tidak
terbuat. Ini tidak terlalu penting di sini, tetapi penting dalam situasi yang lebih rumit
kita akan segera melihat.
Contoh ini juga menunjukkan yang lain , yang menentukan tindakan alternatif jika kondisi bagian dari
sebuah pernyataan if salah. Bentuk umumnya adalah

jika ( ekspresi )
pernyataan 1
lain
pernyataan 2
Satu dan hanya satu dari dua pernyataan yang terkait dengan jika ada yang dilakukan. Jika
ekspresi itu benar, pernyataan 1 dijalankan; jika tidak, pernyataan 2 dijalankan. Setiap pernyataan bisa
satu pernyataan atau beberapa di kawat gigi. Dalam program jumlah kata, satu setelah yang lain adalah
jika itu mengontrol dua pernyataan dalam kawat gigi.
Latihan 1-11. Bagaimana Anda menguji program penghitungan kata? Jenis input apa yang paling banyak
mungkin menemukan bug jika ada?

Latihan 1-12. Tulis program yang mencetak inputnya satu kata per baris.

1.6 Array
Biarkan adalah menulis program untuk menghitung jumlah kemunculan setiap digit, dari spasi putih
karakter (kosong, tab, baris baru), dan semua karakter lainnya. Ini buatan, tapi itu memungkinkan kita
untuk menggambarkan beberapa aspek C dalam satu program.
Ada dua belas kategori input, jadi lebih mudah menggunakan array untuk menampung jumlah
kemunculan setiap digit, bukan sepuluh variabel individual. Ini adalah salah satu versi dari
program:

Halaman 23

24

#termasuk <stdio.h>

/ * hitung angka, spasi putih, lainnya * /


utama()
{
int c, i, nwhite, nother;
int ndigit [10];

nwhite = nother = 0;
untuk (i = 0; i <10; ++ i)
ndigit [i] = 0;
whileif((c
(c>= =getchar
'0' && ())!
c <= ='9')
EOF)
++ ndigit [c-'0 '];
lain jika (c == '' || c == '\ n' || c == '\ t')
++ nwhite;
lain
++ nother;

printf ("digit =");


untuk (i = 0; i <10; ++ i)
printf ("% d", ndigit [i]);
printf (", spasi putih =% d, lainnya =% d \ n",
nwhite, nother);
}
Output dari program ini sendiri adalah
digit = 9 3 0 0 0 0 0 0 0 1, spasi putih = 123, lainnya = 345
Deklarasi

int ndigit [10];


mendeklarasikan ndigit sebagai array 10 integer. Subskrip array selalu dimulai dari nol dalam C, jadi
elemen adalah ndigit [0], ndigit [1], ..., ndigit [9] . Ini tercermin dalam for loop
yang menginisialisasi dan mencetak array.
Subskrip dapat berupa ekspresi integer apa pun, yang mencakup variabel integer seperti i , dan integer
konstanta.

Program khusus ini bergantung pada sifat-sifat representasi karakter dari digit.
Misalnya, tes

if (c> = '0' && c <= '9')


menentukan apakah karakter dalam c adalah digit. Jika ya, nilai numerik digit itu adalah

c - '0'
Ini hanya berfungsi jika '0', '1', ..., '9' memiliki nilai yang meningkat berturut-turut. Untungnya ini
berlaku untuk semua set karakter.
Menurut definisi, char hanya bilangan bulat kecil, sehingga variabel dan konstanta char identik dengan
int s dalam ekspresi aritmatika. Ini alami dan nyaman; misalnya c-'0 ' adalah bilangan bulat
ekspresi dengan nilai antara 0 dan 9 yang sesuai dengan karakter '0' hingga '9' yang disimpan dalam c ,
dan dengan demikian subskrip yang valid untuk array ndigit .

Keputusan untuk menentukan apakah karakter adalah digit, spasi, atau sesuatu yang lain dibuat
urutannya

if (c> = '0' && c <= '9')


++ ndigit [c-'0 '];
lain jika (c == '' || c == '\ n' || c == '\ t')
++ nwhite;

Halaman 24

25
lain
++ nother;
Pola

jika ( kondisi 1 )
pernyataan 1
lain jika ( kondisi 2 )
pernyataan 2
...
...
lain
pernyataan n
sering terjadi dalam program sebagai cara untuk mengekspresikan keputusan multi-arah. The kondisi yang
dievaluasi secara berurutan dari atas hingga beberapa kondisi terpenuhi; pada saat itu
bagian pernyataan yang sesuai dieksekusi, dan seluruh konstruksi selesai. (Apa saja
pernyataan dapat berupa beberapa pernyataan terlampir dalam kurung kurawal.) Jika tidak satu pun dari kondisi terpenuhi,
yang pernyataan setelah final lain dijalankan jika hadir. Jika yang lain dan pernyataan adalah
dihilangkan, seperti dalam program jumlah kata, tidak ada tindakan yang terjadi. Bisa ada sejumlah
lain jika ( kondisi )
pernyataan

kelompok antara awal jika dan final lain .

Sebagai soal gaya, disarankan untuk memformat konstruksi ini seperti yang telah kami tunjukkan; jika masing-masing jika
yang menjorok melewati sebelumnya lain , urutan panjang keputusan akan berbaris kanan
sisi halaman.

The beralih pernyataan, yang akan dibahas di Bab 4 , menyediakan cara lain untuk menulis multi
cara cabang yang sangat cocok ketika kondisinya adalah apakah bilangan bulat atau karakter
Ekspresi cocok dengan satu set konstanta. Sebagai kontras, kami akan menyajikan versi saklar dari
program ini di Bagian 3.4 .

Latihan 1-13. Tulis program untuk mencetak histogram panjang kata dalam inputnya. ini
mudah menggambar histogram dengan palang mendatar; orientasi vertikal lebih menantang.

Latihan 1-14. Tulis program untuk mencetak histogram dari frekuensi karakter yang berbeda
dalam inputnya.

1.7 Fungsi
Dalam C, fungsi setara dengan subrutin atau fungsi dalam Fortran, atau prosedur atau fungsi
dalam Pascal. Suatu fungsi menyediakan cara yang nyaman untuk merangkum beberapa perhitungan, yang dapat
kemudian digunakan tanpa khawatir dengan implementasinya. Dengan fungsi yang dirancang dengan benar, itu
adalah mungkin untuk mengabaikan bagaimana suatu pekerjaan dilakukan; mengetahui apa yang dilakukan sudah cukup. C membuat tuntutan
fungsi mudah, nyaman dan efisien; Anda akan sering melihat fungsi pendek didefinisikan dan dipanggil
hanya sekali, hanya karena itu mengklarifikasi beberapa bagian kode.
Sejauh ini kami hanya menggunakan fungsi seperti printf , getchar dan putchar yang telah
disediakan untuk kita; sekarang saatnya untuk menulis beberapa dari kita sendiri. Karena C tidak memiliki eksponensial
operator seperti ** dari Fortran, mari kita ilustrasikan mekanisme definisi fungsi dengan menulis
fungsi power (m, n) untuk menaikkan integer m ke daya integer positif n . Artinya, nilai
power (2,5) adalah 32. Fungsi ini bukan rutin eksponensial praktis, karena hanya menangani
kekuatan positif bilangan bulat kecil, tetapi cukup baik untuk ilustrasi. (Perpustakaan standar
berisi pow fungsi (x, y) yang menghitung x y .)

Berikut adalah fungsi kekuasaan dan program utama untuk latihan, sehingga Anda dapat melihat seluruh
struktur sekaligus.

Halaman 25

26

#termasuk <stdio.h>

int power (int m, int n);

/ * fungsi uji daya * /


utama()
{
int i;

untuk (i = 0; i <10; ++ i)
printf ("% d% d% d \ n", i, power (2, i), power (-3, i));
return 0;
}

/ * power: naikkan base ke power n-th; n> = 0 * /


int power (int base, int n)
{
int i, p;

p = 1;
untuk (i = 1; i <= n; ++ i)
p = p * basis;
return p;
}
Definisi fungsi memiliki formulir ini:

return-type function-name (deklarasi parameter, jika ada)


{
deklarasi
pernyataan
}
Definisi fungsi dapat muncul dalam urutan apa pun, dan dalam satu file sumber atau beberapa, meskipun tidak
fungsi dapat dibagi antara file. Jika program sumber muncul dalam beberapa file, Anda mungkin memilikinya
mengatakan lebih banyak untuk mengkompilasi dan memuatnya daripada jika semuanya muncul dalam satu, tetapi itu adalah sistem operasi
materi, bukan atribut bahasa. Untuk saat ini, kami akan menganggap bahwa kedua fungsi berada dalam
file yang sama, jadi apa pun yang Anda pelajari tentang menjalankan program C akan tetap berfungsi.
Fungsi daya disebut dua kali oleh utama , di telepon

printf ("% d% d% d \ n", i, power (2, i), power (-3, i));


Setiap panggilan melewati dua argumen ke kekuasaan , yang setiap kali mengembalikan bilangan bulat untuk diformat
dan dicetak. Dalam sebuah ekspresi, power (2, i) adalah bilangan bulat seperti halnya 2 dan i . (Tidak semua fungsi
menghasilkan nilai integer; kita akan membahas ini pada Bab 4. )
Baris pertama kekuasaan itu sendiri,

int power (int base, int n)


mendeklarasikan tipe dan nama parameter, dan tipe hasil yang dikembalikan fungsi.
Nama yang digunakan oleh daya untuk parameternya adalah lokal untuk daya , dan tidak terlihat oleh siapa pun
fungsi lainnya: rutinitas lain dapat menggunakan nama yang sama tanpa konflik. Ini juga berlaku untuk
variabel i dan p : i in power tidak terkait dengan i in main .
Kami biasanya akan menggunakan parameter untuk variabel bernama dalam daftar di dalam tanda kurung dalam suatu fungsi.
Istilah argumen formal dan argumen aktual terkadang digunakan untuk perbedaan yang sama.

Nilai yang dihitung daya dikembalikan ke utama oleh pernyataan return :. Ekspresi apa pun
dapat mengikuti kembali :

ekspresi kembali ;
Fungsi tidak perlu mengembalikan nilai; pernyataan kembali tanpa ekspresi menyebabkan kontrol, tetapi
tidak ada nilai yang berguna, untuk dikembalikan ke pemanggil, seperti halnya `` jatuh dari ujung '' fungsi oleh

Halaman 26

27
mencapai kurung kurawal kanan mengakhiri. Dan fungsi panggilan dapat mengabaikan nilai yang dikembalikan oleh
fungsi.
Anda mungkin telah memperhatikan bahwa ada pernyataan pengembalian di akhir utama . Karena main adalah a
berfungsi seperti yang lain, itu dapat mengembalikan nilai ke peneleponnya, yang berlaku lingkungan di
dimana program dieksekusi. Biasanya, nilai pengembalian nol menyiratkan penghentian normal;
nilai bukan nol menandakan kondisi terminasi yang tidak biasa atau salah. Demi kepentingan
kesederhanaan, kami telah menghilangkan pernyataan kembali dari fungsi utama kami sampai saat ini, tetapi
kami akan memasukkannya di akhirat, sebagai pengingat bahwa program harus mengembalikan status ke mereka
lingkungan Hidup.

Deklarasi

int power (int base, int n);


sebelum utama mengatakan bahwa kekuatan adalah fungsi yang mengharapkan dua argumen int dan mengembalikan sebuah
int . Deklarasi ini, yang disebut prototipe fungsi , harus setuju dengan definisi dan
menggunakan kekuatan . Ini adalah kesalahan jika definisi fungsi atau penggunaannya tidak setuju dengan definisi tersebut
prototipe.
nama parameter tidak harus setuju. Memang, nama parameter adalah opsional dalam suatu fungsi
prototipe, jadi untuk prototipe kita bisa menulis

int power (int, int);


Namun nama-nama yang dipilih dengan baik adalah dokumentasi yang baik, jadi kita akan sering menggunakannya.
Catatan sejarah: perubahan terbesar antara ANSI C dan versi sebelumnya adalah bagaimana fungsinya
dideklarasikan dan didefinisikan. Dalam definisi asli C, fungsi daya seharusnya
ditulis seperti ini:
Halaman 27

28

/ * power: naikkan base ke power n-th; n> = 0 * /


/ * (versi gaya lama) * /
kekuatan (basis, n)
basis int, n;
{
int i, p;

p = 1;
untuk (i = 1; i <= n; ++ i)
p = p * basis;
return p;
}
Parameter dinamai di antara tanda kurung, dan tipenya dideklarasikan sebelumnya
membuka brace kiri; parameter yang tidak dideklarasikan diambil sebagai int . (Tubuh fungsi adalah
sama seperti sebelumnya.)
Deklarasi kekuasaan di awal program akan terlihat seperti ini:

int power ();


Tidak ada daftar parameter yang diizinkan, jadi kompiler tidak dapat dengan mudah memeriksa daya yang sedang
dipanggil dengan benar. Memang, karena secara default kekuatan akan dianggap mengembalikan int , the
seluruh deklarasi mungkin dihilangkan.
Sintaks fungsi prototipe yang baru memudahkan kompiler untuk mendeteksi kesalahan
jumlah argumen atau tipenya. Gaya deklarasi dan definisi lama masih berfungsi
di ANSI C, setidaknya untuk periode transisi, tetapi kami sangat menyarankan Anda menggunakan yang baru
terbentuk ketika Anda memiliki kompiler yang mendukungnya.

Latihan 1.15. Tulis ulang program konversi suhu Bagian 1.2 untuk menggunakan fungsi
untuk konversi.

1.8 Argumen - Panggilan berdasarkan Nilai


Salah satu aspek fungsi C mungkin asing bagi programmer yang terbiasa dengan yang lain
bahasa, khususnya Fortran. Dalam C, semua argumen fungsi dilewatkan `` oleh nilai. '' Ini berarti
bahwa fungsi yang dipanggil diberi nilai-nilai argumennya dalam variabel sementara daripada
yang asli. Ini mengarah ke beberapa properti yang berbeda dari yang terlihat dengan `` panggilan dengan referensi ''
bahasa seperti Fortran atau dengan parameter var di Pascal, di mana yang disebut rutin memiliki akses
ke argumen asli, bukan salinan lokal.
Namun, panggilan berdasarkan nilai adalah aset, bukan kewajiban. Biasanya mengarah ke program yang lebih kompak
dengan lebih sedikit variabel asing, karena parameter dapat diperlakukan dengan mudah diinisialisasi
variabel lokal dalam rutin disebut. Sebagai contoh, di sini adalah versi kekuatan yang memanfaatkan
properti ini.
/ * power: naikkan base ke power n-th; n> = 0; versi 2 * /
int power (int base, int n)
{
int p;

untuk (p = 1; n> 0; --n)


p = p * basis;
return p;
}
Parameter n digunakan sebagai variabel sementara, dan dihitung mundur (a untuk loop yang berjalan
mundur) sampai menjadi nol; tidak ada lagi kebutuhan untuk variabel i . Apapun itu
dilakukan untuk n di dalam kekuasaan tidak berpengaruh pada argumen bahwa kekuasaan awalnya disebut dengan.
Bila perlu, dimungkinkan untuk mengatur fungsi untuk memodifikasi variabel dalam rutinitas pemanggilan.
Penelepon harus memberikan alamat variabel yang akan ditetapkan (secara teknis pointer ke

Halaman 28

29
variabel), dan fungsi yang dipanggil harus mendeklarasikan parameter sebagai pointer dan mengakses
variabel secara tidak langsung melaluinya. Kami akan membahas petunjuk dalam Bab 5 .

Ceritanya berbeda untuk array. Ketika nama array digunakan sebagai argumen, nilainya
diteruskan ke fungsi adalah lokasi atau alamat awal array - tidak ada
menyalin elemen array. Dengan berlangganan nilai ini, fungsi dapat mengakses dan mengubah apa pun
argumen array. Ini adalah topik dari bagian selanjutnya.

1.9 Susunan Karakter


Jenis array yang paling umum di C adalah array karakter. Untuk menggambarkan penggunaan
array karakter dan fungsi untuk memanipulasi mereka, mari kita menulis sebuah program yang membaca sekumpulan teks
garis dan cetak terpanjang. Garis besarnya cukup sederhana:

sementara ( ada garis lain )


if ( lebih panjang dari yang terpanjang sebelumnya )
( simpan )
( simpan panjangnya )
cetak garis terpanjang
Garis besar ini memperjelas bahwa program membagi secara alami menjadi beberapa bagian. Satu bagian mendapat yang baru
baris, yang lain menyimpannya, dan sisanya mengontrol proses.
Karena hal-hal membelah dengan sangat baik, akan baik untuk menulisnya juga. Karena itu, mari kita
pertama tulis fungsi getline terpisah untuk mengambil baris input berikutnya. Kami akan mencoba membuat
fungsi yang berguna dalam konteks lain. Paling tidak, getline harus mengembalikan sinyal
kemungkinan akhir file; desain yang lebih bermanfaat adalah mengembalikan panjang garis, atau nol jika
akhir file ditemui. Nol adalah pengembalian file akhir yang dapat diterima karena tidak pernah valid
panjang garis. Setiap baris teks memiliki setidaknya satu karakter; bahkan garis yang hanya berisi baris baru
panjang 1.

Ketika kami menemukan garis yang lebih panjang dari garis terpanjang sebelumnya, itu harus disimpan di suatu tempat.
Ini menyarankan fungsi kedua, salin , untuk menyalin baris baru ke tempat yang aman.

Akhirnya, kita membutuhkan program utama untuk mengontrol getline dan menyalin . Inilah hasilnya.

Halaman 29

30
#termasuk <stdio.h>
#define MAXLINE 1000 / * panjang jalur input maksimum * /

int getline (baris char [], int maxline);


membatalkan salinan (char to [], char from []);

/ * cetak jalur input terpanjang * /


utama()
{
int len; / * panjang garis saat ini * /
int max; / * panjang maksimum yang terlihat sejauh ini * /
baris char [MAXLINE]; / * jalur input saat ini * /
char terpanjang [MAXLINE]; / * garis terpanjang disimpan di sini * /

maks = 0;
while ((len = getline (line, MAXLINE))> 0)
if (len> max) {
maks = len;
copy (terpanjang, baris);
}
jika (maks> 0) / * ada garis * /
printf ("% s", terpanjang);
return 0;
}

/ * getline: baca baris ke s, panjang kembali * /


int getline (char s [], int lim)
{
int c, saya;
untuk (i = 0; i <lim-1 && (c = getchar ())! = EOF && c! = '\ n'; ++ i)
s [i] = c;
if (c == '\ n') {
s [i] = c;
++ i;
}
s [i] = '\ 0';
mengembalikan saya;
}

/ * copy: salin 'dari' ke 'ke'; anggap cukup besar * /


membatalkan salinan (char to [], char from [])
{
int i;

i = 0;
while ((to [i] = from [i])! = '\ 0')
++ i;
}
Fungsi getline dan copy dideklarasikan di awal program, yang kami
anggap terkandung dalam satu file.
main dan getline berkomunikasi melalui sepasang argumen dan nilai yang dikembalikan. Di
getline , argumennya dinyatakan oleh baris

int getline (char s [], int lim);


yang menentukan bahwa argumen pertama, s , adalah array, dan yang kedua, lim , adalah integer. Itu
tujuan memasok ukuran array dalam deklarasi adalah untuk menyisihkan penyimpanan. Panjangnya
sebuah array s tidak diperlukan dalam getline karena ukurannya ditetapkan pada main . getline menggunakan kembali ke
mengirim nilai kembali ke pemanggil, seperti kekuatan fungsi itu. Baris ini juga menyatakan itu
getline mengembalikan int ; karena int adalah tipe pengembalian default, int bisa dihilangkan.
Beberapa fungsi mengembalikan nilai yang berguna; yang lain, seperti salinan , digunakan hanya untuk efeknya dan kembali
Tidak bernilai. Jenis kembali salinan tidak berlaku , yang menyatakan secara eksplisit bahwa tidak ada nilai yang dikembalikan.

Halaman 30

31

getline menempatkan karakter '\ 0' ( karakter nol , yang nilainya nol) di akhir
array yang dibuatnya, untuk menandai akhir dari rangkaian karakter. Konversi ini juga digunakan oleh
bahasa C: ketika string konstan suka

"halo \ n"
muncul dalam program C, itu disimpan sebagai array karakter yang mengandung karakter dalam
string dan diakhiri dengan '\ 0' untuk menandai akhir.

The % s spesifikasi format di printf mengharapkan yang sesuai argumen untuk menjadi string
terwakili dalam formulir ini. copy juga bergantung pada fakta bahwa argumen inputnya diakhiri
a '\ 0' , dan menyalin karakter ini ke dalam output.

Perlu disebutkan secara sepintas bahwa bahkan sebuah program sekecil ini menyajikan beberapa lengket
masalah desain. Sebagai contoh, apa yang harus utama dilakukan jika menemui garis yang lebih besar dari
batasnya? getline berfungsi dengan aman, karena berhenti mengumpulkan ketika array penuh, bahkan jika tidak
baris baru telah terlihat. Dengan menguji panjang dan karakter terakhir yang dikembalikan, kaleng utama
tentukan apakah garis itu terlalu panjang, dan kemudian atasi sesuai keinginan. Demi kepentingan singkatnya,
kami telah mengabaikan masalah ini.

Tidak ada cara bagi pengguna getline untuk mengetahui sebelumnya berapa lama garis input mungkin, jadi
cek getline untuk overflow. Di sisi lain, pengguna salinan sudah tahu (atau dapat menemukan
keluar) seberapa besar string, jadi kami memilih untuk tidak menambahkan pengecekan kesalahan padanya.

Latihan 1-16. Merevisi rutin utama program garis terpanjang sehingga akan mencetak dengan benar
panjang jalur input panjang yang sewenang-wenang, dan sebanyak mungkin teks.

Latihan 1-17. Tulis program untuk mencetak semua jalur input yang lebih panjang dari 80 karakter.

Latihan 1-18. Tulis sebuah program untuk menghapus trailing blank dan tab dari setiap baris input, dan
untuk menghapus seluruh baris kosong.

Latihan 1-19. Tulis fungsi terbalik yang membalik string karakter s . Gunakan untuk
menulis sebuah program yang membalikkan inputnya satu per satu.

1.10 Variabel dan Cakupan Eksternal


Variabel dalam main , seperti baris , terpanjang , dll. Bersifat pribadi atau lokal ke utama . Karena mereka
dideklarasikan dalam main , tidak ada fungsi lain yang dapat memiliki akses langsung ke sana. Hal yang sama berlaku untuk
variabel dalam fungsi lain; misalnya, variabel i di getline tidak terkait dengan i in
salinan. Setiap variabel lokal dalam suatu fungsi muncul hanya ketika fungsi dipanggil,
dan menghilang saat fungsi keluar. Inilah sebabnya mengapa variabel tersebut biasanya dikenal sebagai
variabel otomatis , mengikuti terminologi dalam bahasa lain. Kami akan menggunakan istilah otomatis
untuk selanjutnya merujuk pada variabel lokal ini. ( Bab 4 membahas kelas penyimpanan statis , dalam
variabel lokal mana yang mempertahankan nilainya di antara panggilan.)
Karena variabel otomatis datang dan pergi dengan pemanggilan fungsi, mereka tidak mempertahankannya
nilai dari satu panggilan ke yang berikutnya, dan harus secara eksplisit ditetapkan pada setiap entri. Jika tidak diatur,
mereka akan mengandung sampah.

Sebagai alternatif untuk variabel otomatis, dimungkinkan untuk mendefinisikan variabel yang eksternal untuk semua
fungsi, yaitu variabel yang dapat diakses dengan nama oleh fungsi apa pun. (Mekanisme ini adalah
agak seperti Fortran COMMON atau variabel Pascal dideklarasikan di blok terluar.) Karena

Halaman 31

32
variabel eksternal dapat diakses secara global, mereka dapat digunakan sebagai pengganti daftar argumen
mengkomunikasikan data antar fungsi. Lebih lanjut, karena variabel eksternal tetap dalam
keberadaan secara permanen, daripada muncul dan menghilang seperti fungsi dipanggil dan
keluar, mereka mempertahankan nilai-nilai mereka bahkan setelah fungsi yang mengaturnya telah kembali.

Variabel eksternal harus didefinisikan , tepat sekali, di luar fungsi apa pun; ini mengesampingkan
penyimpanan untuk itu. Variabel juga harus dideklarasikan di setiap fungsi yang ingin mengaksesnya; ini
menyatakan tipe variabel. Deklarasi tersebut bisa berupa pernyataan eksternal atau eksplisit
tersirat dari konteks. Untuk membuat diskusi yang konkret, mari kita menulis ulang program garis terpanjang
dengan garis , terpanjang , dan maks sebagai variabel eksternal. Ini membutuhkan perubahan panggilan,
deklarasi, dan badan ketiga fungsi.

#termasuk <stdio.h>

#define MAXLINE 1000 / * ukuran saluran input maksimum * /

int max; / * panjang maksimum yang terlihat sejauh ini * /


baris char [MAXLINE]; / * jalur input saat ini * /
char terpanjang [MAXLINE]; / * garis terpanjang disimpan di sini * /

int getline (void);


membatalkan salinan (void);

/ * cetak jalur input terpanjang; versi khusus * /


utama()
{
int len;
extern int max;
extern char terpanjang [];

maks = 0;
while ((len = getline ())> 0)
if (len> max) {
maks = len;
salinan();
}
jika (maks> 0) / * ada garis * /
printf ("% s", terpanjang);
return 0;
}
Halaman 32

33

/ * getline: versi khusus * /


int getline (batal)
{
int c, saya;
extern char line [];

untuk (i = 0; i <MAXLINE - 1
&& (c = getchar))! = EOF && c! = '\ n'; ++ i)
baris [i] = c;
if (c == '\ n') {
baris [i] = c;
++ i;
}
baris [i] = '\ 0';
mengembalikan saya;
}

/ * copy: versi khusus * /


void copy (batal)
{
int i;
extern char line [], terpanjang [];

i = 0;
while ((terpanjang [i] = baris [i])! = '\ 0')
++ i;
}
Variabel eksternal dalam main , getline dan copy didefinisikan oleh baris pertama dari contoh
di atas, yang menyatakan jenisnya dan menyebabkan penyimpanan dialokasikan untuk mereka. Secara sintaksis, eksternal
definisi seperti definisi variabel lokal, tetapi karena mereka terjadi di luar fungsi,
variabelnya eksternal. Sebelum suatu fungsi dapat menggunakan variabel eksternal, nama
variabel harus diketahui fungsi; deklarasi sama seperti sebelumnya kecuali untuk
kata kunci eksternal yang ditambahkan .
Dalam keadaan tertentu, deklarasi eksternal dapat dihilangkan. Jika definisi
variabel eksternal terjadi pada file sumber sebelum digunakan dalam fungsi tertentu, maka tidak ada
butuhkan untuk deklarasi eksternal dalam fungsi. The extern deklarasi di utama , getline dan
salinan dengan demikian berlebihan. Bahkan, praktik umum adalah menempatkan definisi semua eksternal
variabel di awal file sumber, dan kemudian hilangkan semua deklarasi eksternal.

Jika program ini dalam beberapa file sumber, dan variabel didefinisikan dalam file1 dan digunakan dalam file2 dan
file3 , maka deklarasi eksternal diperlukan dalam file2 dan file3 untuk menghubungkan kejadian
variabel. Praktik yang biasa dilakukan adalah mengumpulkan deklarasi eksternal variabel dan fungsi dalam a
file terpisah, secara historis disebut header , yang disertakan oleh #include di bagian depan masing-masing
sumber data. Sufiks .h adalah konvensional untuk nama header. Fungsi standar
perpustakaan, misalnya, dideklarasikan dalam header seperti <stdio.h> . Topik ini dibahas panjang lebar
di Bab 4 , dan perpustakaan itu sendiri dalam Bab 7 dan Lampiran B .

Karena versi khusus dari getline dan copy tidak memiliki argumen, logika akan menyarankan
bahwa prototipe mereka di awal file harus berupa getline () dan salin () . Tapi untuk
kompatibilitas dengan program C yang lebih lama standar mengambil daftar kosong sebagai gaya lama
deklarasi, dan mematikan semua pemeriksaan daftar argumen; kata void harus digunakan untuk
daftar kosong secara eksplisit. Kami akan membahas ini lebih lanjut dalam Bab 4 .

Anda harus mencatat bahwa kami menggunakan definisi dan deklarasi kata-kata dengan hati-hati ketika kami
lihat variabel eksternal di bagian ini. `` Definisi '' merujuk ke tempat variabel tersebut
penyimpanan dibuat atau ditugaskan; `` deklarasi '' mengacu pada tempat-tempat di mana sifat dari variabel tersebut
menyatakan tetapi tidak ada penyimpanan yang dialokasikan.

Halaman 33

34
By the way, ada kecenderungan untuk membuat segala sesuatu yang terlihat sebuah extern variabel karena
tampaknya menyederhanakan komunikasi - daftar argumen pendek dan variabel selalu ada
ketika Anda menginginkannya. Tetapi variabel eksternal selalu ada bahkan ketika Anda tidak menginginkannya.
Terlalu bergantung pada variabel eksternal penuh dengan bahaya karena mengarah ke program yang
koneksi data tidak semuanya jelas - variabel dapat diubah secara tak terduga dan bahkan
cara yang tidak disengaja, dan program ini sulit untuk dimodifikasi. Versi kedua dari garis terpanjang
Program lebih rendah daripada yang pertama, sebagian karena alasan ini, dan sebagian karena merusak
generalisasi dua fungsi yang berguna dengan menuliskan ke dalamnya nama-nama variabel mereka
memanipulasi.

Pada titik ini kita telah membahas apa yang mungkin disebut inti konvensional C. Dengan ini
beberapa blok bangunan, mungkin untuk menulis program yang bermanfaat dengan ukuran yang cukup besar, dan itu
mungkin akan menjadi ide yang baik jika Anda berhenti cukup lama untuk melakukannya. Latihan-latihan ini menyarankan
program dengan kompleksitas agak lebih besar daripada yang sebelumnya dalam bab ini.

Latihan 1-20. Tuliskan detab program yang menggantikan tab pada input dengan angka yang tepat
dari kosong ke ruang ke perhentian tab berikutnya. Asumsikan satu set tab berhenti, katakan setiap n kolom.
Haruskah n menjadi variabel atau parameter simbolis?

Latihan 1-21. Tulis entab program yang menggantikan string kosong dengan jumlah minimum
tab dan kosong untuk mencapai jarak yang sama. Gunakan berhenti tab yang sama seperti untuk detab . Kapan
salah satu tab atau satu kosong akan cukup untuk mencapai halte tab, yang harus diberikan
Pilihan?

Latihan 1-22. Tulis sebuah program untuk `melipat 'jalur input panjang menjadi dua atau lebih baris lebih pendek setelahnya
karakter non-kosong terakhir yang muncul sebelum kolom masukan ke- n . Pastikan Anda
Program melakukan sesuatu yang cerdas dengan garis yang sangat panjang, dan jika tidak ada yang kosong atau tab
sebelum kolom yang ditentukan.

Latihan 1-23. Tulis program untuk menghapus semua komentar dari program C. Jangan lupa
menangani string dan konstanta karakter yang dikutip dengan benar. Komentar C jangan bersarang.

Latihan 1-24. Tulis sebuah program untuk memeriksa program C untuk mengetahui kesalahan sintaks yang belum sempurna
kurung, kurung, dan kawat gigi yang tak tertandingi. Jangan lupa tentang tanda kutip, baik tunggal maupun
ganda, urutan melarikan diri, dan komentar. (Program ini sulit jika Anda melakukannya secara umum.)

Halaman 34

35

Bab 2 - Jenis, Operator dan


Ekspresi
Variabel dan konstanta adalah objek data dasar yang dimanipulasi dalam suatu program. Daftar deklarasi
variabel yang akan digunakan, dan nyatakan jenis apa yang mereka miliki dan mungkin apa nilai awal mereka.
Operator menentukan apa yang harus dilakukan pada mereka. Ekspresi menggabungkan variabel dan konstanta ke
menghasilkan nilai-nilai baru. Jenis objek menentukan set nilai yang dapat dimiliki dan apa
operasi dapat dilakukan di atasnya. Blok bangunan ini adalah topik dari bab ini.
Standar ANSI telah membuat banyak perubahan kecil dan penambahan pada tipe dan ekspresi dasar.
Sekarang ada formulir yang ditandatangani dan tidak ditandatangani dari semua jenis bilangan bulat, dan notasi untuk yang tidak ditandatangani
konstanta dan konstanta karakter heksadesimal. Operasi titik-mengambang dapat dilakukan di
presisi tunggal; ada juga tipe double panjang untuk presisi yang diperluas. Konstanta string dapat
disatukan pada waktu kompilasi. Enumerasi telah menjadi bagian dari bahasa, meresmikan
fitur lama berdiri. Objek dapat dideklarasikan sebagai const , yang mencegahnya
berubah. Aturan untuk paksaan otomatis di antara tipe aritmatika telah ditambahkan ke
menangani set tipe yang lebih kaya.

2.1 Nama Variabel


Meskipun
dan kamisimbolik.
konstanta tidak mengatakannya di Bab
Nama terdiri dari 1 , dan
huruf ada angka;
beberapa batasanpertama
karakter pada nama
harusvariabel
a
surat. Garis bawah `` _ '' dihitung sebagai huruf; terkadang berguna untuk meningkatkan
keterbacaan nama variabel panjang. Namun, jangan mulai nama variabel dengan garis bawah
rutinitas perpustakaan sering menggunakan nama-nama seperti itu. Huruf besar dan kecil berbeda, jadi x dan X adalah
dua nama berbeda. Praktik C tradisional adalah menggunakan huruf kecil untuk nama variabel, dan semuanya
huruf besar untuk konstanta simbolik.
Setidaknya 31 karakter pertama dari nama internal signifikan. Untuk nama fungsi dan
variabel eksternal, jumlahnya mungkin kurang dari 31, karena nama eksternal dapat digunakan oleh
assembler dan loader di mana bahasa tidak memiliki kontrol. Untuk nama eksternal, the
standar menjamin keunikan hanya untuk 6 karakter dan satu case. Kata kunci seperti jika ,
lain , int , mengambang , dll, disediakan: Anda tidak dapat menggunakannya sebagai nama variabel. Mereka harus masuk
huruf kecil.

Adalah bijaksana untuk memilih nama variabel yang terkait dengan tujuan variabel, dan itu
tidak mungkin tercampur tipografi. Kami cenderung menggunakan nama pendek untuk variabel lokal,
terutama indeks loop, dan nama yang lebih panjang untuk variabel eksternal.

2.2 Jenis dan Ukuran Data


Hanya ada beberapa tipe data dasar di C:
arang satu byte, mampu menampung satu karakter dalam set karakter lokal
int bilangan bulat, biasanya mencerminkan ukuran alami bilangan bulat pada mesin host
float floating-point presisi tunggal
titik mengambang presisi ganda ganda
Selain itu, ada sejumlah kualifikasi yang dapat diterapkan pada tipe dasar ini. pendek dan
lama berlaku untuk bilangan bulat:

int singkat;
penghitung int panjang;
Kata int dapat dihilangkan dalam deklarasi seperti itu, dan biasanya itu.

Halaman 35

36
Maksudnya adalah bahwa pendek dan panjang harus memberikan panjang bilangan bulat yang berbeda jika praktis;
int biasanya akan menjadi ukuran alami untuk mesin tertentu. pendek sering panjang 16 bit, dan
int baik 16 atau 32 bit. Setiap kompiler bebas memilih ukuran yang sesuai untuk dirinya sendiri
perangkat keras, hanya tunduk pada pembatasan yang s pendek dan int setidaknya 16 bit, s panjang
setidaknya 32 bit, dan pendek tidak lebih dari int , yang tidak lebih dari panjang .

Kualifikasi yang ditandatangani atau tidak ditandatangani dapat diterapkan ke char atau bilangan bulat apa pun. nomor yang tidak ditandatang
selalu positif atau nol, dan mematuhi hukum-hukum aritmetika modulo 2 n , di mana n adalah bilangan
bit dalam jenisnya. Jadi, misalnya, jika karakter adalah 8 bit, variabel karakter yang tidak ditandai memiliki nilai
antara 0 dan 255, sementara char yang ditandatangani memiliki nilai antara -128 dan 127 (dalam dua
mesin pelengkap.) Apakah karakter biasa ditandatangani atau tidak, tergantung mesin, tetapi
karakter yang dapat dicetak selalu positif.

Tipe long double menentukan floating point dengan presisi tinggi. Seperti halnya bilangan bulat, ukurannya
objek floating-point didefinisikan implementasi; melayang , ganda dan panjang ganda bisa
mewakili satu, dua atau tiga ukuran berbeda.

Header standar <Limit.h> dan <float.h> mengandung konstanta simbolis untuk semua ini
ukuran, bersama dengan properti lain dari mesin dan kompiler. Ini dibahas dalam
Lampiran B .

Latihan 2-1. Tulis program untuk menentukan rentang char , pendek , int , dan panjang
variabel, baik yang ditandatangani maupun yang tidak ditandatangani , dengan mencetak nilai yang sesuai dari header standar
dan dengan perhitungan langsung. Lebih sulit jika Anda menghitungnya: tentukan rentang variasi
tipe floating-point.

2.3 Konstanta
Konstanta integer seperti 1234 adalah int . Sebuah panjang konstan ditulis dengan terminal l (ela) atau L ,
seperti pada 123456789L ; konstanta integer yang terlalu besar untuk masuk ke int juga akan dianggap sebagai panjang.
Konstanta yang tidak ditandatangani ditulis dengan terminal u atau U , dan akhiran ul atau UL menunjukkan
lama tidak ditandatangani .
Konstanta floating-point mengandung titik desimal ( 123,4 ) atau eksponen ( 1e-2 ) atau keduanya; mereka
jenisnya dua kali lipat , kecuali sufiks. Sufiks f atau F mengindikasikan konstanta float ; l atau L menunjukkan a
panjang ganda .

Nilai integer dapat ditentukan dalam oktal atau heksadesimal bukan desimal. A terkemuka 0
(nol) pada konstanta bilangan bulat berarti oktal; 0x atau 0X terkemuka berarti heksadesimal. Sebagai contoh,
desimal 31juga
konstanta dapat ditulis
dapat sebagai
diikuti oleh037 dalam
L untuk oktal dan 0x1f
membuatnya atau 0x1F
panjang dan Udalam
untukhex. Oktal dantidak
membuatnya heksadesimal
ditandatangani : 0XFUL
adalah konstanta panjang unsigned dengan nilai 15 desimal.

Sebuah karakter yang konstan adalah integer, ditulis sebagai salah satu karakter dalam tanda kutip tunggal, seperti
'x' . Nilai konstanta karakter adalah nilai numerik karakter di mesin
set karakter. Misalnya, dalam karakter ASCII mengatur konstanta karakter '0' memiliki nilai
48, yang tidak terkait dengan nilai numerik 0. Jika kita menulis '0', bukan nilai numerik seperti
48 yang tergantung pada rangkaian karakter, program tidak tergantung pada nilai tertentu dan
lebih mudah dibaca. Konstanta karakter berpartisipasi dalam operasi numerik sama seperti bilangan bulat lainnya,
meskipun mereka paling sering digunakan dalam perbandingan dengan karakter lain.

Karakter tertentu dapat direpresentasikan dalam konstanta karakter dan string dengan urutan escape
seperti \ n (baris baru); urutan ini terlihat seperti dua karakter, tetapi hanya mewakili satu. Tambahan,
pola bit berukuran byte sewenang-wenang dapat ditentukan oleh

' ooo '

Halaman 36

37
di mana ooo adalah satu hingga tiga digit oktal (0 ... 7) atau oleh

'\ x hh '
di mana hh adalah satu atau lebih digit heksadesimal ( 0 ... 9, a ... f, A ... F ). Jadi kita bisa menulis

#define VTAB '\ 013' / * ASCII tab vertikal * /


#define BELL '\ 007' / * karakter bel ASCII * /
atau, dalam heksadesimal,

#define VTAB '\ xb' / * ASCII tab vertikal * /


#define BELL '\ x7' / * ASCII bell character * /
Seperangkat urutan pelarian yang lengkap adalah
\ karakter peringatan (bel) \ backslash
\ backspace \ tanda tanya
dari formfeed \ ' kutipan tunggal
\ n baris baru \ " kutipan ganda
\ r carriage return \ ooo angka oktal
\ t tab horizontal \ x hh angka heksadesimal
\ v tab vertikal
Konstanta karakter '\ 0' mewakili karakter dengan nilai nol, karakter nol. '\ 0'
sering ditulis bukan 0 untuk menekankan sifat karakter dari beberapa ekspresi, tetapi
nilai numerik hanya 0.

Sebuah ekspresi konstanta adalah ekspresi yang hanya melibatkan konstanta. Ekspresi seperti itu mungkin
dievaluasi pada saat kompilasi daripada run-time, dan karenanya dapat digunakan di sembarang tempat
bahwa konstanta dapat terjadi, seperti pada

#define MAXLINE 1000


baris char [MAXLINE + 1];
atau

#definisikan LEAP 1 / * dalam tahun kabisat * /


hari int [31 + 28 + LEAP + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31];
Sebuah string konstan , atau string literal , merupakan urutan nol atau lebih karakter yang dikelilingi oleh
kutipan ganda, seperti pada

"Aku seorang string"


atau

"" / * string kosong * /


Kutipan bukan bagian dari string, tetapi hanya melayani untuk membatasi itu. Urutan melarikan diri yang sama
digunakan dalam konstanta karakter berlaku dalam string; \ " mewakili karakter kutipan ganda. String
konstanta dapat digabungkan pada waktu kompilasi:
"Halo Dunia"
setara dengan

"Halo Dunia"
Ini berguna untuk memisahkan string panjang di beberapa baris sumber.
Secara teknis, konstanta string adalah array karakter. Representasi internal string
memiliki karakter nol '\ 0' di akhir, sehingga penyimpanan fisik yang diperlukan adalah lebih dari satu
jumlah karakter yang ditulis di antara tanda kutip. Representasi ini berarti tidak ada
membatasi berapa lama sebuah string bisa, tetapi program harus memindai string sepenuhnya untuk menentukan stringnya
panjangnya. Strlen fungsi pustaka standar mengembalikan panjang string karakternya
argumen s , tidak termasuk terminal '\ 0' . Ini versi kami:

Halaman 37

38

/ * strlen: panjang kembali s * /


int strlen (char s [])
{
int i;

while (s [i]! = '\ 0')


++ i;
mengembalikan saya;
}
strlen dan fungsi string lainnya dideklarasikan di header standar <string.h> .
Berhati-hatilah untuk membedakan antara konstanta karakter dan string yang berisi satu
karakter: 'x' tidak sama dengan "x" . Yang pertama adalah bilangan bulat, yang digunakan untuk menghasilkan angka
nilai huruf x dalam set karakter mesin. Yang terakhir adalah array karakter yang
berisi satu karakter (huruf x ) dan a '\ 0' .

Ada satu jenis konstanta, konstanta enumerasi . Enumerasi adalah daftar


nilai integer konstan, seperti pada

enum boolean {TIDAK, YES};


Nama pertama dalam enum memiliki nilai 0, 1 berikutnya, dan seterusnya, kecuali nilai eksplisitnya
ditentukan. Jika tidak semua nilai ditentukan, nilai yang tidak ditentukan melanjutkan kelanjutan dari
nilai yang ditentukan terakhir, sebagai yang kedua dari contoh ini:

enum lolos {BELL = '\ a', BACKSPACE = '\ b', TAB = '\ t',
NEWLINE = '\ n', VTAB = '\ v', RETURN = '\ r'};

enum bulan {JAN = 1, FEB, MAR, APR, MEI, JUNI,


JUL, AUG, SEP, OCT, NOV, DEC};
/ * FEB = 2, MAR = 3, dll. * /
Nama dalam enumerasi yang berbeda harus berbeda. Nilai tidak harus berbeda dalam hal yang sama
enumerasi.
Pencacahan menyediakan cara mudah untuk mengaitkan nilai konstan dengan nama, alternatif
untuk # define dengan keuntungan bahwa nilai-nilai dapat dihasilkan untuk Anda. Meskipun variabel
Jenis enum dapat dideklarasikan, kompiler tidak perlu memeriksa apa yang Anda simpan dalam variabel tersebut
nilai yang valid untuk enumerasi. Namun demikian, variabel enumerasi menawarkan peluang
memeriksa dan seringkali lebih baik daripada #define s. Selain itu, debugger mungkin dapat mencetak
nilai variabel enumerasi dalam bentuk simbolisnya.

2.4 Deklarasi
Semua variabel harus dideklarasikan sebelum digunakan, meskipun deklarasi tertentu dapat dibuat secara implisit
berdasarkan konten. Deklarasi menentukan tipe, dan berisi daftar satu atau lebih variabel itu
ketik, seperti pada

int lebih rendah, atas, langkah;


char c, line [1000];
Variabel dapat didistribusikan di antara deklarasi dengan cara apa pun; daftar di atas bisa jadi
ditulis sebagai

int lebih rendah;


int atas;
langkah int;
char c;
baris char [1000];
Formulir yang terakhir membutuhkan lebih banyak ruang, tetapi mudah untuk menambahkan komentar pada setiap deklarasi
untuk modifikasi selanjutnya.

Halaman 38

39
Variabel juga dapat diinisialisasi dalam deklarasi. Jika nama diikuti oleh tanda sama dengan
dan ekspresi, ekspresi berfungsi sebagai inisialisasi, seperti pada

char esc = '\\';


int i = 0;
batas int = MAXLINE + 1;
float eps = 1.0e-5;
Jika variabel yang dipermasalahkan tidak otomatis, inisialisasi dilakukan sekali saja, secara konsepsi
sebelum program mulai dijalankan, dan penginisialisasi harus berupa ekspresi konstan. Sebuah
variabel otomatis yang diinisialisasi secara eksplisit diinisialisasi setiap kali fungsi atau blok itu berada adalah
masuk; initializer dapat berupa ekspresi apa saja. Variabel eksternal dan statis diinisialisasi ke
nol secara default. Variabel otomatis yang tidak ditetapkan oleh initializer eksplisit (yaitu,
nilai sampah).
Konstanta kualifikasi dapat diterapkan pada deklarasi variabel apa pun untuk menentukan nilainya
tidak akan diubah Untuk array, kualifikasi const mengatakan bahwa elemen tidak akan diubah.

const double e = 2.71828182845905;


const char msg [] = "peringatan:";
The const deklarasi juga dapat digunakan dengan argumen array, untuk menunjukkan bahwa fungsi
tidak mengubah array itu:

int strlen (const char []);


Hasilnya adalah implementasi yang ditentukan jika suatu upaya dilakukan untuk mengubah konst .

2.5 Operator Aritmatika


Operator aritmatika biner adalah + , - , * , / , dan operator modulus % . Divisi integer
memotong bagian pecahan. Ekspresi

x% y
menghasilkan sisanya ketika x dibagi dengan y , dan dengan demikian adalah nol ketika y membagi x dengan tepat. Untuk
Misalnya, satu tahun adalah tahun kabisat jika dibagi dengan 4 tetapi tidak sebesar 100, kecuali tahun yang dapat dibagi
400 adalah tahun kabisat. Karena itu

if ((tahun% 4 == 0 && tahun% 100! = 0) || tahun% 400 == 0)


printf ("% d adalah tahun kabisat \ n", tahun);
lain
printf ("% d bukan tahun kabisat \ n", tahun);
The % Operator tidak dapat diterapkan pada pelampung atau ganda . Arah pemotongan untuk / dan
tanda hasil untuk % bergantung pada mesin untuk operan negatif, seperti tindakan yang diambil
pada overflow atau underflow.
Biner + dan - operator memiliki prioritas yang sama, yang lebih rendah dari prioritas
* , / dan % , yang pada gilirannya lebih rendah dari unary + dan - . Asosiasikan operator aritmatika ke kiri
Baik.

Tabel 2.1 di akhir bab ini merangkum prioritas dan asosiasi untuk semua operator.

2.6 Operator Relasional dan Logis


Operator relasional adalah

>> = <<=
Mereka semua memiliki prioritas yang sama. Tepat di bawah mereka sebagai prioritas adalah operator kesetaraan:

==! =
Operator relasional memiliki prioritas lebih rendah daripada operator aritmatika, jadi ungkapan seperti i
<lim-1 diambil sebagai i <(lim-1) , seperti yang diharapkan.

Halaman 39

40
Lebih menarik adalah operator logis && dan || . Ekspresi yang dihubungkan oleh && atau || adalah
dievaluasi dari kiri ke kanan, dan evaluasi berhenti segera setelah kebenaran atau kepalsuan hasilnya
dikenal. Sebagian besar program C mengandalkan properti ini. Sebagai contoh, ini adalah loop dari input
fungsi getline yang kami tulis di Bab 1 :

untuk (i = 0; i <lim-1 && (c = getchar ())! = '\ n' && c! = EOF; ++ i)


s [i] = c;
Sebelum membaca karakter baru, perlu untuk memeriksa bahwa ada ruang untuk menyimpannya di
array s , jadi tes i <lim-1 harus dilakukan terlebih dahulu. Apalagi jika tes ini gagal, kita tidak boleh ikut
dan baca karakter lain.
Demikian pula, akan sangat disayangkan jika c diuji terhadap EOF sebelum getchar dipanggil;
karena itu panggilan dan penugasan harus terjadi sebelum karakter dalam c diuji.

Diutamakan dari && lebih tinggi dari pada || , dan keduanya lebih rendah dari relasional dan kesetaraan
operator, jadi ungkapan suka

i <lim-1 && (c = getchar ())! = '\ n' && c! = EOF


tidak perlu tanda kurung tambahan. Tapi karena didahului ! = Lebih tinggi dari penugasan,
tanda kurung diperlukan dalam

(c = getchar ())! = '\ n'


untuk mencapai hasil tugas yang diinginkan untuk c dan kemudian membandingkannya dengan '\ n' .
Menurut definisi, nilai numerik ekspresi relasional atau logis adalah 1 jika relasinya benar,
dan 0 jika relasinya salah.

Operator negasi unary ! mengubah operan non-nol menjadi 0, dan operan nol dalam 1. A
penggunaan umum ! dalam konstruksi seperti

jika (! valid)
daripada

if (valid == 0)
Sulit untuk menggeneralisasi bentuk mana yang lebih baik. Konstruksi seperti ! Valid dibaca dengan baik (`` jika
tidak valid ''), tetapi yang lebih rumit mungkin sulit dipahami.
Latihan 2-2. Tulis loop yang setara dengan loop for di atas tanpa menggunakan && atau || .

2.7 Ketik Konversi


Ketika seorang operator memiliki operan dari tipe yang berbeda, mereka dikonversikan ke tipe yang umum
menurut sejumlah kecil aturan. Secara umum, satu-satunya konversi otomatis adalah yang
mengkonversi operan `` yang lebih sempit '' menjadi operan yang `` lebih luas '' tanpa kehilangan informasi, seperti
mengubah integer menjadi floating point dalam ekspresi seperti f + i . Ekspresi yang tidak
masuk akal, seperti menggunakan pelampung sebagai subskrip, tidak diizinkan. Ekspresi yang mungkin hilang
informasi, seperti menetapkan tipe integer yang lebih panjang ke yang lebih pendek, atau tipe floating-point ke
bilangan bulat, dapat menarik peringatan, tetapi mereka tidak ilegal.
Sebuah arang hanya bilangan bulat kecil, sehingga arang s dapat secara bebas digunakan dalam ekspresi aritmatika. Ini
memungkinkan fleksibilitas yang cukup besar dalam beberapa jenis transformasi karakter. Seseorang dicontohkan
oleh implementasi naif ini dari fungsi atoi , yang mengubah serangkaian angka menjadi nya
setara numerik.

/ * atoi: convert s ke integer * /


int atoi (char s [])
{
int i, n;

Halaman 40

41

n = 0;
untuk (i = 0; s [i]> = '0' && s [i] <= '9'; ++ i)
n = 10 * n + (s [i] - '0');
return n;
}
Seperti yang kita bahas di Bab 1 , ungkapan

s [i] - '0'
memberikan nilai numerik karakter yang disimpan dalam s [i] , karena nilai '0' , '1' , dll.,
membentuk urutan peningkatan yang berdekatan.
Contoh lain konversi char to int adalah fungsi yang lebih rendah , yang memetakan satu
karakter ke huruf kecil untuk set karakter ASCII . Jika karakternya bukan huruf besar,
lebih rendah mengembalikannya tidak berubah.

/ * lebih rendah: konversi c ke huruf kecil; Hanya ASCII * /


int lower (int c)
{
if (c> = 'A' && c <= 'Z')
return c + 'a' - 'A';
lain
kembali c;
}
Ini bekerja untuk ASCII karena huruf besar yang sesuai dan huruf kecil adalah tetap
jarak terpisah sebagai nilai numerik dan setiap alfabet bersebelahan - tidak ada yang lain selain huruf
antara A dan Z . Pengamatan terakhir ini tidak benar dari rangkaian karakter EBCDIC, namun demikian
kode ini akan mengkonversi lebih dari sekadar huruf dalam EBCDIC.
Header standar <ctype.h> , yang dijelaskan dalam Lampiran B , mendefinisikan sekumpulan fungsi yang
memberikan pengujian dan konversi yang tidak tergantung pada rangkaian karakter. Misalnya saja fungsinya
tolower adalah pengganti portabel untuk fungsi yang lebih rendah seperti yang ditunjukkan di atas. Begitu pula dengan tesnya

c> = '0' && c <= '9'


dapat digantikan oleh

isdigit (c)
Kami akan menggunakan fungsi <ctype.h> mulai sekarang.
Ada satu titik halus tentang konversi karakter menjadi bilangan bulat. Bahasanya tidak
tentukan apakah variabel tipe char ditandatangani atau tidak ditandatangani. Ketika char adalah
dikonversi menjadi int , bisakah itu menghasilkan bilangan bulat negatif? Jawabannya bervariasi dari mesin
ke mesin, mencerminkan perbedaan dalam arsitektur. Pada beberapa mesin, char memiliki bit paling kiri
adalah 1 akan dikonversi ke bilangan bulat negatif (`` tandatangani ekstensi ''). Pada orang lain, arang dipromosikan
ke int dengan menambahkan nol di ujung kiri, dan dengan demikian selalu positif.

Definisi C menjamin bahwa setiap karakter dalam karakter pencetakan standar mesin
set tidak akan pernah negatif, jadi karakter ini akan selalu menjadi jumlah positif dalam ekspresi.
Tetapi pola bit sewenang-wenang yang disimpan dalam variabel karakter mungkin tampak negatif pada beberapa
mesin, namun positif pada orang lain. Untuk portabilitas, tentukan ditandatangani atau tidak ditandatangani jika bukan karakter
data harus disimpan dalam variabel char .

Ekspresi relasional seperti i> j dan ekspresi logis yang dihubungkan oleh && dan || didefinisikan
memiliki nilai 1 jika benar, dan 0 jika salah. Demikian penugasannya

d = c> = '0' && c <= '9'


set d ke 1 jika c adalah angka, dan 0 jika tidak. Namun, fungsi seperti isdigit dapat mengembalikan non-
nilai nol untuk true. Di bagian pengujian if , while , for , etc., `` true '' hanya berarti `` bukan-nol '', jadi
ini tidak ada bedanya.

Halaman 41

42
Konversi aritmatika tersirat berfungsi seperti yang diharapkan. Secara umum, jika operator menyukai + atau *
yang mengambil dua operan (operator biner) memiliki operan dari tipe yang berbeda, tipe `` lebih rendah '' adalah
dipromosikan ke tipe `` lebih tinggi '' sebelum operasi dilanjutkan. Hasilnya adalah tipe integer.
Bagian 6 dari Lampiran A menyatakan aturan konversi dengan tepat. Jika tidak ada yang tidak ditandatangani
operan, bagaimanapun, seperangkat aturan informal berikut akan cukup:
â € ¢ Jika salah satu operan panjang ganda , ubah yang lain menjadi panjang ganda .

â € ¢ Kalau tidak, jika salah satu operan ganda , konversi yang lain menjadi ganda .
â € ¢ Kalau tidak, jika salah satu operan float , konversikan yang lain menjadi float .

â € ¢ Jika tidak, konversi char dan short ke int .


â € ¢ Kemudian, jika salah satu operan panjang , ubah yang lain menjadi panjang .
Perhatikan bahwa float s dalam ekspresi tidak secara otomatis dikonversi menjadi dua kali lipat ; ini adalah sebuah
berubah dari definisi aslinya. Secara umum, fungsi matematika seperti yang ada di <math.h>
akan menggunakan presisi ganda. Alasan utama untuk menggunakan float adalah untuk menghemat penyimpanan dalam array besar,
atau, lebih jarang, untuk menghemat waktu pada mesin di mana aritmatika presisi ganda khususnya
mahal.
Aturan konversi lebih rumit ketika operan yang tidak ditandatangani terlibat. Masalahnya adalah
bahwa perbandingan antara nilai yang ditandatangani dan yang tidak ditandatangani bergantung pada mesin, karena mereka
tergantung pada ukuran berbagai tipe integer. Sebagai contoh, misalkan int adalah 16 bit dan
panjangnya 32 bit. Kemudian -1L <1U , karena 1U , yang merupakan int unsigned , dipromosikan ke a
ditandatangani lama . Tapi -1L> 1UL karena -1L dipromosikan menjadi unsigned long dan muncul
menjadi angka positif yang besar.

Konversi terjadi di seluruh penugasan; nilai sisi kanan dikonversi ke tipe


dari kiri, yang merupakan jenis hasilnya.

Karakter dikonversi ke integer, baik dengan ekstensi tanda atau tidak, seperti dijelaskan di atas.

Bilangan bulat yang lebih panjang dikonversi menjadi yang lebih pendek atau menjadi karakter dengan menjatuhkan kelebihan orde tinggi
bit. Demikianlah dalam

int i;
char c;

i = c;
c = i;
nilai c tidak berubah. Ini benar apakah ekstensi tanda terlibat atau tidak. Membalik
Namun, urutan penugasan mungkin kehilangan informasi.
Jika x adalah float dan i adalah int , maka x = i dan i = x keduanya menyebabkan konversi; mengapung ke penyebab int
pemotongan bagian pecahan. Ketika ganda dikonversi menjadi float , apakah nilainya
bulat atau terpotong tergantung implementasi.

Karena argumen panggilan fungsi adalah ekspresi, ketik konversi juga terjadi ketika
argumen dilewatkan ke fungsi. Dengan tidak adanya prototipe fungsi, char dan pendek
menjadi int, dan mengapung menjadi ganda . Inilah sebabnya kami telah mendeklarasikan argumen fungsi ke
menjadi int dan gandakan bahkan ketika fungsi dipanggil dengan char dan float .

Akhirnya, konversi jenis eksplisit dapat dipaksa (`` dipaksakan '') dalam ekspresi apa pun, dengan unary
Operator disebut gips . Dalam pembangunan
ekspresi ( ketik nama )

Halaman 42

43
yang ekspresi dikonversi ke tipe bernama oleh aturan konversi di atas. Tepatnya
arti dari pemeran adalah seolah-olah ekspresi ditugaskan ke variabel dari tipe yang ditentukan, yang
kemudian digunakan sebagai pengganti seluruh konstruksi. Sebagai contoh, sqrt rutin perpustakaan mengharapkan a
argumen ganda , dan akan menghasilkan omong kosong jika secara tidak sengaja menangani hal lain. ( sqrt adalah
dideklarasikan dalam <math.h> .) Jadi jika n adalah bilangan bulat, kita dapat menggunakan

sqrt ((ganda) n)
untuk mengkonversi nilai n menjadi dua kali lipat sebelum meneruskannya ke sqrt . Perhatikan bahwa para pemain menghasilkan
nilai dari n dalam jenis yang tepat; n itu sendiri tidak diubah. Operator cor memiliki tinggi yang sama
diutamakan sebagai operator unary lainnya, sebagaimana dirangkum dalam tabel di akhir bab ini.
Jika argumen dideklarasikan oleh prototipe fungsi, seperti yang seharusnya, deklarasi
menyebabkan pemaksaan otomatis argumen apa pun ketika fungsi dipanggil. Jadi, diberi fungsi
prototipe untuk sqrt :

sqrt ganda (ganda)


panggilan

root2 = sqrt (2)


memaksa integer 2 ke dalam nilai ganda 2.0 tanpa perlu melakukan cast.
Perpustakaan standar mencakup implementasi portabel dari generator nomor pseudo-acak
dan fungsi untuk menginisialisasi benih; yang pertama menggambarkan pemeran:

unsigned long int next = 1;

/ * rand: kembalikan integer pseudo-acak pada 0..32767 * /


int rand (batal)
{
selanjutnya = berikutnya * 1103515245 + 12345;
return (int unsigned) (berikutnya / 65536)% 32768;
}

/ * srand: set seed untuk rand () * /


void srand (seed int unsigned)
{
selanjutnya = benih;
}
Latihan 2-3. Tulis fungsi htoi (s) , yang mengubah serangkaian digit heksadesimal
(termasuk 0x atau 0X opsional ) ke dalam nilai integer yang setara. Digit yang diijinkan adalah 0
melalui 9 , sebuah melalui f , dan A melalui F .

2.8 Operator Peningkatan dan Penurunan


C menyediakan dua operator yang tidak biasa untuk variabel kenaikan dan penurunan. Selisih
operator ++ menambahkan 1 ke operan, sedangkan operator penurunan - kurangi 1. Kami punya
sering digunakan ++ untuk meningkatkan variabel, seperti pada

if (c == '\ n')
++ nl;
Aspek yang tidak biasa adalah bahwa ++ dan - dapat digunakan sebagai operator awalan (sebelum
variabel, seperti pada ++ n ), atau operator postfix (setelah variabel: n ++ ). Dalam kedua kasus, efeknya adalah
kenaikan n . Tetapi ekspresi ++ n menambah n sebelum nilainya digunakan, sedangkan n ++
peningkatan n setelah nilainya digunakan. Ini berarti bahwa dalam konteks di mana nilainya
sedang digunakan, bukan hanya efeknya, ++ n dan n ++ berbeda. Jika n adalah 5, maka

x = n ++;
set x ke 5, tetapi

x = ++ n;

Halaman 43

44
set x ke 6. Dalam kedua kasus, n menjadi 6. Operator kenaikan dan penurunan hanya bisa
diterapkan pada variabel; ekspresi seperti (i + j) ++ ilegal.
Dalam konteks di mana tidak ada nilai yang diinginkan, hanya efek yang bertambah, seperti pada
if (c == '\ n')
nl ++;
awalan dan postfix sama. Tetapi ada situasi di mana satu atau yang lain secara khusus
panggilan untuk. Misalnya, perhatikan fungsi pemerasan (s, c) , yang menghilangkan semua kejadian
dari karakter c dari string s .

/ * squeeze: hapus semua c dari s * /


void squeeze (char s [], int c)
{
int i, j;

untuk (i = j = 0; s [i]! = '\ 0'; i ++)


if (s [i]! = c)
s [j ++] = s [i];
s [j] = '\ 0';
}
Setiap kali non- c terjadi, itu disalin ke posisi j saat ini , dan hanya kemudian j
bertambah agar siap untuk karakter berikutnya. Ini persis sama dengan

if (s [i]! = c) {
s [j] = s [i];
j ++;
}
Contoh lain dari konstruksi serupa berasal dari fungsi getline yang kami tulis
Bab 1 , tempat kita bisa mengganti

if (c == '\ n') {
s [i] = c;
++ i;
}
oleh yang lebih kompak

if (c == '\ n')
s [i ++] = c;
Sebagai contoh ketiga, perhatikan fungsi standar strcat (s, t) , yang menggabungkan
string t hingga akhir string s . strcat mengasumsikan bahwa ada cukup ruang dalam s untuk menampung
kombinasi. Seperti yang telah kami tulis, strcat tidak mengembalikan nilai; versi perpustakaan standar
mengembalikan pointer ke string yang dihasilkan.

/ * strcat: menyatukan t sampai akhir s; Harus cukup besar * /


membatalkan strcat (char s [], char t [])
{
int i, j;

i = j = 0;
while (s [i]! = '\ 0') / * temukan akhir s * /
i ++;
while ((s [i ++] = t [j ++])! = '\ 0') / * salin t * /
;
}
Karena setiap anggota disalin dari t ke s , postfix ++ diterapkan ke i dan j untuk memastikan
bahwa mereka berada dalam posisi untuk melewati loop berikutnya.
Latihan 2-4. Tulis versi alternatif pemerasan (s1, s2) yang menghapus setiap karakter
s1 yang cocok dengan karakter apa pun di string s2 .

Halaman 44

45

Latihan 2-5. Tulis fungsi any (s1, s2) , yang mengembalikan lokasi pertama dalam string s1
di mana karakter apa pun dari string s2 muncul, atau -1 jika s1 tidak mengandung karakter dari s2 .
(Fungsi pustaka standar strpbrk melakukan pekerjaan yang sama tetapi mengembalikan pointer ke
lokasi.)

2.9 Operator Bitwise


C menyediakan enam operator untuk manipulasi bit; ini hanya dapat diterapkan pada operan integral,
yaitu, char , pendek , int , dan panjang , baik yang ditandatangani atau tidak.
& bitwise DAN
| termasuk bitwise ATAU
^ bitwise eksklusif ATAU
<< shift kiri
>> bergeser ke kanan
~ komplemen seseorang (unary)
Bitwise AND operator & sering digunakan untuk menutupi beberapa set bit, misalnya

n = n & 0177;
set ke nol semua kecuali urutan rendah 7 bit n .
Operator bitwise ATAU | digunakan untuk menghidupkan bit:

x = x | DINYALAKAN;
set ke satu dalam x bit yang ditetapkan ke satu di SET_ON .
Operator ATAU bitwise eksklusif ^ menetapkan satu di setiap posisi bit di mana operan-operasinya
bit yang berbeda, dan nol di mana mereka sama.

Seseorang harus membedakan operator bitwise & dan | dari operator logis && dan || yang
menyiratkan evaluasi nilai kebenaran dari kiri ke kanan. Misalnya, jika x adalah 1 dan y adalah 2, maka x & y adalah
nol sementara x && y adalah satu.

Operator shift << dan >> melakukan pergeseran kiri dan kanan operan kiri mereka dengan nomor tersebut
dari posisi bit yang diberikan oleh operan kanan, yang harus non-negatif. Jadi x << 2 shift
nilai x dengan dua posisi, mengisi bit kosong dengan nol; ini setara dengan
perkalian dengan 4. Menggeser dengan benar jumlah yang tidak ditandatangani selalu cocok dengan bit yang dikosongkan dengan nol.
Menggeser kanan kuantitas yang telah ditandatangani akan terisi dengan tanda bit (`` pergeseran aritmatika '') pada beberapa mesin
dan dengan 0-bit (`` shift logis '') pada yang lain.

Operator unary ~ menghasilkan komplemen integer; yaitu, itu mengkonversi setiap 1-bit
menjadi 0-bit dan sebaliknya. Sebagai contoh

x = x & ~ 077
menetapkan enam bit terakhir x ke nol. Perhatikan bahwa x & ~ 077 tidak tergantung pada panjang kata, dan
dengan demikian lebih disukai daripada, misalnya, x & 0177700 , yang mengasumsikan bahwa x adalah kuantitas 16-bit. Itu
bentuk portabel tidak melibatkan biaya tambahan, karena ~ 077 adalah ekspresi konstan yang dapat dievaluasi
pada waktu kompilasi.
Sebagai ilustrasi dari beberapa operator bit, pertimbangkan fungsi getbits (x, p, n) itu
mengembalikan bidang (sesuaikan kanan) n -bit x yang dimulai pada posisi p . Kami menganggap itu sedikit
posisi 0 berada di ujung kanan dan bahwa n dan p adalah nilai positif yang masuk akal. Sebagai contoh,
getbits (x, 4,3) mengembalikan tiga bit pada posisi 4, 3 dan 2, disesuaikan dengan benar.

/ * getbits: dapatkan n bit dari posisi p * /


getbits yang tidak ditandatangani (unsigned x, int p, int n)
{

Halaman 45

46
return (x >> (p + 1-n)) & ~ (~ 0 << n);
}
Ekspresi x >> (p + 1-n) memindahkan bidang yang diinginkan ke ujung kanan kata. ~ 0 semuanya 1-
bit; menggesernya meninggalkan n posisi dengan ~ 0 << n menempatkan nol di paling kanan n bit; melengkapi
yang dengan ~ membuat topeng dengan yang ada di n bit paling kanan .
Latihan 2-6. Tulis setbits fungsi (x, p, n, y) yang mengembalikan x dengan n bit yang dimulai
posisi p diatur ke n bit paling kanan dari y , membiarkan bit lainnya tidak berubah.

Latihan 2-7. Tulis fungsi terbalik (x, p, n) yang mengembalikan x dengan n bit yang dimulai
posisi p terbalik (yaitu, 1 berubah menjadi 0 dan sebaliknya), meninggalkan yang lain tidak berubah.

Latihan 2-8. Tulis fungsi rightrot (x, n) yang mengembalikan nilai integer x diputar
ke kanan dengan posisi n .

2.10 Penugasan Operator dan Ekspresi


Ekspresi seperti

i = i + 2
di mana variabel di sisi kiri diulangi segera di sebelah kanan, dapat ditulis dalam
bentuk terkompresi

i + = 2
Operator + = disebut operator penugasan .
Sebagian besar operator biner (operator seperti + yang memiliki operan kiri dan kanan) memiliki a
operator penugasan yang sesuai op = , di mana op adalah salah satunya

+ - * /% << >> & ^ |


Jika expr 1 dan expr 2 adalah ekspresi, maka

expr 1 op = expr 2

setara dengan

expr 1 = ( expr 1 ) op ( expr 2 )


kecuali bahwa expr 1 dihitung hanya sekali. Perhatikan tanda kurung di sekitar expr 2 :
x * = y + 1
cara

x = x * (y + 1)
daripada

x = x * y + 1
Sebagai contoh, fungsi bitcount menghitung jumlah 1-bit dalam argumen integernya .

/ * bitcount: hitung 1 bit dalam x * /


int bitcount (tidak ditandatangani x)
{
int b;

untuk (b = 0; x! = 0; x >> = 1)
jika (x & 01)
b ++;
kembali b;
}
Mendeklarasikan argumen x menjadi unsigned memastikan bahwa ketika benar-bergeser, dikosongkan bit
akan diisi dengan angka nol, bukan tanda bit, terlepas dari mesin mana program dijalankan.

Halaman 46

47
Terlepas dari keringkasan, operator penugasan memiliki keunggulan yang sesuai
lebih baik cara orang berpikir. Kami mengatakan `` tambahkan 2 ke i '' atau `` increment i by 2 '', bukan `` take i , add 2,
lalu masukkan hasilnya kembali ke i ''. Jadi ungkapan i + = 2 lebih disukai daripada i = i + 2 . Di
Selain itu, untuk ekspresi rumit seperti

yyval [yypv [p3 + p4] + yypv [p1]] + = 2


operator penugasan membuat kode lebih mudah dipahami, karena pembaca tidak harus melakukannya
periksa dengan susah payah bahwa dua ekspresi panjang memang sama, atau bertanya-tanya mengapa mereka
tidak. Dan operator penugasan bahkan dapat membantu kompiler untuk menghasilkan kode yang efisien.
Kita telah melihat bahwa pernyataan penugasan memiliki nilai dan dapat terjadi dalam ekspresi;
contoh paling umum adalah

while ((c = getchar ())! = EOF)


...
Operator penugasan lainnya ( + = , - = , dll.) Juga dapat muncul dalam ekspresi, meskipun demikian
jarang.
Dalam semua ekspresi seperti itu, tipe ekspresi penugasan adalah tipe operan kirinya, dan
nilainya adalah nilai setelah penugasan.

Latihan 2-9. Dalam sistem bilangan pelengkap dua, x & = (x-1) menghapus 1-bit paling kanan
dalam x . Jelaskan mengapa. Gunakan pengamatan ini untuk menulis versi bitcount yang lebih cepat .

2.11 Ekspresi Bersyarat


Pernyataan

jika (a> b)
z = a;
lain
z = b;
menghitung dalam z maksimum a dan b . The ekspresi kondisional , ditulis dengan terner yang
operator `` ?: '', menyediakan cara alternatif untuk menulis konstruksi ini dan yang serupa. Dalam
ekspresi

expr 1 ? expr 2 : expr 3

ekspresi expr 1 dievaluasi terlebih dahulu. Jika bukan nol (benar), maka ekspresi expr 2 adalah
dievaluasi, dan itu adalah nilai ekspresi kondisional. Kalau tidak, expr 3 dievaluasi, dan
itulah nilainya. Hanya satu dari expr 2 dan expr 3 yang dievaluasi. Jadi untuk mengatur z ke maksimum a
dan b ,

z = (a> b)? a: b; / * z = maks (a, b) * /


Perlu dicatat bahwa ekspresi kondisional memang ekspresi, dan dapat digunakan
dimanapun ekspresi lain bisa. Jika expr 2 dan expr 3 adalah dari tipe yang berbeda, tipe dari
hasil ditentukan oleh aturan konversi yang dibahas sebelumnya dalam bab ini. Misalnya, jika f
adalah mengambang dan n sebuah int , maka ekspresi

(n> 0)? f: n
adalah tipe float terlepas dari apakah n adalah positif.
Kurung tidak diperlukan di sekitar ekspresi pertama dari ekspresi kondisional, karena
diutamakan dari :? sangat rendah, tepat di atas tugas. Bagaimanapun mereka disarankan,
karena mereka membuat kondisi bagian dari ekspresi lebih mudah dilihat.
Ekspresi bersyarat sering mengarah pada kode ringkas. Sebagai contoh, loop ini mencetak n
elemen array, 10 per baris, dengan setiap kolom dipisahkan oleh satu kosong, dan dengan setiap baris
(termasuk yang terakhir) diakhiri oleh baris baru.

Halaman 47

48

untuk (i = 0; i <n; i ++)


printf ("% 6d% c", a [i], (i% 10 == 9 || i == n-1)? '\ n': '');
Baris baru dicetak setelah setiap elemen kesepuluh, dan setelah ke- n . Semua elemen lainnya
diikuti oleh satu kosong. Ini mungkin terlihat rumit, tetapi lebih kompak daripada jika
lain . Contoh bagus lainnya adalah

printf ("Anda memiliki% d item% s. \ n", n, n == 1? "": "s");


Latihan 2-10. Tulis ulang fungsi lebih rendah , yang mengubah huruf besar menjadi huruf kecil,
dengan ekspresi kondisional alih-alih if-else .

2.12. Prioritas dan Urutan Evaluasi


Tabel 2.1 merangkum aturan untuk diutamakan dan asosiasi dari semua operator, termasuk
yang belum kita diskusikan. Operator di jalur yang sama memiliki prioritas yang sama;
baris dalam urutan menurun diutamakan, jadi, misalnya, * , / , dan % semua memiliki yang sama
diutamakan, yang lebih tinggi dari biner + dan - . `` Operator '' () merujuk ke fungsi
panggilan. Operator -> dan . digunakan untuk mengakses anggota struktur; mereka akan dibahas
Bab 6 , bersama dengan sizeof (ukuran objek). Bab 5 membahas * (tipuan melalui a
pointer) dan & (alamat suatu objek), dan Bab 3 membahas operator koma.
Operator Asosiatif
() [] ->. kiri ke kanan
! ~ ++ - + - * ( tipe ) sizeof kanan ke kiri
* /% kiri ke kanan
+ - kiri ke kanan
<< >> kiri ke kanan
<<=>> = kiri ke kanan
==! = kiri ke kanan
& kiri ke kanan
^ kiri ke kanan
| kiri ke kanan
&& kiri ke kanan
|| kiri ke kanan
?: kanan ke kiri
= + = - = * = / =% = & = ^ = | = << = >> = kanan ke kiri
, kiri ke kanan
Unary & +, -, dan * memiliki prioritas lebih tinggi daripada bentuk biner.

Tabel 2.1: Presedensi dan Asosiasi Operator

Perhatikan bahwa prioritas operator bitwise & , ^ , dan | jatuh di bawah == dan ! = . Ini
menyiratkan bahwa ekspresi pengujian bit suka

if ((x & MASK) == 0) ...


harus dikurung sepenuhnya untuk memberikan hasil yang tepat.
C, seperti kebanyakan bahasa, tidak menentukan urutan operan dari operator
dievaluasi. (Pengecualiannya adalah && , || , ?: , Dan ` , '.) Misalnya, dalam pernyataan seperti

x = f () + g ();

Halaman 48

49

f dapat dievaluasi sebelum g atau sebaliknya; jadi jika salah satu f atau g mengubah variabel yang mana
lain tergantung, x dapat tergantung pada urutan evaluasi. Hasil antara dapat disimpan di
variabel sementara untuk memastikan urutan tertentu.
Demikian pula, urutan di mana argumen fungsi dievaluasi tidak ditentukan, jadi
pernyataan

printf ("% d% d \ n", ++ n, power (2, n)); / * SALAH * /


dapat menghasilkan hasil yang berbeda dengan kompiler yang berbeda, tergantung pada apakah n bertambah
sebelum kekuatan dipanggil. Solusinya, tentu saja, adalah menulis

++ n;
printf ("% d% d \ n", n, power (2, n));
Panggilan fungsi, pernyataan penugasan bersarang, dan penyebab kenaikan dan penurunan operator
`` efek samping '' - beberapa variabel diubah sebagai produk sampingan dari evaluasi ekspresi. Di
ekspresi apa pun yang melibatkan efek samping, dapat ada ketergantungan halus pada urutannya
variabel yang mengambil bagian dalam ekspresi diperbarui. Satu situasi yang tidak menyenangkan ditandai oleh
pernyataan

a [i] = i ++;
Pertanyaannya adalah apakah subskrip tersebut adalah nilai lama dari i atau yang baru. Compiler dapat menafsirkan
ini dengan cara yang berbeda, dan menghasilkan jawaban yang berbeda tergantung pada interpretasinya. Itu
standar sengaja membiarkan sebagian besar masalah seperti itu tidak ditentukan. Ketika efek samping (tugas untuk
variabel) terjadi dalam ekspresi yang diserahkan kepada kebijaksanaan kompiler, karena yang terbaik
pesanan sangat bergantung pada arsitektur mesin. (Standar tidak menentukan bahwa semua efek samping
pada argumen berlaku sebelum suatu fungsi dipanggil, tetapi itu tidak akan membantu dalam panggilan ke
printf di atas.)
Moralnya adalah bahwa menulis kode yang tergantung pada urutan evaluasi adalah pemrograman yang buruk
berlatih dalam bahasa apa pun. Secara alami, perlu mengetahui hal-hal apa yang harus dihindari, tetapi jika Anda
tidak tahu bagaimana mereka dilakukan pada berbagai mesin, Anda tidak akan tergoda untuk memanfaatkannya
implementasi tertentu.

Halaman 49

50

Bab 3 - Aliran Kontrol


Alur kontrol dari suatu bahasa menentukan urutan komputasi dilakukan. Kita
telah memenuhi konstruksi aliran kontrol yang paling umum dalam contoh sebelumnya; disini kita
akan menyelesaikan set, dan lebih tepat tentang yang dibahas sebelumnya.

3.1 Pernyataan dan Blok


Ekspresi seperti x = 0 atau i ++ atau printf (...) menjadi pernyataan ketika diikuti
oleh titik koma, seperti pada

x = 0;
i ++;
printf (...);
Di C, titik koma adalah terminator pernyataan, bukan pemisah seperti dalam bahasa seperti
Pascal.
Kawat gigi { dan } digunakan untuk mengelompokkan deklarasi dan pernyataan bersama menjadi suatu senyawa
pernyataan , atau blok , sehingga secara sintaksis setara dengan satu pernyataan. Kawat gigi
yang melingkupi pernyataan fungsi adalah salah satu contoh nyata; menguatkan beberapa
pernyataan setelah if , else , while , atau for adalah yang lain. (Variabel dapat dideklarasikan di dalam
blok; kita akan membicarakan hal ini di Bab 4. ) Tidak ada titik koma setelah tanda kurung yang tepat
mengakhiri satu blok.

3.2 If-Else
Pernyataan if-else digunakan untuk mengekspresikan keputusan. Secara formal sintaksnya adalah

jika ( ekspresi )
pernyataan 1
lain
pernyataan 2
di mana bagian lain adalah opsional. The ekspresi dievaluasi; jika itu benar (yaitu, jika ekspresi
memiliki nilai bukan nol), pernyataan 1 dijalankan. Jika salah ( ekspresi nol) dan jika ada
lain bagian, pernyataan 2 dijalankan sebagai gantinya.
Karena a jika menguji nilai numerik ekspresi, pintasan koding tertentu dimungkinkan. Itu
paling jelas adalah menulis

jika ( ekspresi )
dari pada

jika ( ekspresi ! = 0)
Terkadang ini alami dan jelas; di lain waktu itu bisa samar.
Karena bagian lain dari if-else adalah opsional, ada ambiguitas ketika yang lain jika dihilangkan
dari urutan bersarang jika . Ini diselesaikan dengan mengaitkan yang lain dengan yang terdekat sebelumnya
lain -kecuali jika . Misalnya, dalam

jika (n> 0)
jika (a> b)
z = a;
lain
z = b;
yang lain masuk ke batin jika , seperti yang telah kita tunjukkan dengan lekukan. Jika itu bukan yang Anda inginkan,
kawat gigi harus digunakan untuk memaksa hubungan yang tepat:

Halaman 50

51
if (n> 0) {
jika (a> b)
z = a;
}
lain
z = b;
Ambiguitas sangat merusak dalam situasi seperti ini:

jika (n> 0)
untuk (i = 0; i <n; i ++)
if (s [i]> 0) {
printf ("...");
mengembalikan saya;
}
lain / * SALAH * /
printf ("error - n adalah negatif \ n");
Lekukan menunjukkan dengan tegas apa yang Anda inginkan, tetapi kompiler tidak mendapatkan pesan,
dan mengaitkan yang lain dengan batin jika . Jenis bug ini mungkin sulit ditemukan; itu ide yang bagus
untuk menggunakan kawat gigi ketika ada bersarang jika s.
Ngomong-ngomong, perhatikan bahwa ada titik koma setelah z = a in

jika (a> b)
z = a;
lain
z = b;
Ini karena secara tata bahasa, pernyataan mengikuti if , dan pernyataan ekspresi seperti `` z
= a; '' selalu diakhiri dengan tanda titik koma.

3.3 Lain-Jika
Konstruksi

jika ( ekspresi )
pernyataan
lain jika ( ekspresi )
pernyataan
lain jika ( ekspresi )
pernyataan
lain jika ( ekspresi )
pernyataan
lain
pernyataan
sering terjadi sehingga perlu diskusi singkat yang terpisah. Urutan pernyataan if ini adalah
cara paling umum menulis keputusan multi-arah. The ekspresi dievaluasi dalam rangka;
jika ekspresi benar, pernyataan yang terkait dengannya dieksekusi, dan ini mengakhiri
seluruh rantai. Seperti biasa, kode untuk setiap pernyataan adalah pernyataan tunggal, atau sekelompok
mereka di kawat gigi.
Bagian lain terakhir menangani `` tidak ada di atas '' atau kasus default di mana tidak ada yang lain
kondisi terpenuhi. Terkadang tidak ada tindakan eksplisit untuk default; dalam hal ini
tertinggal

lain
pernyataan
dapat dihilangkan, atau dapat digunakan untuk memeriksa kesalahan untuk menangkap kondisi `` mustahil ''.
Untuk mengilustrasikan keputusan tiga arah, berikut ini adalah fungsi pencarian biner yang memutuskan jika ada yang khusus
nilai x terjadi dalam array yang diurutkan v . Elemen v harus dalam urutan yang meningkat. Itu
fungsi mengembalikan posisi (angka antara 0 dan n-1 ) jika x terjadi dalam v , dan -1 jika tidak.

Halaman 51

52
Pencarian biner pertama membandingkan nilai input x ke elemen tengah dari array v . Jika x kurang
daripada nilai tengah, pencarian berfokus pada bagian bawah tabel, jika tidak di bagian atas
setengah. Dalam kedua kasus tersebut, langkah selanjutnya adalah membandingkan x dengan elemen tengah dari setengah yang dipilih.
Proses membagi rentang dalam dua berlanjut sampai nilainya ditemukan atau kisaran tersebut
kosong.

/ * binsearch: cari x dalam v [0] <= v [1] <= ... <= v [n-1] * /
int binsearch (int x, int v [], int n)
{
int low, high, mid;

rendah = 0;
tinggi = n - 1;
sementara (rendah <= tinggi) {
mid = (rendah + tinggi) / 2;
if (x <v [mid])
tinggi = pertengahan + 1;
lain jika (x> v [pertengahan])
rendah = pertengahan + 1;
lain / * ditemukan cocok * /
kembali pertengahan;
}
return -1; / * tidak cocok * /
}
Keputusan mendasar adalah apakah x kurang dari, lebih besar dari, atau sama dengan elemen tengah
v [pertengahan] pada setiap langkah; ini wajar untuk yang lain-jika .
Latihan 3-1. Pencarian biner kami membuat dua tes di dalam loop, ketika satu sudah cukup (at
harga lebih banyak tes di luar.) Tulis versi dengan hanya satu tes di dalam loop dan ukur
perbedaan run-time.

3.4 Beralih
The saklar pernyataan adalah keputusan multi-cara itu tes apakah suatu ekspresi cocok dengan salah satu
sejumlah nilai integer konstan , dan cabang sesuai.

beralih ( ekspresi ) {
case const-expr : pernyataan
case const-expr : pernyataan
default: pernyataan
}
Setiap case diberi label oleh satu atau lebih konstanta bernilai integer atau ekspresi konstan. Jika suatu kasus
cocok dengan nilai ekspresi, eksekusi dimulai pada kasus itu. Semua ekspresi case harus
berbeda. Kasus berlabel standar dijalankan jika tidak ada kasus lain yang puas. SEBUAH
default adalah opsional; jika tidak ada dan jika tidak ada kasus yang cocok, tidak ada tindakan sama sekali yang terjadi.
Kasus dan klausa default dapat terjadi dalam urutan apa pun.
Dalam Bab 1 kami menulis sebuah program untuk menghitung kemunculan setiap digit, spasi putih, dan semua
karakter lain, menggunakan urutan if ... else if ... else . Ini program yang sama
dengan saklar :

#termasuk <stdio.h>

main () / * hitungan digit, spasi putih, lainnya * /


{
int c, i, nwhite, nother, ndigit [10];

nwhite = nother = 0;
untuk (i = 0; i <10; i ++)
ndigit [i] = 0;
while ((c = getchar ())! = EOF) {

Halaman 52

53
beralih (c) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
ndigit [c-'0 '] ++;
istirahat;
kasus ' ':
huruf '\ n':
huruf 't':
nwhite ++;
istirahat;
default:
nother ++;
istirahat;
}
}
printf ("digit =");
untuk (i = 0; i <10; i ++)
printf ("% d", ndigit [i]);
printf (", spasi putih =% d, lainnya =% d \ n",
nwhite, nother);
return 0;
}
The istirahat pernyataan menyebabkan segera keluar dari saklar . Karena kasus berfungsi sama seperti
label, setelah kode untuk satu kasus selesai, eksekusi jatuh ke yang berikutnya kecuali Anda mengambil
tindakan eksplisit untuk melarikan diri. istirahat dan kembali adalah cara paling umum untuk meninggalkan saklar . SEBUAH
pernyataan break juga dapat digunakan untuk memaksa keluar langsung dari saat , untuk , dan melakukan loop,
seperti yang akan dibahas nanti dalam bab ini.
Jatuh melalui kasus-kasus adalah berkat yang beragam. Di sisi positif, memungkinkan beberapa kasus terjadi
terlampir pada satu tindakan, seperti halnya angka dalam contoh ini. Tetapi itu juga menyiratkan hal itu secara normal
setiap kasing harus diakhiri dengan istirahat untuk mencegah jatuh ke yang berikutnya. Jatuh dari
satu kasus ke yang lain tidak kuat, rentan terhadap disintegrasi ketika program dimodifikasi.
Dengan pengecualian beberapa label untuk satu komputasi, fall-throughs harus digunakan
hemat, dan berkomentar.

Sebagai bentuk yang baik, beri istirahat setelah kasus terakhir ( default di sini) meskipun itu
secara logis tidak perlu. Suatu hari ketika kasus lain ditambahkan pada akhirnya, ini sedikit defensif
pemrograman akan menghemat Anda.

Latihan 3-2. Tulis fungsi pelarian (s, t) yang mengubah karakter seperti baris baru dan tab menjadi
terlihat urutan pelarian seperti \ n dan \ t saat menyalin string t ke s . Gunakan sakelar . Menulis sebuah
berfungsi untuk arah lain juga, mengubah urutan melarikan diri menjadi karakter nyata.

3.5 Loops - While dan For


Kami telah menemukan while dan for loop. Di

sementara ( ekspresi )
pernyataan
yang ekspresi dievaluasi. Jika tidak nol, pernyataan dieksekusi dan ekspresi dikembalikan
dievaluasi. Siklus ini berlanjut sampai ekspresi menjadi nol, di mana titik eksekusi
dilanjutkan setelah pernyataan .
The untuk pernyataan

untuk ( expr 1 ; expr 2 ; expr 3 )


pernyataan
setara dengan

expr 1 ;
while ( expr 2 ) {

Halaman 53

54
pernyataan
expr 3 ;
}
kecuali untuk perilaku lanjutan , yang dijelaskan dalam Bagian 3.7 .
Secara gramatikal, tiga komponen dari for for adalah ekspresi. Paling umum, expr 1
dan expr 3 adalah penugasan atau panggilan fungsi dan expr 2 adalah ekspresi relasional. Salah satu
tiga bagian dapat dihilangkan, meskipun titik koma harus tetap ada. Jika expr 1 atau expr 3 dihilangkan, itu
hanya dijatuhkan dari ekspansi. Jika tes, expr 2 , tidak ada, itu diambil sebagai
benar secara permanen, jadi

untuk (;;) {
...
}
adalah loop `` tak terbatas '', mungkin akan diputus dengan cara lain, seperti istirahat atau kembali .
Apakah akan digunakan sementara atau untuk sebagian besar adalah masalah preferensi pribadi. Misalnya, dalam

while ((c = getchar ()) == '' || c == '\ n' || c = '\ t')


; / * lewati karakter spasi putih * /
tidak ada inisialisasi atau re-inisialisasi, jadi sementara yang paling alami.
The untuk disukai ketika ada inisialisasi sederhana dan kenaikan karena itu membuat lingkaran
pernyataan kontrol saling berdekatan dan terlihat di bagian atas loop. Ini paling jelas di

untuk (i = 0; i <n; i ++)


...
yang merupakan idiom C untuk memproses elemen n pertama dari array, analog dari Fortran
DO loop atau Pascal untuk . Analogi ini tidak sempurna, karena variabel indeks i
mempertahankan nilainya ketika loop berakhir karena alasan apa pun. Karena komponen untuk
adalah ekspresi sewenang-wenang, karena loop tidak terbatas pada perkembangan aritmatika. Meskipun begitu,
itu adalah gaya yang buruk untuk memaksa perhitungan yang tidak terkait ke inisialisasi dan penambahan a for ,
yang lebih baik disediakan untuk operasi kontrol loop.
Sebagai contoh yang lebih besar, berikut adalah versi lain atoi untuk mengonversi string menjadi numeriknya
setara. Yang ini sedikit lebih umum daripada yang ada di Bab 2 ; itu mengatasi dengan opsional
memimpin ruang putih dan tanda + atau - opsional . ( Bab 4 menunjukkan hal yang sama, yang melakukan hal yang sama
konversi untuk angka floating-point.)

Struktur program mencerminkan bentuk input:

lewati ruang putih, jika ada


dapatkan tanda, jika ada
dapatkan bagian integer dan konversikan

Setiap langkah melakukan bagiannya, dan membiarkan semuanya dalam keadaan bersih untuk langkah berikutnya. Seluruh proses
berakhir pada karakter pertama yang tidak bisa menjadi bagian dari angka.

#termasuk <ctype.h>

/ * atoi: convert s ke integer; versi 2 * /


int atoi (char s [])
{
int i, n, tandatangani;

untuk (i = 0; ruang is (s [i]); i ++) / * lewati spasi putih * /


;
sign = (s [i] == '-')? -1: 1;
if (s [i] == '+' || s [i] == '-') / * lewati tanda * /
i ++;

Halaman 54

55
untuk (n = 0; isdigit (s [i]); i ++)
n = 10 * n + (s [i] - '0');
tanda balik * n;
}
Pustaka standar menyediakan fungsi strtol yang lebih rumit untuk konversi string menjadi
bilangan bulat panjang; lihat Bagian 5 dari Lampiran B .
Keuntungan dari menjaga kontrol loop tetap terpusat bahkan lebih jelas ketika ada
beberapa loop bersarang. Fungsi berikut adalah pengurutan Shell untuk mengurutkan array bilangan bulat. Itu
Ide dasar dari algoritma pengurutan ini, yang ditemukan pada tahun 1959 oleh DL Shell, adalah bahwa pada awal
tahapan, elemen berjauhan dibandingkan, bukan yang berdekatan seperti dalam pertukaran yang lebih sederhana
macam. Ini cenderung menghilangkan sejumlah besar gangguan dengan cepat, sehingga tahap selanjutnya memiliki lebih sedikit pekerjaan
melakukan. Interval antara elemen-elemen yang dibandingkan secara bertahap dikurangi menjadi satu, pada titik mana
pengurutan secara efektif menjadi metode pertukaran yang berdekatan.

/ * shellsort: urutkan v [0] ... v [n-1] dalam urutan yang meningkat * /


void shellsort (int v [], int n)
{
int gap, i, j, temp;

untuk (gap = n / 2; gap> 0; gap / = 2)


untuk (i = gap; i <n; i ++)
untuk (j = i-gap; j> = 0 && v [j]> v [j + gap]; j- = gap) {
temp = v [j];
v [j] = v [j + gap];
v [j + gap] = temp;
}
}
Ada tiga loop bersarang. Bagian terluar mengontrol jarak antara elemen-elemen yang dibandingkan,
menyusutkannya dari n / 2 dengan faktor dua setiap pass sampai menjadi nol. Langkah tengah loop
sepanjang elemen. Loop terdalam membandingkan setiap pasangan elemen yang dipisahkan oleh
celah dan membalikkan semua yang rusak. Karena kesenjangan akhirnya dikurangi menjadi satu, semua elemen
akhirnya dipesan dengan benar. Perhatikan bagaimana sifat umum untuk membuat loop luar pas
dalam bentuk yang sama dengan yang lain, meskipun itu bukan perkembangan aritmatika.
Satu operator C terakhir adalah koma `` , '', yang paling sering ditemukan digunakan dalam pernyataan for . SEBUAH
sepasang ekspresi yang dipisahkan oleh koma dievaluasi dari kiri ke kanan, dan jenis dan nilai
hasilnya adalah jenis dan nilai operan yang tepat. Jadi dalam pernyataan for, dimungkinkan untuk
letakkan banyak ekspresi di berbagai bagian, misalnya untuk memproses dua indeks secara paralel.
Hal ini digambarkan dalam fungsi terbalik (s) , yang membalikkan string s di tempat.

#termasuk <string.h>

/ * mundur: membalikkan string s di tempat * /


membatalkan kebalikan (char s [])
{
int c, i, j;

untuk (i = 0, j = strlen (s) -1; i <j; i ++, j--) {


c = s [i];
s [i] = s [j];
s [j] = c;
}
}
Koma yang memisahkan argumen fungsi, variabel dalam deklarasi, dll., Bukan koma
operator, dan tidak menjamin evaluasi kiri ke kanan.
Operator koma harus digunakan dengan hemat. Penggunaan yang paling cocok adalah untuk konstruksi yang kuat
terkait satu sama lain, seperti pada for loop secara terbalik , dan di macro di mana multistep
perhitungan harus berupa ekspresi tunggal. Ekspresi koma juga mungkin sesuai untuk

Halaman 55

56
pertukaran elemen secara terbalik , di mana pertukaran dapat dianggap satu
operasi:

untuk (i = 0, j = strlen (s) -1; i <j; i ++, j--)


c = s [i], s [i] = s [j], s [j] = c;
Latihan 3-3. Tulis perluasan fungsi (s1, s2) yang memperluas notasi singkatan seperti az in
string s1 ke dalam daftar lengkap yang setara abc ... xyz di s2 . Izinkan untuk surat dari kedua kasus
dan digit, dan bersiaplah untuk menangani kasus seperti abc dan a-z0-9 dan -az . Atur bahwa a
memimpin atau mengikuti - diartikan secara harfiah.

3,6 Loops - Do-While


Seperti yang kita bahas di Bab 1 , while dan for loop menguji kondisi terminasi di
teratas. Sebaliknya, loop ketiga dalam C, do-while , menguji di bagian bawah setelah melakukan setiap gerakan
melalui loop body; tubuh selalu dieksekusi setidaknya sekali.
Sintaks dari do adalah

melakukan
pernyataan
sementara ( ekspresi );
The pernyataan dijalankan, maka ekspresi dievaluasi. Jika itu benar, pernyataan dievaluasi
lagi, dan seterusnya. Saat ekspresi menjadi salah, loop berakhir. Kecuali indra
tes, do-while setara dengan pernyataan repeat-hingga Pascal .
Pengalaman menunjukkan bahwa do-while jauh lebih sedikit digunakan daripada sementara dan untuk . Meskipun demikian, dari
dari waktu ke waktu itu berharga, seperti dalam fungsi berikut itoa , yang mengubah angka menjadi a
string karakter (kebalikan dari atoi ). Pekerjaan itu sedikit lebih rumit dari yang seharusnya
berpikir pada awalnya, karena metode mudah menghasilkan angka menghasilkan mereka yang salah
memesan. Kami telah memilih untuk membuat string mundur, lalu balikkan.

/ * itoa: konversikan n ke karakter dalam s * /


batal itoa (int n, char s [])
{
int i, tandatangani;

if ((sign = n) <0) / * rekam tanda * /


n = -n; / * membuat n positif * /
i = 0;
do {/ * menghasilkan digit dalam urutan terbalik * /
s [i ++] = n% 10 + '0'; / * dapatkan angka berikutnya * /
} while ((n / = 10)> 0); /* Hapus */
if (tanda <0)
s [i ++] = '-';
s [i] = '\ 0';
membalikkan;
}
The do-while diperlukan, atau setidaknya nyaman, karena setidaknya satu karakter harus
dipasang di array s , bahkan jika n adalah nol. Kami juga menggunakan kawat gigi di sekitar pernyataan tunggal itu
membuat tubuh do-while , meskipun mereka tidak perlu, jadi pembaca terburu-buru
tidak akan kesalahan sementara bagian untuk awal dari sementara lingkaran.
Latihan 3-4. Dalam representasi angka pelengkap dua, versi itoa kami tidak
menangani angka negatif terbesar, yaitu nilai n sama dengan - (2 wordsize-1 ). Jelaskan mengapa tidak.
Ubah untuk mencetak nilai itu dengan benar, terlepas dari mesin yang menjalankannya.

Latihan 3-5. Menulis fungsi itob (n, s, b) yang mengubah integer n menjadi dasar b
representasi karakter dalam string s . Secara khusus, itob (n, s, 16) format s sebagai
integer heksadesimal dalam s .

Halaman 56

57

Latihan 3-6. Tulis versi itoa yang menerima tiga argumen, bukan dua. Ketiga
argumen adalah lebar bidang minimum; nomor yang dikonversi harus diisi dengan kosong pada
tersisa jika perlu untuk membuatnya cukup lebar.

3.7 Istirahat dan Lanjutkan


Terkadang nyaman untuk keluar dari loop selain dengan menguji di atas atau
bawah. The istirahat pernyataan memberikan keluar awal dari untuk , sementara , dan lakukan , seperti dari
beralih . Sebuah istirahat menyebabkan loop melampirkan terdalam atau saklar untuk keluar segera.

Fungsi berikut, trim , menghapus trailing blank, tab, dan baris baru dari akhir a
string, menggunakan break untuk keluar dari loop ketika baris paling kanan kosong, non-tab, non-baris baru
ditemukan.

/ * trim: hapus trailing blank, tabs, newlines * /


int trim (char s [])
{
int n;

untuk (n = strlen (s) -1; n> = 0; n--)


if (s [n]! = '' && s [n]! = '\ t' && s [n]! = '\ n')
istirahat;
s [n + 1] = '\ 0';
return n;
}
strlen mengembalikan panjang string. The untuk lingkaran dimulai pada akhir dan scan mundur
mencari karakter pertama yang bukan kosong atau tab atau baris baru. Loop rusak saat
satu ditemukan, atau ketika n menjadi negatif (yaitu, ketika seluruh string telah dipindai).
Anda harus memverifikasi bahwa ini adalah perilaku yang benar bahkan ketika string kosong atau hanya berisi
karakter spasi putih.
The melanjutkan pernyataan berkaitan dengan istirahat , tapi kurang sering digunakan; itu menyebabkan iterasi berikutnya
lampirkan untuk , sementara , atau lakukan loop untuk memulai. Di saat dan melakukan , ini berarti tes
bagian dieksekusi segera; di for , kontrol beralih ke langkah kenaikan. The melanjutkan
Pernyataan hanya berlaku untuk loop, bukan untuk beralih . Sebuah terus di dalam saklar di dalam lingkaran
menyebabkan iterasi loop berikutnya.

Sebagai contoh, fragmen ini hanya memproses elemen non-negatif dalam array a ; negatif
nilai dilewati.

untuk (i = 0; i <n; i ++)


jika (a [i] <0) / * lewati elemen negatif * /
terus;
... / * lakukan elemen positif * /
The melanjutkan pernyataan sering digunakan ketika bagian dari loop yang mengikuti rumit, sehingga
bahwa membalikkan tes dan membuat indentasi tingkat lain akan membuat sarang program terlalu dalam.

3.8 Goto dan label


C menyediakan jauh-abusable goto pernyataan, dan label untuk cabang untuk. Secara resmi, goto
pernyataan tidak pernah diperlukan, dan dalam praktiknya hampir selalu mudah untuk menulis kode tanpanya.
Kami belum menggunakan goto dalam buku ini.
Namun demikian, ada beberapa situasi di mana goto dapat menemukan tempat. Yang paling umum adalah
meninggalkan pemrosesan dalam beberapa struktur yang sangat bersarang, seperti memecah dua atau lebih
loop sekaligus. The istirahat pernyataan tidak dapat digunakan secara langsung karena hanya keluar dari
loop terdalam. Jadi:

untuk (...)

Halaman 57

58
untuk (...) {
...
jika (bencana)
kesalahan goto;
}
...
kesalahan:
/ * membersihkan kekacauan * /
Organisasi ini berguna jika kode penanganan kesalahan adalah non-sepele, dan jika kesalahan dapat terjadi di
beberapa tempat.
Label memiliki bentuk yang sama dengan nama variabel, dan diikuti oleh tanda titik dua. Itu bisa dilampirkan
pernyataan apa pun dalam fungsi yang sama dengan goto . Lingkup label adalah seluruh fungsi.

Sebagai contoh lain, pertimbangkan masalah menentukan apakah dua array a dan b memiliki
elemen yang sama. Satu kemungkinan adalah

untuk (i = 0; i <n; i ++)


untuk (j = 0; j <m; j ++)
if (a [i] == b [j])
goto ditemukan;
/ * tidak menemukan elemen umum * /
...
ditemukan:
/ * punya satu: a [i] == b [j] * /
...
Kode yang melibatkan goto selalu dapat ditulis tanpa satu, meskipun mungkin dengan harga beberapa
tes berulang atau variabel tambahan. Misalnya, pencarian array menjadi
ditemukan = 0;
untuk (i = 0; i <n &&! ditemukan; i ++)
untuk (j = 0; j <m &&! ditemukan; j ++)
if (a [i] == b [j])
ditemukan = 1;
jika ketemu)
/ * dapatkan satu: a [i-1] == b [j-1] * /
...
lain
/ * tidak menemukan elemen umum * /
...
Dengan beberapa pengecualian seperti yang dikutip di sini, kode yang bergantung pada pernyataan goto umumnya
lebih sulit dipahami dan dipelihara daripada kode tanpa goto s. Meskipun kami tidak dogmatis
tentang masalah ini, tampaknya pernyataan goto harus jarang digunakan, jika sama sekali.

Halaman 58

59
Bab 4 - Fungsi dan Program
Struktur
Fungsi memecah tugas-tugas komputasi besar menjadi yang lebih kecil, dan memungkinkan orang untuk membangun apa
yang lain telah melakukan alih-alih memulai dari awal. Fungsi yang sesuai menyembunyikan detail
operasi dari bagian program yang tidak perlu tahu tentang mereka, sehingga memperjelas
utuh, dan meringankan rasa sakit karena membuat perubahan.
C telah dirancang untuk membuat fungsi menjadi efisien dan mudah digunakan; Program C umumnya terdiri
banyak fungsi kecil daripada beberapa yang besar. Suatu program dapat berada dalam satu atau lebih
file sumber. File sumber dapat dikompilasi secara terpisah dan dimuat bersama, bersama dengan
fungsi yang dikompilasi sebelumnya dari perpustakaan. Kami tidak akan masuk ke proses itu di sini, bagaimanapun,
karena perinciannya bervariasi dari satu sistem ke sistem lainnya.

Deklarasi dan definisi fungsi adalah area di mana standar ANSI telah membuat sebagian besar
perubahan ke C. Seperti yang kita lihat pertama di Bab 1 , sekarang dimungkinkan untuk menyatakan jenis argumen
ketika suatu fungsi dideklarasikan. Sintaks deklarasi fungsi juga berubah, sehingga
deklarasi dan definisi cocok. Hal ini memungkinkan kompiler untuk mendeteksi lebih banyak
kesalahan daripada sebelumnya. Selanjutnya, ketika argumen dideklarasikan dengan benar, tepat
paksaan tipe dilakukan secara otomatis.

Standar ini mengklarifikasi aturan tentang ruang lingkup nama; khususnya, harus ada
hanya satu definisi dari setiap objek eksternal. Inisialisasi lebih umum: array otomatis dan
struktur sekarang dapat diinisialisasi.

Preprosesor C juga telah ditingkatkan. Fasilitas preprocessor baru termasuk lebih


set lengkap arahan kompilasi bersyarat, cara untuk membuat string yang dikutip dari makro
argumen, dan kontrol yang lebih baik atas proses ekspansi makro.

4.1 Dasar-dasar Fungsi


Untuk memulainya, mari kita merancang dan menulis sebuah program untuk mencetak setiap baris inputnya yang berisi a
`` pola '' atau string karakter tertentu. (Ini adalah kasus khusus dari grep program UNIX .)
Misalnya, mencari pola huruf `` ould '' di set baris
Ah Cinta! bisakah kau dan aku dengan Takdir berkonspirasi
Untuk memahami keseluruhan Skema Maaf ini,
Bukankah kita akan hancur berkeping-keping - dan kemudian
Cetak ulang itu lebih dekat dengan Keinginan Hati!
akan menghasilkan output
Ah Cinta! bisakah kau dan aku dengan Takdir berkonspirasi
Bukankah kita akan hancur berkeping-keping - dan kemudian
Cetak ulang itu lebih dekat dengan Keinginan Hati!
Pekerjaan itu terbagi menjadi tiga bagian:

sementara ( ada garis lain )


if ( baris berisi pola )
cetak ini
Meskipun tentu saja mungkin untuk meletakkan kode untuk semua ini di utama , cara yang lebih baik adalah dengan menggunakan
struktur untuk keuntungan dengan membuat setiap bagian fungsi yang terpisah. Tiga potong kecil lebih baik
untuk berurusan dengan satu yang besar, karena detail yang tidak relevan dapat dimakamkan dalam fungsi, dan
peluang interaksi yang tidak diinginkan diminimalkan. Dan potongan-potongan itu bahkan bisa berguna untuk yang lain
program.

Halaman 59

60
`` Sementara ada baris lain '' adalah getline , fungsi yang kami tulis di Bab 1 , dan `` cetak itu '' adalah
printf , yang telah disediakan seseorang untuk kita. Ini berarti kita hanya perlu menulis rutin
untuk memutuskan apakah garis mengandung kemunculan pola.

Kita dapat memecahkan masalah itu dengan menulis strindex fungsi (s, t) yang mengembalikan posisi atau
indeks dalam string s di mana string t dimulai, atau -1 jika s tidak mengandung t . Karena array C
mulai dari posisi nol, indeks akan nol atau positif, dan nilai negatif seperti -1 adalah
nyaman untuk kegagalan pensinyalan. Ketika nanti kita membutuhkan pencocokan pola yang lebih canggih, kita
hanya perlu mengganti strindex ; sisa kode dapat tetap sama. (Perpustakaan standar
menyediakan strstr fungsi yang mirip dengan strindex , kecuali bahwa ia mengembalikan pointer
indeks.)

Dengan desain sebanyak ini, mengisi rincian program sangatlah mudah. Ini dia
Semuanya, sehingga Anda bisa melihat bagaimana potongan-potongannya cocok. Untuk saat ini, pola yang akan dicari
karena adalah string literal, yang bukan mekanisme yang paling umum. Kami akan segera kembali ke
diskusi tentang bagaimana menginisialisasi array karakter, dan dalam Bab 5 akan menunjukkan cara membuat
pola suatu parameter yang diatur ketika program dijalankan. Ada juga yang sedikit berbeda
versi getline ; Anda mungkin mendapati bahwa membandingkannya dengan yang ada di Bab 1 adalah instruktif .

#termasuk <stdio.h>
#define MAXLINE 1000 / * panjang jalur input maksimum * /

int getline (baris char [], int maks)


int strindex (sumber char [], pencarian char untuk []);

pola char [] = "ould"; / * pola untuk mencari * /

/ * temukan semua pola garis yang cocok * /


utama()
{
baris char [MAXLINE];
int ditemukan = 0;

while (getline (line, MAXLINE)> 0)


if (strindex (line, pattern)> = 0) {
printf ("% s", line);
ditemukan ++;
}
kembali ditemukan;
}

/ * getline: dapatkan baris ke s, panjang kembali * /


int getline (char s [], int lim)
{
int c, saya;

i = 0;
while (--lim> 0 && (c = getchar ())! = EOF && c! = '\ n')
s [i ++] = c;
if (c == '\ n')
s [i ++] = c;
s [i] = '\ 0';
mengembalikan saya;
}

/ * strindex: indeks balik t dalam s, -1 jika tidak ada * /


int strindex (char s [], char t [])
{
int i, j, k;

untuk (i = 0; s [i]! = '\ 0'; i ++) {


untuk (j = i, k = 0; t [k]! = '\ 0' && s [j] == t [k]; j ++, k ++)

Halaman 60

61
;
if (k> 0 && t [k] == '\ 0')
mengembalikan saya;
}
return -1;
}
Setiap definisi fungsi memiliki formulir

return-type function-name ( deklarasi argumen )


{
deklarasi dan pernyataan
}
Berbagai bagian mungkin tidak ada; fungsi minimal adalah

dummy () {}
yang tidak melakukan apa pun dan tidak mengembalikan apa pun. Fungsi do-nothing seperti ini terkadang berguna sebagai
pemegang tempat selama pengembangan program. Jika tipe pengembalian dihilangkan, int diasumsikan.
Suatu program hanyalah sekumpulan definisi variabel dan fungsi. Komunikasi antara
fungsi adalah dengan argumen dan nilai yang dikembalikan oleh fungsi, dan melalui variabel eksternal.
Fungsi dapat terjadi dalam urutan apa pun dalam file sumber, dan program sumber dapat dipecah
menjadi beberapa file, selama tidak ada fungsi yang terpecah.

The kembali pernyataan adalah mekanisme untuk mengembalikan nilai dari fungsi dipanggil ke nya
penelepon. Ekspresi apa pun dapat mengikuti kembali :

ekspresi kembali ;
The ekspresi akan dikonversi ke jenis kembalinya fungsi jika perlu. Tanda kurung
sering digunakan di sekitar ekspresi , tetapi bersifat opsional.
Fungsi panggilan bebas untuk mengabaikan nilai yang dikembalikan. Lebih lanjut, tidak perlu ada
ekspresi setelah kembali ; dalam hal ini, tidak ada nilai yang dikembalikan ke pemanggil. Kontrol juga kembali ke
penelepon tanpa nilai saat eksekusi `` jatuh dari fungsi '' dengan mencapai
menutup brace kanan. Ini bukan ilegal, tetapi mungkin pertanda masalah, jika suatu fungsi mengembalikan nilai
dari satu tempat dan tidak ada nilai dari yang lain. Dalam kasus apa pun, jika suatu fungsi gagal mengembalikan nilai, itu adalah
`` Nilai '' sudah pasti menjadi sampah.

Program pencarian pola mengembalikan status dari utama , jumlah kecocokan yang ditemukan. Ini
nilai tersedia untuk digunakan oleh lingkungan yang disebut program

Mekanisme cara mengkompilasi dan memuat program C yang berada pada banyak file sumber
bervariasi dari satu sistem ke yang berikutnya. Pada sistem UNIX, misalnya, perintah cc
disebutkan dalam Bab 1 melakukan pekerjaan. Misalkan ketiga fungsi disimpan dalam tiga file
disebut main.c , getline.c , dan strindex.c . Lalu perintahnya

cc main.c getline.c strindex.c


mengkompilasi ketiga file, menempatkan kode objek yang dihasilkan dalam file main.o , getline.o , dan
strindex.o , lalu muat semuanya ke dalam file yang dapat dieksekusi yang disebut a.out . Jika ada kesalahan, katakan di
main.c , file dapat dikompilasi ulang dengan sendirinya dan hasilnya dimuat dengan file objek sebelumnya,
dengan perintah

cc main.c getline.o strindex.o


The cc perintah menggunakan `` c '' terhadap `` .o '' konvensi penamaan untuk membedakan file sumber
dari file objek.
Latihan 4-1. Tulis fungsi strindex (s, t) yang mengembalikan posisi paling kanan
terjadinya t dalam s , atau -1 jika tidak ada.

Halaman 61

62

4.2 Fungsi Pengembalian Non-integer


Sejauh ini contoh fungsi kami telah mengembalikan nilai tidak ada ( void ) atau int . Bagaimana jika a
fungsi harus mengembalikan beberapa tipe lainnya? banyak fungsi numerik seperti sqrt , sin , dan cos
kembali ganda ; fungsi khusus lainnya mengembalikan tipe lain. Untuk menggambarkan cara menangani
ini, mari kita menulis dan menggunakan fungsi atof (s) , yang mengubah string s menjadi dobel
setara dengan floating-point presisi. Atof jika ekstensi atoi , yang kami menunjukkan versi
dalam Bab 2 dan 3 . Ini menangani tanda opsional dan titik desimal, dan ada tidaknya
baik bagian atau bagian fraksional. Versi kami bukan rutin konversi input berkualitas tinggi; bahwa
akan membutuhkan lebih banyak ruang daripada yang ingin kita gunakan. Perpustakaan standar mencakup satu atap ; header
<stdlib.h> mendeklarasikannya.
Pertama, atof itu sendiri harus menyatakan jenis nilai yang dikembalikan, karena tidak int . Nama tipe
mendahului nama fungsi:

#termasuk <ctype.h>

/ * atof: convert string s menjadi double * /


atof ganda (char s [])
{
val ganda, kekuatan;
int i, tandatangani;

untuk (i = 0; ruang is (s [i]); i ++) / * lewati spasi putih * /


;
sign = (s [i] == '-')? -1: 1;
if (s [i] == '+' || s [i] == '-')
i ++;
untuk (val = 0,0; isdigit (s [i]); i ++)
val = 10.0 * val + (s [i] - '0');
if (s [i] == '.')
i ++;
untuk (power = 1.0; isdigit (s [i]); i ++) {
val = 10.0 * val + (s [i] - '0');
power * = 10;
}
tanda kembali * val / power;
}
Kedua, dan sama pentingnya, rutinitas panggilan harus tahu bahwa semuanya mengembalikan nilai non-int.
Salah satu cara untuk memastikan ini adalah untuk menyatakan atof secara eksplisit dalam rutinitas memanggil. Deklarasi tersebut adalah
ditunjukkan dalam kalkulator primitif ini (hampir tidak cukup untuk menyeimbangkan buku cek), yang berbunyi satu
nomor per baris, secara opsional didahului dengan tanda, dan menambahkannya, mencetak jumlah yang berjalan
setelah setiap input:

#termasuk <stdio.h>

#define MAXLINE 100

/ * kalkulator dasar * /
utama()
{
jumlah ganda,
baris char atof (char []);
[MAXLINE];
int getline (char line [], int max);

jumlah = 0;
while (getline (line, MAXLINE)> 0)
printf ("\ t% g \ n", jumlah + = atof (baris));
return 0;
}
Deklarasi

Halaman 62

63
jumlah ganda, atof (char []);
mengatakan bahwa jumlah adalah ganda variabel, dan yang atof adalah fungsi yang mengambil satu [char] argumen
dan mengembalikan ganda .
Fungsi atof harus dinyatakan dan didefinisikan secara konsisten. Jika ada sendiri dan panggilan untuk masuk
main memiliki tipe yang tidak konsisten dalam file sumber yang sama, kesalahan akan dideteksi oleh kompiler.
Tetapi jika (seperti yang lebih mungkin) atof disusun secara terpisah, ketidakcocokan tidak akan terdeteksi,
atof akan mengembalikan dua kali lipat yang utama akan diperlakukan sebagai int , dan jawaban yang tidak berarti akan
hasil.

Mengingat apa yang telah kami katakan tentang bagaimana deklarasi harus cocok dengan definisi, ini mungkin
tampak mengejutkan. Alasan ketidakcocokan bisa terjadi adalah bahwa jika tidak ada prototipe fungsi, a
fungsi secara implisit dinyatakan oleh penampilan pertama dalam ekspresi, seperti

jumlah + = jumlah (baris)


Jika nama yang belum dideklarasikan sebelumnya terjadi dalam ekspresi dan diikuti oleh a
kurung kiri, itu dinyatakan oleh konteks untuk menjadi nama fungsi, fungsi diasumsikan
mengembalikan int , dan tidak ada yang diasumsikan tentang argumennya. Selanjutnya jika suatu fungsi
deklarasi tidak termasuk argumen, seperti pada
dua kali lipat ();
itu juga berarti bahwa tidak ada yang dapat diasumsikan tentang argumen atof ; semua
pemeriksaan parameter dimatikan. Makna khusus dari daftar argumen kosong ini dimaksudkan
untuk mengizinkan program C lama untuk dikompilasi dengan kompiler baru. Tapi itu ide yang buruk untuk menggunakannya
program C baru. Jika fungsi mengambil argumen, nyatakan; jika tidak membutuhkan argumen, gunakan
batal .
Diberikan pada , dinyatakan dengan benar, kita dapat menulis atoi (mengonversi string ke int ) dalam hal:

/ * atoi: konversi string s menjadi integer menggunakan atof * /


int atoi (char s [])
{
atof ganda (char s []);

return (int) atof (s);


}
Perhatikan struktur deklarasi dan pernyataan pengembalian. Nilai dari ekspresi
di

ekspresi kembali ;
dikonversi ke jenis fungsi sebelum pengembalian diambil. Oleh karena itu, nilai atof ,
a ganda , diubah secara otomatis ke int ketika muncul dalam ini kembali , karena fungsi
atoi mengembalikan int . Namun, operasi ini secara potensial membuang informasi
kompiler memperingatkannya. Para pemeran menyatakan secara eksplisit bahwa operasi itu dimaksudkan, dan menekan
peringatan apa pun.
Latihan 4-2. Memperpanjang atof untuk menangani notasi ilmiah dalam bentuk

123.45e-6
di mana angka floating-point dapat diikuti oleh e atau E dan eksponen yang ditandatangani secara opsional.

4.3 Variabel Eksternal


Program AC terdiri dari satu set objek eksternal, yang merupakan variabel atau fungsi. Itu
kata sifat `` eksternal '' digunakan berbeda dengan `` internal '', yang menjelaskan argumen dan
variabel yang didefinisikan di dalam fungsi. Variabel eksternal didefinisikan di luar fungsi apa pun, dan
dengan demikian berpotensi tersedia untuk banyak fungsi. Fungsi itu sendiri selalu eksternal,
karena C tidak memungkinkan fungsi didefinisikan di dalam fungsi lain. Secara default, eksternal

Halaman 63
64
variabel dan fungsi memiliki properti yang dirujuk oleh semua dengan nama yang sama
dari fungsi yang dikompilasi secara terpisah, adalah referensi untuk hal yang sama. (Standar menyebut ini
linkage eksternal properti .) Dalam hal ini, variabel eksternal analog dengan Fortran
Blok UMUM atau variabel di blok terluar di Pascal. Kita akan lihat nanti bagaimana caranya
mendefinisikan variabel dan fungsi eksternal yang hanya terlihat dalam satu file sumber tunggal. Karena
variabel eksternal dapat diakses secara global, mereka memberikan alternatif untuk argumen fungsi dan
mengembalikan nilai untuk mengkomunikasikan data antar fungsi. Fungsi apa pun dapat mengakses eksternal
variabel dengan merujuknya dengan nama, jika nama telah dideklarasikan entah bagaimana.
Jika sejumlah besar variabel harus dibagi di antara berbagai fungsi, variabel eksternal lebih banyak
nyaman dan efisien daripada daftar argumen panjang. Namun, seperti yang ditunjukkan dalam Bab 1 , ini
penalaran harus diterapkan dengan hati-hati, karena dapat berdampak buruk pada program
struktur, dan mengarah ke program dengan terlalu banyak koneksi data antar fungsi.

Variabel eksternal juga berguna karena ruang lingkup dan masa pakai yang lebih besar. Otomatis
variabel internal untuk suatu fungsi; mereka muncul ketika fungsi dimasukkan, dan
menghilang ketika dibiarkan. Variabel eksternal, di sisi lain, adalah permanen, sehingga mereka bisa
mempertahankan nilai dari satu pemanggilan fungsi ke yang berikutnya. Jadi, jika dua fungsi harus berbagi beberapa
data, namun tidak ada panggilan yang lain, sering kali paling nyaman jika data bersama disimpan di eksternal
variabel daripada diteruskan keluar-masuk melalui argumen.

Mari kita periksa masalah ini dengan contoh yang lebih besar. Masalahnya adalah menulis program kalkulator
yang menyediakan operator + , - , * dan / . Karena lebih mudah diterapkan, kalkulator akan melakukannya
gunakan notasi terbalik Polandia bukan infiks. (Notasi Reverse Polandia digunakan oleh beberapa saku
kalkulator, dan dalam bahasa seperti Forth dan Postscript.)

Dalam notasi Polandia terbalik, setiap operator mengikuti operannya; ekspresi infix suka

(1 - 2) * (4 + 5)
dimasukkan sebagai

1 2 - 4 5 + *
Kurung tidak diperlukan; notasi tidak ambigu asalkan kita tahu berapa banyak
operan yang diharapkan setiap operator.
Implementasinya sederhana. Setiap operan didorong ke tumpukan; ketika seorang operator tiba,
jumlah operan yang tepat (dua untuk operator biner) muncul, operator diterapkan
mereka, dan hasilnya didorong kembali ke tumpukan. Dalam contoh di atas, misalnya, 1 dan 2
didorong, lalu digantikan oleh perbedaannya, -1. Selanjutnya, 4 dan 5 didorong dan kemudian diganti
dengan jumlah mereka, 9. Produk -1 dan 9, yaitu -9, menggantikannya di tumpukan. Nilai pada
bagian atas tumpukan muncul dan dicetak ketika ujung jalur input ditemui.

Struktur program dengan demikian merupakan loop yang melakukan operasi yang tepat pada masing-masing
operator dan operan seperti yang terlihat:

while ( operator atau operan berikutnya bukan indikator akhir file )


jika ( angka )
mendorongnya
lain jika ( operator )
operan pop
lakukan operasi
hasil mendorong
lain jika ( baris baru )
pop dan cetak bagian atas tumpukan
lain
kesalahan

Halaman 64

65
Operasi mendorong dan memunculkan tumpukan itu sepele, tetapi oleh deteksi kesalahan waktu dan
pemulihan ditambahkan, mereka cukup lama sehingga lebih baik untuk menempatkan masing-masing dalam fungsi yang terpisah
daripada mengulangi kode di seluruh program. Dan harus ada yang terpisah
berfungsi untuk mengambil operator input atau operan berikutnya.
Keputusan desain utama yang belum dibahas adalah di mana tumpukan berada, yaitu, di mana
rutin mengaksesnya secara langsung. Pada kemungkinan adalah untuk tetap di main , dan lulus tumpukan dan
posisi stack saat ini ke rutinitas yang mendorong dan pop itu. Tapi utama tidak perlu tahu
tentang variabel yang mengontrol tumpukan; hanya operasi push dan pop. Jadi kita punya
memutuskan untuk menyimpan tumpukan dan informasi terkait dalam variabel eksternal yang dapat diakses oleh
fungsi push dan pop tetapi tidak ke utama .
Menerjemahkan garis besar ini ke dalam kode cukup mudah. Jika untuk saat ini kami menganggap program sebagai
ada dalam satu file sumber, akan terlihat seperti ini:
#termasuk s
#define s
deklarasi fungsi untuk main

main () {...}

variabel eksternal untuk push dan pop

void push (double f) {...}


pop ganda (batal) {...}

int getop (char s []) {...}


rutinitas yang disebut oleh getop
Nanti kita akan membahas bagaimana ini dapat dipecah menjadi dua atau lebih file sumber.

Fungsi utama adalah loop yang berisi saklar besar pada jenis operator atau operan; ini adalah
penggunaan sakelar yang lebih tipikal daripada yang ditunjukkan pada Bagian 3.4 .

#termasuk <stdio.h>
#include <stdlib.h> / * untuk atof () * /

#define MAXOP 100 / * ukuran maksimum operan atau operator * /


# tentukan NUMBER sinyal '0' / * yang menemukan nomor * /

int getop (char []);


void push (dobel);
pop ganda (batal);

/ * membalikkan kalkulator Polandia * /


utama()
{
tipe int;
op2 ganda;
karakter [MAXOP];

while ((type = getop)! = EOF) {


beralih (jenis) {
nomor kasus:
push (atof (s));
istirahat;
huruf '+':
push (pop () + pop ());
istirahat;
kasus '*':
push (pop () * pop ());

Halaman 65

66
istirahat;
kasus '-':
op2 = pop ();
push (pop () - op2);
istirahat;
kasus '/':
op2 = pop ();
jika (op2! = 0.0)
push (pop () / op2);
lain
printf ("error: zero divisor \ n");
istirahat;
huruf '\ n':
printf ("\ t% .8g \ n", pop ());
istirahat;
default:
printf ("error: command% s \ n", s);
istirahat;
}
}
return 0;
}
Karena + dan * adalah operator komutatif, urutan di mana operan muncul adalah
gabungan tidak relevan, tetapi untuk - dan / operan kiri dan kanan harus dibedakan. Di
push (pop () - pop ()); / * SALAH * /
urutan di mana dua panggilan pop dievaluasi tidak ditentukan. Untuk menjamin hak
Agar, perlu untuk memasukkan nilai pertama ke dalam variabel sementara seperti yang kami lakukan di main .
#define MAXVAL 100 / * kedalaman maksimum val stack * /

int sp = 0; / * Posisi tumpukan gratis berikutnya * /


double val [MAXVAL]; / * tumpukan nilai * /
/ * push: push f ke stack nilai * /
void push (double f)
{
if (sp <MAXVAL)
val [sp ++] = f;
lain
printf ("error: stack full, tidak bisa mendorong% g \ n", f);
}

/ * pop: pop dan kembalikan nilai teratas dari stack * /


pop ganda (batal)
{
jika (sp> 0)
return val [- sp];
lain {
printf ("error: stack empty \ n");
return 0,0;
}
}
Variabel eksternal jika didefinisikan di luar fungsi apa pun. Dengan demikian stack and stack index
yang harus dibagi dengan push dan pop didefinisikan di luar fungsi-fungsi ini. Tapi utama itu sendiri
tidak merujuk ke posisi tumpukan atau tumpukan - representasi dapat disembunyikan.
Mari kita beralih ke implementasi getop , fungsi yang mengambil operator berikutnya atau
operan. Tugasnya mudah. Lewati kosong dan tab. Jika karakter selanjutnya bukan digit atau a
titik heksadesimal, kembalikan. Jika tidak, kumpulkan string angka (yang mungkin termasuk a
titik desimal), dan mengembalikan NUMBER , sinyal bahwa suatu angka telah dikumpulkan.

#termasuk <ctype.h>

Halaman 66

67
int getch (void);
membatalkan ungetch (int);

/ * getop: dapatkan operan karakter atau numerik selanjutnya * /


int getop (char s [])
{
int i, c;

while ((s [0] = c = getch ()) == '' || c == '\ t')


;
s [1] = '\ 0';
if (! isdigit (c) && c! = '.')
kembali c; / * bukan angka * /
i = 0;
if (isdigit (c)) / * kumpulkan bagian integer * /
while (isdigit (s [++ i] = c = getch ()))
;
if (c == '.') / * kumpulkan bagian pecahan * /
while (isdigit (s [++ i] = c = getch ()))
;
s [i] = '\ 0';
if (c! = EOF)
ungetch (c);
kembalikan NUMBER;
}
Apa itu getch and ungetch ? Sering kali suatu program tidak dapat menentukan apa yang dimilikinya
baca input yang cukup sampai terlalu banyak membaca. Salah satu contohnya adalah mengumpulkan karakter yang dibuat
sebuah nomor: sampai non-digit pertama terlihat, jumlahnya tidak lengkap. Tapi kemudian programnya
telah membaca satu karakter terlalu jauh, karakter yang tidak siap untuknya.
Masalahnya akan dipecahkan jika memungkinkan untuk `` membatalkan-baca '' karakter yang tidak diinginkan. Kemudian,
setiap kali program membaca satu karakter terlalu banyak, itu bisa mendorongnya kembali pada input, jadi
sisa kode bisa berperilaku seolah-olah itu belum pernah dibaca. Untungnya, mudah untuk mensimulasikan
mendapatkan karakter, dengan menulis sepasang fungsi bekerja sama. getch mengirimkan input selanjutnya
karakter yang harus dipertimbangkan; ungetch akan mengembalikannya sebelum membaca input baru.

Cara mereka bekerja bersama itu sederhana. ungetch menempatkan karakter yang didorong ke belakang menjadi yang dibagikan
buffer - array karakter. getch membaca dari buffer jika ada hal lain, dan menelepon
getchar jika buffer kosong. Harus juga ada variabel indeks yang mencatat posisi
dari karakter saat ini di buffer.

Karena buffer dan indeks dibagikan oleh getch dan ungetch dan harus mempertahankan nilainya
di antara panggilan, keduanya harus eksternal untuk kedua rutinitas. Jadi kita bisa menulis getch , ungetch , dan
variabel bersama mereka sebagai:

#define BUFSIZE 100

char buf [BUFSIZE]; / * buffer untuk ungetch * /


int bufp = 0; / * posisi bebas berikutnya di buf * /
int getch (void) / * dapatkan karakter (kemungkinan didorong kembali) * /
{
kembali (bufp> 0)? buf [- bufp]: getchar ();
}

membatalkan ungetch (int c) / * mendorong kembali karakter pada input * /


{
if (bufp> = BUFSIZE)
printf ("ungetch: terlalu banyak karakter \ n");
lain
buf [bufp ++] = c;
}

Halaman 67

68
Perpustakaan standar mencakup fungsi ungetch yang menyediakan satu karakter pushback; kita
akan membahasnya di Bab 7 . Kami telah menggunakan array untuk pushback, bukan satu
karakter, untuk menggambarkan pendekatan yang lebih umum.
Latihan 4-3. Dengan kerangka dasar, mudah untuk memperluas kalkulator. Tambahkan
modulus (%) operator dan ketentuan untuk angka negatif.

Latihan 4-4. Tambahkan perintah untuk mencetak elemen atas tumpukan tanpa muncul, ke
duplikatnya, dan untuk menukar dua elemen teratas. Tambahkan perintah untuk menghapus tumpukan.

Latihan 4-5. Tambahkan akses ke fungsi perpustakaan seperti sin , exp , dan pow . Lihat <math.h> di
Lampiran B, Bagian 4 .

Latihan 4-6. Tambahkan perintah untuk menangani variabel. (Sangat mudah untuk menyediakan dua puluh enam variabel
dengan nama satu huruf.) Tambahkan variabel untuk nilai yang dicetak terakhir.

Latihan 4-7. Tuliskan ungets rutin yang akan mendorong seluruh string ke input.
Haruskah unget tahu tentang buf dan bufp , atau haruskah hanya menggunakan ungetch ?

Latihan 4-8. Misalkan tidak akan ada lebih dari satu karakter pushback. Memodifikasi
getch dan ungetch sesuai.

Latihan 4-9. Kami getch dan ungetch tidak menangani mendorong kembali EOF dengan benar. Memutuskan
apa sifat-sifatnya jika EOF didorong kembali, kemudian terapkan desain Anda.

Latihan 4-10. Organisasi alternatif menggunakan getline untuk membaca seluruh jalur input; ini membuat
getch dan ungetch tidak perlu. Merevisi kalkulator untuk menggunakan pendekatan ini.

4.4 Aturan Lingkup


Fungsi dan variabel eksternal yang membentuk program C tidak perlu dikompilasi di
waktu yang sama; teks sumber program dapat disimpan dalam beberapa file, dan sebelumnya dikompilasi
rutinitas dapat diambil dari perpustakaan. Di antara pertanyaan yang menarik adalah
â € ¢ Bagaimana deklarasi ditulis sehingga variabel dideklarasikan dengan benar selama
kompilasi?
â € ¢ Bagaimanadeklarasi diatur sehingga semua bagian akan terhubung dengan benar saat
program dimuat?
â € ¢ Bagaimana deklarasi diatur sehingga hanya ada satu salinan?
â € ¢ Bagaimana variabel eksternal diinisialisasi?
Mari kita bahas topik ini dengan mengatur ulang program kalkulator menjadi beberapa file. Sebagai
masalah praktis, kalkulator terlalu kecil untuk dibelah, tetapi ini adalah ilustrasi yang bagus
masalah yang muncul dalam program yang lebih besar.
The lingkup dari nama adalah bagian dari program di mana nama tersebut dapat digunakan. Untuk sebuah
variabel otomatis dideklarasikan di awal fungsi, ruang lingkup adalah fungsi di mana
namanya dideklarasikan. Variabel lokal dengan nama yang sama dalam fungsi yang berbeda tidak terkait. Itu
Hal yang sama berlaku untuk parameter fungsi, yang berlaku variabel lokal.

Cakupan variabel eksternal atau fungsi berlangsung dari titik di mana ia dinyatakan
akhir file yang sedang dikompilasi. Misalnya, jika main , sp , val , push , dan pop didefinisikan dalam
satu file, dalam urutan yang ditunjukkan di atas, yaitu,

main () {...}
Halaman 68

69
int sp = 0;
double val [MAXVAL];

void push (double f) {...}

pop ganda (batal) {...}


maka variabel sp dan val dapat digunakan dalam push dan pop hanya dengan memberi nama mereka; tidak lebih jauh
deklarasi dibutuhkan. Tetapi nama-nama ini tidak terlihat di utama , juga tidak ada push dan pop
diri.
Di sisi lain, jika variabel eksternal harus dirujuk sebelum didefinisikan, atau jika itu
didefinisikan dalam file sumber yang berbeda dari yang digunakan, kemudian eksternal
deklarasi adalah wajib.

Penting untuk membedakan antara deklarasi variabel eksternal dan definisinya .


Deklarasi mengumumkan properti variabel (terutama tipenya); definisi juga
menyebabkan penyimpanan harus disisihkan. Jika garis

int sp;
double val [MAXVAL];
muncul di luar fungsi apa pun, mereka menentukan variabel eksternal sp dan val , menyebabkan penyimpanan
disisihkan, dan juga berfungsi sebagai deklarasi untuk sisa file sumber itu. Di sisi lain
tangan, garis

extern int sp;


extern double val [];
mendeklarasikan untuk sisa dari file sumber yang sp adalah int dan bahwa val adalah array ganda (yang
ukuran ditentukan di tempat lain), tetapi mereka tidak membuat variabel atau menyimpan cadangan untuk mereka.
Harus ada hanya satu definisi variabel eksternal di antara semua file yang membentuk
program sumber; file lain mungkin berisi deklarasi eksternal untuk mengaksesnya. (Mungkin juga ada
deklarasi eksternal dalam file yang berisi definisi.) Ukuran array harus ditentukan dengan
definisi, tetapi opsional dengan deklarasi eksternal .

Inisialisasi variabel eksternal hanya berjalan dengan definisi.

Meskipun ini bukan organisasi yang mungkin untuk program ini, fungsi push dan pop bisa saja
didefinisikan dalam satu file, dan variabel val dan sp didefinisikan dan diinisialisasi dalam file lain. Lalu ini
definisi dan deklarasi akan diperlukan untuk mengikat mereka:

dalam file1 :

extern int sp;


extern double val [];

void push (double f) {...}

pop ganda (batal) {...}


dalam file2 :
int sp = 0;
double val [MAXVAL];
Karena deklarasi eksternal dalam file1 terletak di depan dan di luar definisi fungsi, mereka
berlaku untuk semua fungsi; satu set deklarasi sudah cukup untuk semua file1 . Organisasi yang sama ini
juga akan dibutuhkan jika definisi sp dan val mengikuti penggunaannya dalam satu file.

4.5 File Header


Biarkan sekarang mempertimbangkan untuk membagi program kalkulator ke dalam beberapa file sumber, sebagaimana adanya
masing-masing komponen jauh lebih besar. Fungsi utama akan masuk dalam satu file,

Halaman 69

70
yang akan kita sebut main.c ; push , pop , dan variabel-variabelnya masuk ke file kedua, stack.c ;
Getop masuk ke yang ketiga, getop.c . Akhirnya, getch dan ungetch masuk ke file keempat, getch.c ;
kami memisahkan mereka dari yang lain karena mereka akan datang dari perpustakaan yang dikompilasi secara terpisah
dalam program yang realistis.
Ada satu hal lagi yang perlu dikhawatirkan - definisi dan deklarasi dibagikan di antara file.
Sebisa mungkin, kami ingin memusatkan ini, sehingga hanya ada satu salinan untuk mendapatkan dan menyimpan
tepat ketika program berkembang. Karenanya, kami akan menempatkan materi umum ini dalam file header ,
calc.h , yang akan dimasukkan seperlunya. (Baris #sertakan diuraikan dalam Bagian 4.11 .)
Program yang dihasilkan kemudian terlihat seperti ini:
Ada tradeoff antara keinginan bahwa setiap file hanya memiliki akses ke informasi itu
kebutuhan untuk pekerjaannya dan kenyataan praktis bahwa lebih sulit untuk mempertahankan lebih banyak file header. Hingga
beberapa ukuran program moderat, mungkin yang terbaik untuk memiliki satu file header yang berisi
segala sesuatu yang akan dibagikan di antara dua bagian program; itu adalah keputusan kami
dibuat di sini. Untuk program yang jauh lebih besar, lebih banyak organisasi dan lebih banyak header akan dibutuhkan.

4.6 Variabel Statis

Halaman 70

71
Variabel sp dan val di stack.c , dan buf dan bufp di getch.c , adalah untuk penggunaan pribadi
fungsi dalam file sumber masing-masing, dan tidak dimaksudkan untuk diakses oleh hal lain.
The statis deklarasi, diterapkan ke variabel eksternal atau fungsi, membatasi ruang lingkup yang
objek ke sisa file sumber yang sedang dikompilasi. Statis eksternal dengan demikian menyediakan cara untuk
sembunyikan nama seperti buf dan bufp dalam kombinasi getch-ungetch , yang pastinya eksternal
mereka dapat dibagikan, namun yang seharusnya tidak terlihat oleh pengguna getch dan ungetch .
Penyimpanan statis ditentukan dengan mengawali deklarasi normal dengan kata statis . Jika keduanya
rutin dan dua variabel dikompilasi dalam satu file, seperti pada

static char buf [BUFSIZE]; / * buffer untuk ungetch * /


static int bufp = 0; / * posisi bebas berikutnya di buf * /

int getch (void) {...}

membatalkan ungetch (int c) {...}


maka tidak ada rutin lain akan dapat mengakses buf dan bufp , dan nama-nama itu tidak akan bertentangan
dengan nama yang sama di file lain dari program yang sama. Dengan cara yang sama, variabel itu
penggunaan push dan pop untuk manipulasi stack dapat disembunyikan, dengan menyatakan sp dan val sebagai statis .
Deklarasi statis eksternal paling sering digunakan untuk variabel, tetapi dapat diterapkan
berfungsi juga. Biasanya, nama fungsi bersifat global, dapat dilihat oleh bagian mana pun dari keseluruhan
program. Jika suatu fungsi dinyatakan statis , namanya tidak terlihat di luar file di
yang dinyatakan.

The static deklarasi juga dapat diterapkan untuk variabel internal. Variabel statis internal adalah
lokal ke fungsi tertentu sama seperti variabel otomatis, tetapi tidak seperti otomatis, mereka tetap
ada daripada datang dan pergi setiap kali fungsi diaktifkan. Ini artinya
variabel statis internal menyediakan penyimpanan permanen pribadi dalam satu fungsi.
Latihan 4-11. Ubah getop sehingga tidak perlu menggunakan ungetch . Petunjuk: gunakan internal
variabel statis .

4.7. Mendaftar Variabel


Sebuah daftar deklarasi menyarankan compiler bahwa variabel tersebut akan banyak digunakan.
Idenya adalah bahwa variabel register harus ditempatkan di register mesin, yang dapat mengakibatkan
program yang lebih kecil dan lebih cepat. Tetapi kompiler bebas untuk mengabaikan saran.
The mendaftar deklarasi terlihat seperti

daftar int x;
daftar char c;
dan seterusnya. The mendaftar deklarasi hanya dapat diterapkan pada variabel otomatis dan ke
parameter formal suatu fungsi. Dalam kasus ini nanti, sepertinya

f (daftar tanpa tanda tangan m, daftar panjang n)


{
daftar int i;
...
}
Dalam praktiknya, ada pembatasan pada variabel register, yang mencerminkan realitas yang mendasarinya
perangkat keras. Hanya beberapa variabel di setiap fungsi yang dapat disimpan dalam register, dan hanya tipe tertentu
diijinkan. Namun, deklarasi register berlebih tidak berbahaya, karena kata register adalah
diabaikan karena deklarasi berlebihan atau tidak diizinkan. Dan tidak mungkin untuk mengambil alamat a
register variable (topik yang dibahas pada Bab 5 ), terlepas dari apakah variabel itu sebenarnya
ditempatkan dalam register. Pembatasan spesifik pada jumlah dan jenis variabel register bervariasi
dari mesin ke mesin.

Halaman 71

72

4.8 Struktur Blok


C bukan bahasa terstruktur-blok dalam arti Pascal atau bahasa sejenis, karena
fungsi mungkin tidak didefinisikan dalam fungsi lain. Di sisi lain, variabel bisa
didefinisikan dalam mode blok-terstruktur dalam suatu fungsi. Deklarasi variabel (termasuk
inisialisasi) dapat mengikuti brace kiri yang memperkenalkan setiap pernyataan majemuk, bukan hanya
salah satu yang memulai fungsi. Variabel yang dideklarasikan dengan cara ini menyembunyikan variabel yang diidentifikasi secara identik
di blok luar, dan tetap ada sampai penjepit kanan yang cocok. Misalnya, dalam

if (n> 0) {
int i; / * mendeklarasikan i baru * /

untuk (i = 0; i <n; i ++)


...
}
ruang lingkup variabel i adalah cabang `` true '' dari if ; ini saya tidak ada hubungannya dengan saya di luar
blok. Variabel otomatis yang dideklarasikan dan diinisialisasi dalam sebuah blok diinisialisasi setiap kali
blok dimasukkan.
Variabel otomatis, termasuk parameter formal, juga menyembunyikan variabel eksternal dan fungsi
nama yang sama. Diberikan deklarasi

int x;
int y;

f (double x)
{
gandakan y;
}
kemudian dalam fungsi f , kejadian x merujuk pada parameter, yang merupakan ganda ; di luar
f , mereka merujuk ke int eksternal . Hal yang sama berlaku untuk variabel y .
Sebagai soal gaya, yang terbaik adalah menghindari nama variabel yang menyembunyikan nama dalam lingkup luar; itu
potensi kebingungan dan kesalahan terlalu besar.

4.9 Inisialisasi
Inisialisasi telah disebutkan berkali-kali sejauh ini, tetapi selalu dilakukan secara terpisah untuk beberapa orang
topik lainnya. Bagian ini merangkum beberapa aturan, sekarang kita telah membahas
berbagai kelas penyimpanan.
Dengan tidak adanya inisialisasi eksplisit, variabel eksternal dan statis dijamin
diinisialisasi ke nol; variabel otomatis dan register memiliki nilai awal yang tidak ditentukan (yaitu, sampah).

Variabel skalar dapat diinisialisasi ketika mereka didefinisikan, dengan mengikuti nama dengan equals
tanda tangan dan ekspresi:
int x = 1;
char squota = '\' ';
hari yang panjang = 1000L * 60L * 60L * 24L; / * milidetik / hari * /
Untuk variabel eksternal dan statis, initializer harus berupa ekspresi konstan; inisialisasi
dilakukan sekali, secara konseptual sebelum program dimulai eksekusi. Untuk otomatis dan daftar
variabel, penginisialisasi tidak terbatas menjadi konstanta: itu bisa berupa ekspresi apa pun
nilai yang ditentukan sebelumnya, bahkan panggilan fungsi. Misalnya, inisialisasi biner
program pencarian di Bagian 3.3 dapat ditulis sebagai

int binsearch (int x, int v [], int n)


{
int low = 0;
int high = n - 1;

Halaman 72

73
pertengahan pertengahan;
...
}
dari pada
int low, high, mid;

rendah = 0;
tinggi = n - 1;
Akibatnya, inisialisasi variabel otomatis hanya singkatan untuk pernyataan penugasan.
Bentuk yang disukai sebagian besar adalah masalah selera. Kami biasanya menggunakan penugasan eksplisit,
karena inisialisasi dalam deklarasi lebih sulit dilihat dan jauh dari titik penggunaan.
Array dapat diinisialisasi dengan mengikuti deklarasi dengan daftar inisialisasi yang terlampir
kurung kurawal dan dipisahkan dengan koma. Misalnya, untuk menginisialisasi hari array dengan jumlah
hari di setiap bulan:

int days [] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
Ketika ukuran array dihilangkan, kompiler akan menghitung panjangnya dengan menghitung
inisialisasi, yang ada 12 dalam hal ini.
Jika ada lebih sedikit inisialisasi untuk array daripada ukuran yang ditentukan, yang lain akan menjadi nol untuk
variabel eksternal, statis dan otomatis. Merupakan kesalahan memiliki terlalu banyak inisialisasi. Tidak ada
cara untuk menentukan pengulangan penginisialisasi, atau untuk menginisialisasi elemen di tengah array
tanpa memasok semua nilai sebelumnya juga.

Array karakter adalah kasus khusus inisialisasi; string dapat digunakan sebagai pengganti kawat gigi
dan notasi koma:

pola char = "ould";


adalah singkatan untuk yang lebih lama tapi setara
pola char [] = {'o', 'u', 'l', 'd', '\ 0'};
Dalam hal ini, ukuran array adalah lima (empat karakter plus terminating '\ 0' ).

4.10 Rekursi
Fungsi C dapat digunakan secara rekursif; yaitu suatu fungsi dapat memanggil dirinya sendiri secara langsung atau
secara tidak langsung. Pertimbangkan mencetak nomor sebagai string karakter. Seperti yang kami sebutkan sebelumnya, digit
dihasilkan dalam urutan yang salah: digit orde rendah tersedia sebelum digit orde tinggi, tetapi
mereka harus dicetak sebaliknya.
Ada dua solusi untuk masalah ini. Aktif adalah untuk menyimpan angka dalam array seperti apa adanya
dihasilkan, lalu cetak dengan urutan terbalik, seperti yang kami lakukan dengan itoa di bagian 3.6 . Itu
alternatif adalah solusi rekursif, di mana printd pertama kali menyebut dirinya untuk mengatasi setiap pemimpin
digit, lalu cetak digit trailing. Sekali lagi, versi ini dapat gagal pada angka negatif terbesar.

#termasuk <stdio.h>

/ * printd: cetak n dalam desimal * /


void printd (int n)
{
if (n <0) {
putchar ('-');
n = -n;
}
jika (n / 10)
printd (n / 10);
putchar (n% 10 + '0');
}
Halaman 73

74
Ketika suatu fungsi menyebut dirinya secara rekursif, setiap doa mendapat set baru semua otomatis
variabel, independen dari set sebelumnya. Ini di printd (123) printd pertama menerima
argumen n = 123 . Ini melewati 12 ke printd kedua , yang pada gilirannya melewati 1 ke sepertiga. Itu
printd level ketiga mencetak 1 , kemudian kembali ke level kedua. Itu printd cetakan 2 , kembali maka
ke tingkat pertama. Yang satu mencetak 3 dan berakhir.
Contoh rekursi yang bagus lainnya adalah quicksort, algoritma penyortiran yang dikembangkan oleh CAR
Hoare pada tahun 1962. Diberikan array, satu elemen dipilih dan yang lain dipartisi dalam dua himpunan bagian
- Yang kurang dari elemen partisi dan yang lebih besar atau sama dengan itu. Proses yang sama juga
kemudian diterapkan secara rekursif ke dua himpunan bagian. Ketika subset memiliki kurang dari dua elemen, itu
tidak perlu disortir; ini menghentikan rekursi.

Versi quicksort kami bukan yang tercepat, tetapi ini salah satu yang paling sederhana. Kami menggunakan
elemen tengah setiap subarray untuk dipartisi.

/ * qsort: urutkan v [kiri] ... v [kanan] ke urutan yang meningkat * /


membatalkan qsort (int v [], int kiri, int kanan)
{
int i, last;
membatalkan swap (int v [], int i, int j);

if (kiri> = kanan) / * tidak melakukan apa-apa jika array mengandung * /


kembali; / * kurang dari dua elemen * /
swap (v, kiri, (kiri + kanan) / 2); / * pindahkan partisi elem * /
terakhir = kiri; / * ke v [0] * /
untuk (i = kiri + 1; i <= kanan; i ++) / * partisi * /
jika (v [i] <v [kiri])
swap (v, ++ terakhir, i);
swap (v, kiri, terakhir); / * mengembalikan partisi elem * /
qsort (v, kiri, terakhir-1);
qsort (v, +1 terakhir, kanan);
}
Kami memindahkan operasi swapping ke swap fungsi terpisah karena ini terjadi tiga kali
di qsort .

/ * swap: interchange v [i] dan v [j] * /


membatalkan swap (int v [], int i, int j)
{
int temp;

temp = v [i];
v [i] = v [j];
v [j] = temp;
}
Perpustakaan standar mencakup versi qsort yang dapat mengurutkan objek dari jenis apa pun.
Rekursi mungkin tidak memberikan penghematan dalam penyimpanan, karena di suatu tempat ada tumpukan nilai-nilai
diproses harus dipertahankan. Juga tidak akan lebih cepat. Tetapi kode rekursif lebih kompak, dan
seringkali jauh lebih mudah untuk ditulis dan dipahami daripada padanan non-rekursif. Rekursi adalah
terutama nyaman untuk struktur data yang didefinisikan secara rekursif seperti pohon, kita akan melihat yang bagus
contoh di Bagian 6.6 .

Latihan 4-12. Adaptasikan ide-ide printd untuk menulis itoa versi rekursif ; yaitu, insaf
integer ke dalam string dengan memanggil rutin rekursif.

Latihan 4-13. Tulis versi rekursif dari fungsi yang terbalik , yang membalikkan
String s di tempat.

4.11 Preprosesor C

Halaman 74

75
C menyediakan fasilitas bahasa tertentu melalui preprosesor, yang secara konseptual a
pisahkan langkah pertama dalam kompilasi. Dua fitur yang paling sering digunakan adalah #include , to
termasuk isi file selama kompilasi, dan #define , untuk mengganti token dengan
urutan karakter yang sewenang-wenang. Fitur lain yang dijelaskan dalam bagian ini termasuk bersyarat
kompilasi dan makro dengan argumen.
4.11.1 Inklusi File
Penyertaan file memudahkan menangani koleksi #define s dan deklarasi (antara lain
sesuatu). Setiap baris sumber formulir
#sertakan " namafile "
atau

#termasuk < nama file >


diganti dengan isi nama file file . Jika nama file dikutip, cari file
biasanya dimulai ketika program sumber ditemukan; jika tidak ditemukan di sana, atau jika namanya
terlampir dalam <dan>, pencarian mengikuti aturan yang ditentukan implementasi untuk menemukan file. Sebuah
file yang disertakan mungkin berisi #include lines.
Sering ada beberapa baris #include pada awal file sumber, untuk menyertakan umum
Pernyataan #define dan deklarasi eksternal , atau untuk mengakses deklarasi prototipe fungsi
untuk fungsi perpustakaan dari header seperti <stdio.h> . (Sebenarnya, ini bukan file;
rincian cara header diakses tergantung pada implementasi.)

#include adalah cara yang disukai untuk mengikat deklarasi bersama untuk program besar. Itu
menjamin bahwa semua file sumber akan diberikan dengan definisi dan variabel yang sama
deklarasi, dan karenanya menghilangkan jenis bug yang sangat jahat. Wajar bila dimasukkan
file diubah, semua file yang bergantung padanya harus dikompilasi ulang.

4.11.2 Substitusi Makro


Definisi memiliki bentuk

# tentukan teks pengganti nama


Ia membutuhkan substitusi makro dari jenis yang paling sederhana - kemunculan token berikutnya
nama akan diganti oleh teks pengganti . Nama dalam #define memiliki bentuk yang sama dengan a
nama variabel; teks pengganti sewenang-wenang. Biasanya teks pengganti adalah sisa dari
baris, tetapi definisi panjang dapat dilanjutkan ke beberapa baris dengan menempatkan \ pada akhir masing-masing
line untuk dilanjutkan. Lingkup nama yang didefinisikan dengan #define adalah dari sudut definisi
ke akhir file sumber yang sedang dikompilasi. Definisi dapat menggunakan definisi sebelumnya.
Substitusi dibuat hanya untuk token, dan tidak terjadi dalam string yang dikutip. Untuk
contoh, jika YA adalah nama yang ditentukan, tidak akan ada substitusi dalam printf ("YA") atau dalam
YESMAN .
Nama apa pun dapat didefinisikan dengan teks pengganti apa pun. Sebagai contoh

#define selamanya untuk (;;) / * infinite loop * /


mendefinisikan kata baru, selamanya , untuk loop tanpa batas.
Dimungkinkan juga untuk mendefinisikan makro dengan argumen, sehingga teks pengganti bisa berbeda untuk
panggilan makro yang berbeda. Sebagai contoh, tentukan makro yang disebut max :

#define maks (A, B) ((A)> (B)? (A): (B))


Meskipun terlihat seperti pemanggilan fungsi, penggunaan max diperluas menjadi kode in-line. Setiap kejadian
dari parameter formal (di sini A atau B ) akan diganti oleh argumen aktual yang sesuai.
Demikian garisnya

Halaman 75

76
x = maks (p + q, r + s);
akan diganti oleh garis

x = ((p + q)> (r + s)? (p + q): (r + s));


Selama argumen diperlakukan secara konsisten, makro ini akan berfungsi untuk semua tipe data; sana
tidak perlu untuk berbagai jenis maks untuk berbagai jenis data, karena akan ada dengan fungsi.
Jika Anda memeriksa perluasan max , Anda akan melihat beberapa jebakan. Ungkapannya adalah
dievaluasi dua kali; ini buruk jika mereka melibatkan efek samping seperti operator increment atau input dan
keluaran. Contohnya

maks (i ++, j ++) / * SALAH * /


akan bertambah besar dua kali lipat. Beberapa perawatan juga harus dilakukan dengan tanda kurung untuk memastikan
urutan evaluasi dipertahankan; pertimbangkan apa yang terjadi ketika makro

#define square (x) x * x / * SALAH * /


dipanggil sebagai kuadrat (z +1) .
Meskipun demikian, makro sangat berharga. Salah satu contoh praktis berasal dari <stdio.h> , di mana
getchar dan putchar sering didefinisikan sebagai makro untuk menghindari overhead run-time dari a
panggilan fungsi per karakter diproses. Fungsi dalam <ctype.h> juga biasanya
diimplementasikan sebagai makro.

Nama mungkin tidak terdefinisi dengan #undef , biasanya untuk memastikan bahwa rutin benar-benar sebuah fungsi, bukan
makro:

#undef getchar
int getchar (void) {...}
Parameter formal tidak diganti dalam string yang dikutip. Namun, jika nama parameternya adalah
didahului oleh # dalam teks pengganti, kombinasi akan diperluas menjadi string yang dikutip
dengan parameter digantikan oleh argumen yang sebenarnya. Ini dapat dikombinasikan dengan string
gabungan untuk membuat, misalnya, makro cetak debugging:

#define dprint (expr) printf (#expr "=% g \ n", expr)


Saat ini dipanggil, seperti pada

cetak (x / y)
makro diperluas ke

printf ("x / y" "= & g \ n", x / y);


dan senar digabungkan, sehingga efeknya

printf ("x / y = & g \ n", x / y);


Dalam argumen aktual, masing-masing " digantikan oleh \" dan masing-masing \ oleh \\ , sehingga hasilnya adalah legal
string konstan.
Operator preprocessor ## menyediakan cara untuk menggabungkan argumen aktual selama makro
ekspansi. Jika parameter dalam teks pengganti berdekatan dengan ## , parameter diganti
oleh argumen aktual, ## dan ruang putih di sekitarnya dihapus, dan hasilnya adalah
dipindai. Misalnya, tempel makro menyatukan dua argumennya:

#define paste (depan, belakang) depan ## belakang


jadi tempel (nama, 1) membuat token name1 .
Aturan untuk penggunaan ## yang bersarang adalah rahasia; keterangan lebih lanjut dapat ditemukan dalam Lampiran A .

Latihan 4-14. Tentukan makro swap (t, x, y) yang bertukar dua argumen tipe t .
(Struktur blok akan membantu.)

Halaman 76

77

4.11.3 Inklusi Bersyarat


Dimungkinkan untuk mengontrol preprocessing sendiri dengan pernyataan kondisional yang dievaluasi
selama preprocessing. Ini menyediakan cara untuk memasukkan kode secara selektif, tergantung pada nilainya
kondisi dievaluasi selama kompilasi.
Baris #jika mengevaluasi ekspresi integer konstan (yang mungkin tidak termasuk sizeof , gips, atau
konstanta enum ). Jika ekspresi itu bukan nol, baris berikutnya akan menjadi # endif atau #elif atau
#selain disertakan. (Pernyataan preprocessor #elif seperti else-if .) Ekspresi
didefinisikan ( nama ) dalam # jika adalah 1 jika nama telah ditentukan, dan 0 sebaliknya.

Misalnya, untuk memastikan bahwa isi file hdr.h hanya disertakan satu kali, file
isi file dikelilingi dengan syarat seperti ini:

#jika ditentukan! (HDR)


#definisikan HDR

/ * Isi hdr.h buka di sini * /

#berakhir jika
Dimasukkannya pertama hdr.h mendefinisikan nama HDR ; inklusi berikutnya akan menemukan nama
didefinisikan dan lewati ke # endif . Gaya serupa dapat digunakan untuk menghindari memasukkan file
beberapa kali. Jika gaya ini digunakan secara konsisten, maka setiap tajuk itu sendiri dapat menyertakan yang lain
header tempat bergantung, tanpa pengguna header harus berurusan dengan
saling ketergantungan.
Urutan ini menguji nama SISTEM untuk memutuskan versi header mana yang akan disertakan:

#jika SISTEM == SYSV


#define HDR "sysv.h"
#elif SYSTEM == BSD
#define HDR "bsd.h"
#elif SYSTEM == MSDOS
#define HDR "msdos.h"
#lain
#define HDR "default.h"
#berakhir jika
#termasuk HDR
Baris #ifdef dan #ifndef adalah formulir khusus yang menguji apakah suatu nama didefinisikan. Itu
Contoh pertama #jika di atas bisa ditulis

#jika HDR
#definisikan HDR

/ * Isi hdr.h buka di sini * /


#berakhir jika

Halaman 77

78

Bab 5 - Pointer dan Array


Pointer adalah variabel yang berisi alamat variabel. Pointer banyak digunakan dalam C,
sebagian karena mereka kadang-kadang satu-satunya cara untuk mengekspresikan perhitungan, dan sebagian karena
mereka biasanya menghasilkan kode yang lebih ringkas dan efisien daripada yang bisa diperoleh dengan cara lain.
Pointer dan array terkait erat; bab ini juga mengeksplorasi hubungan dan pertunjukan ini
bagaimana cara memanfaatkannya.
Pointer telah digabungkan dengan pernyataan kebagian sebagai cara yang luar biasa untuk membuat mustahil-
untuk memahami program. Ini tentu benar ketika mereka digunakan secara sembarangan, dan itu mudah dilakukan
buat pointer yang menunjuk suatu tempat yang tidak terduga. Namun, dengan disiplin, petunjuk juga bisa
digunakan untuk mencapai kejelasan dan kesederhanaan. Ini adalah aspek yang akan kami coba ilustrasikan.

Perubahan utama dalam ANSI C adalah membuat aturan eksplisit tentang bagaimana pointer bisa
dimanipulasi, pada dasarnya mengamanatkan apa yang sudah diprogram oleh programmer yang baik dan kompiler yang baik
sudah menegakkan. Selain itu, tipe void * (pointer to void ) menggantikan char * sebagai yang tepat
ketik untuk pointer generik.

5.1 Pointer dan Alamat


Mari kita mulai dengan gambaran sederhana tentang bagaimana memori disusun. Mesin khas memiliki
array sel memori yang diberi nomor atau alamat secara berurutan yang dapat dimanipulasi
secara individu atau dalam kelompok yang berdekatan. Satu situasi umum adalah byte apa pun bisa berupa char , a
sepasang sel satu byte dapat diperlakukan sebagai integer pendek , dan empat byte yang berdekatan membentuk panjang . SEBUAH
pointer adalah sekelompok sel (seringkali dua atau empat) yang dapat menyimpan alamat. Jadi jika c adalah char dan p
adalah penunjuk yang menunjuk ke sana, kita dapat mewakili situasi dengan cara ini:

Operator unary & memberikan alamat suatu objek, demikian pernyataan itu

p = & c;
memberikan alamat c ke variabel p , dan p dikatakan `` arahkan ke '' c . The & operator saja
berlaku untuk objek dalam memori: variabel dan elemen array. Itu tidak dapat diterapkan pada ekspresi,
konstanta, atau mendaftar variabel.
Operator unary * adalah operator tipuan atau dereferencing ; bila diterapkan ke pointer, itu
mengakses objek yang ditunjuk oleh pointer. Misalkan x dan y adalah bilangan bulat dan ip adalah sebuah pointer
ke int . Urutan buatan ini menunjukkan cara mendeklarasikan pointer dan cara menggunakan & dan * :

int x = 1, y = 2, z [10];
int * ip; / * ip adalah pointer ke int * /

ip = & x; / * ip sekarang menunjuk ke x * /


y = * ip; / * y sekarang 1 * /
* ip = 0; / * x sekarang 0 * /
ip = & z [0]; / * ip sekarang menunjuk ke z [0] * /
Deklarasi x , y , dan z adalah apa yang telah kita lihat selama ini. Deklarasi dari pointer ip ,
int * ip;
Halaman 78

79
dimaksudkan sebagai mnemonik; ia mengatakan bahwa ekspresi * ip adalah sebuah int . Sintaks dari
deklarasi untuk variabel meniru sintaks ekspresi di mana variabel mungkin muncul.
Alasan ini berlaku untuk deklarasi fungsi juga. Sebagai contoh,

dobel * dp, atof (char *);


mengatakan bahwa dalam ekspresi * dp dan atof (s) memiliki nilai ganda , dan argumen
Atof adalah pointer ke char .
Anda juga harus mencatat implikasi bahwa pointer dibatasi untuk menunjuk ke jenis tertentu
objek: setiap pointer menunjuk ke tipe data tertentu. (Ada satu pengecualian: penunjuk `` ke
void '' digunakan untuk memegang semua jenis pointer tetapi tidak dapat didereferensi dengan sendirinya. Kami akan kembali lagi
dalam Bagian 5.11 .)

Jika ip menunjuk ke integer x , maka * ip dapat terjadi dalam konteks apa pun di mana x bisa, jadi

* ip = * ip + 10;
peningkatan * ip oleh 10.
Operator unary * dan & mengikat lebih ketat daripada operator aritmatika, begitu penugasannya

y = * ip + 1
mengambil titik ip apa pun , menambahkan 1, dan memberikan hasilnya ke y , sementara

* ip + = 1
increments apa ip menunjuk ke, seperti halnya
++ * ip
dan
(* ip) ++
Tanda kurung diperlukan dalam contoh terakhir ini; tanpa mereka, ekspresi akan
increment ip daripada menunjuk apa, karena operator unary seperti * dan ++ mengasosiasikan dengan benar
ke kiri.
Akhirnya, karena pointer adalah variabel, mereka dapat digunakan tanpa dereferensi. Misalnya, jika iq
adalah pointer lain ke int ,

iq = ip
menyalin isi ip ke iq , sehingga membuat iq menunjuk ke apa pun yang ditunjuk ip .

5.2 Pointer dan Argumen Fungsi


Karena C meneruskan argumen ke fungsi berdasarkan nilai, tidak ada cara langsung untuk fungsi yang dipanggil
untuk mengubah variabel dalam fungsi panggilan. Misalnya, rutinitas penyortiran mungkin bertukar dua
argumen out-of-order dengan fungsi yang disebut swap . Tidak cukup menulis
swap (a, b);
di mana fungsi swap didefinisikan sebagai

batal swap (int x, int y) / * SALAH * /


{
int temp;

temp = x;
x = y;
y = temp;
}
Karena panggilan berdasarkan nilai, swap tidak dapat memengaruhi argumen a dan b dalam rutinitas yang menyebutnya.
Fungsi di atas swap salinan dari sebuah dan b .

Halaman 79

80
Cara untuk mendapatkan efek yang diinginkan adalah agar program pemanggil meneruskan pointer ke nilai
diubah:

tukar (& a, & b);


Karena operator & menghasilkan alamat variabel, & a adalah penunjuk ke a . Dalam swap itu sendiri, the
parameter dinyatakan sebagai pointer, dan operan diakses secara tidak langsung melalui mereka.
batal swap (int * px, int * py) / * interchange * px dan * py * /
{ int temp;

temp = * px;
* px = * py;
* py = temp;
}
Secara gambar:

Argumen pointer memungkinkan fungsi untuk mengakses dan mengubah objek dalam fungsi yang memanggilnya.
Sebagai contoh, pertimbangkan fungsi getint yang melakukan konversi input format bebas oleh
memecah aliran karakter ke dalam nilai integer, satu integer per panggilan. getint harus kembali
nilai yang ditemukan dan juga sinyal akhir file ketika tidak ada lagi input. Nilai-nilai ini harus
dilewatkan kembali oleh jalur yang terpisah, karena tidak peduli nilai apa yang digunakan untuk EOF , itu juga bisa
nilai integer input.

Salah satu solusinya adalah mendapatkan getint mengembalikan status file sebagai nilai fungsinya, saat menggunakan a
argumen pointer untuk menyimpan integer yang dikonversi kembali dalam fungsi panggilan. Ini skemanya
digunakan oleh scanf juga; lihat Bagian 7.4 .

Lingkaran berikut mengisi array dengan bilangan bulat melalui panggilan ke getint :

Halaman 80

81
int n, array [SIZE], getint (int *);

untuk (n = 0; n <SIZE && getint (& array [n])! = EOF; n ++)


;
Setiap panggilan menetapkan larik [n] ke bilangan bulat berikutnya yang ditemukan dalam input dan kenaikan n . Perhatikan itu
Sangat penting untuk meneruskan alamat array [n] ke getint . Kalau tidak, tidak ada cara untuk mendapatkan
untuk mengkomunikasikan integer yang dikonversi kembali ke pemanggil.
Versi getint kami mengembalikan EOF untuk akhir file, nol jika input berikutnya bukan angka, dan a
nilai positif jika input berisi angka yang valid.

#termasuk <ctype.h>

int getch (void);


membatalkan ungetch (int);

/ * getint: dapatkan integer berikutnya dari input ke * pn * /


int getint (int * pn)
{
int c, tandatangani;

while (isspace (c = getch ())) / * lewati spasi putih * /


;
if (! isdigit (c) && c! = EOF && c! = '+' && c! = '-') {
ungetch (c); / * itu bukan angka * /
return 0;
}
tanda
if (c =
==(c ==||
'+' '-')?
c ==-1: 1;
'-')
c = getch ();
untuk (* pn = 0; isdigit (c), c = getch ())
* pn = 10 * * pn + (c - '0');
* pn * = masuk;
if (c! = EOF)
ungetch (c);
kembali c;
}
Sepanjang getint , * pn digunakan sebagai variabel int biasa . Kami juga telah menggunakan getch dan
ungetch (dijelaskan pada Bagian 4.3 ) sehingga satu karakter tambahan yang harus dibaca dapat didorong
kembali ke input.
Latihan 5-1. Seperti yang ditulis, getint memperlakukan tanda + atau - tidak diikuti oleh angka sebagai valid
representasi nol. Perbaiki untuk mendorong karakter seperti itu kembali pada input.

Latihan 5-2. Tulis getfloat , analog floating-point dari getint . Apa jenis getfloat
kembali sebagai nilai fungsinya?

5.3 Pointer dan Array


Di C, ada hubungan yang kuat antara pointer dan array, cukup kuat bahwa pointer
dan array harus didiskusikan secara bersamaan. Operasi apa pun yang dapat dicapai dengan array
Berlangganan juga dapat dilakukan dengan pointer. Versi pointer secara umum akan lebih cepat tetapi, pada
Setidaknya bagi yang belum tahu, agak sulit untuk dipahami.
Deklarasi

int a [10];
mendefinisikan array ukuran 10, yaitu, blok 10 objek berturut-turut bernama [0] , a [1] ,
..., a [9] .

Halaman 81

82

Notasi a [i] mengacu pada elemen ke- i dari array. Jika pa adalah pointer ke integer,
dinyatakan sebagai

int * pa;
maka tugas

pa = & a [0];
set pa untuk menunjuk ke elemen nol dari a ; yaitu, pa berisi alamat [0] .

Sekarang tugasnya

x = * pa;
akan menyalin isi dari [0] ke x .
Jika pa menunjuk ke elemen tertentu dari array, maka menurut definisi pa +1 menunjuk ke yang berikutnya
elemen, pa + i menunjuk i elemen setelah pa , dan pa-i menunjukkan i elemen sebelumnya. Jadi, jika poin pa
ke [0] ,

* (pa + 1)
mengacu pada isi dari [1] , pa + i adalah alamat dari [i] , dan * (pa + i) adalah isi dari
a [i] .

Halaman 82

83
Pernyataan ini benar terlepas dari jenis atau ukuran variabel dalam array a . Itu
arti dari `` menambahkan 1 ke sebuah pointer, '' dan dengan ekstensi, semua pointer aritmatika, adalah bahwa pa + 1 poin
ke objek berikutnya, dan pa + i menunjuk ke objek ke- i di luar pa .

Korespondensi antara pengindeksan dan pointer aritmatika sangat dekat. Menurut definisi,
nilai dari suatu variabel atau ekspresi dari tipe array adalah alamat elemen nol dari array. Jadi
setelah penugasan

pa = & a [0];
pa dan sebuah memiliki nilai yang identik. Karena nama array adalah sinonim untuk lokasi
elemen awal, tugas pa = & a [0] juga dapat ditulis sebagai
pa = a;
Pada pandangan pertama, agak lebih mengejutkan adalah fakta bahwa referensi ke [i] juga dapat ditulis sebagai
* (a + i) . Dalam mengevaluasi [i] , C mengubahnya menjadi * (a + i) segera; dua bentuk itu
setara. Menerapkan operator & ke kedua bagian dari kesetaraan ini, berarti & a [i] dan
a + i juga identik: a + i adalah alamat elemen ke- i di luar a . Seperti sisi lain dari ini
koin, jika pa adalah pointer, ekspresi mungkin menggunakannya dengan subskrip; pa [i] identik dengan * (pa + i) .
Singkatnya, ekspresi array-dan-indeks setara dengan yang ditulis sebagai pointer dan offset.
Ada satu perbedaan antara nama array dan pointer yang harus diingat. SEBUAH
pointer adalah variabel, jadi pa = a dan pa ++ legal. Tetapi nama array bukan variabel;
konstruksi seperti a = pa dan a ++ ilegal.

Ketika nama array dilewatkan ke fungsi, apa yang dilewatkan adalah lokasi inisial
elemen. Dalam fungsi yang dipanggil, argumen ini adalah variabel lokal, dan juga nama array
parameter adalah pointer, yaitu variabel yang berisi alamat. Kita bisa menggunakan fakta ini untuk menulis
versi lain dari strlen , yang menghitung panjang string.

/ * strlen: mengembalikan panjang string s * /


int strlen (char * s)
{
int n;

untuk (n = 0; * s! = '\ 0', s ++)


n ++;
return n;
}
Karena s adalah sebuah pointer, menambahkannya adalah sah; s ++ tidak memiliki efek pada string karakter
dalam fungsi yang disebut strlen , tetapi hanya menambah salinan pribadi strlen dari pointer.
Itu berarti panggilan seperti
strlen ("halo, dunia"); / * string konstan * /
strlen (array); / * char array [100]; * /
strlen (ptr); / * char * ptr; * /
semua bekerja.
Sebagai parameter formal dalam definisi fungsi,

char s [];
dan
char * s;
adalah setara; kami lebih suka yang terakhir karena dikatakan lebih eksplisit bahwa variabel adalah pointer.
Ketika sebuah nama array dilewatkan ke suatu fungsi, fungsi itu bisa dengan nyaman percaya bahwa itu
telah diserahkan baik array atau pointer, dan memanipulasi sesuai itu. Bahkan bisa digunakan
keduanya notasi jika tampaknya tepat dan jelas.
Halaman 83

84
Dimungkinkan untuk melewatkan bagian dari array ke suatu fungsi, dengan melewatkan sebuah pointer ke awal
subarray. Misalnya, jika a adalah array,

f (& a [2])
dan

f (a + 2)
keduanya beralih ke fungsi f alamat subarray yang dimulai pada [2] . Dalam f , the
deklarasi parameter dapat dibaca

f (int arr []) {...}


atau
f (int * arr) {...}
Jadi sejauh menyangkut f , fakta bahwa parameter mengacu pada bagian array yang lebih besar adalah no
konsekuensi.
Jika seseorang yakin bahwa elemen-elemennya ada, dimungkinkan juga untuk mengindeks mundur dalam sebuah array; p [-1] ,
p [-2] , dan seterusnya secara sintaksis legal, dan merujuk pada elemen yang langsung mendahului
p [0] . Tentu saja, ilegal untuk merujuk ke objek yang tidak berada dalam batas array.

5.4 Aritmatika Alamat


Jika p adalah penunjuk ke beberapa elemen array, maka p ++ menambah p untuk menunjuk ke yang berikutnya
elemen, dan p + = i menambahkannya untuk menunjukkan elemen i di luar tempatnya saat ini. Ini dan
konstruksi serupa adalah bentuk sederhana dari pointer atau aritmatika alamat.
C konsisten dan teratur dalam pendekatannya untuk mengatasi aritmatika; integrasi pointer,
array, dan address arithmetic adalah salah satu kekuatan bahasa. Mari kita ilustrasikan oleh
menulis pengalokasi penyimpanan yang belum sempurna. Ada dua rutinitas. Yang pertama, alokasi (n) , mengembalikan a
pointer ke posisi karakter n berturut-turut, yang dapat digunakan oleh pemanggil alokasi untuk
menyimpan karakter. Yang kedua, afree (p) , melepaskan penyimpanan yang diperoleh sehingga dapat dikembalikan
digunakan nanti. Rutinitas adalah `` rudimenter '' karena panggilan untuk memulai harus dilakukan di
urutan berlawanan dengan panggilan yang dibuat berdasarkan alokasi . Artinya, penyimpanan dikelola oleh alokasi dan tiga
adalah stack, atau last-in, first-out. Perpustakaan standar menyediakan fungsi analog yang disebut malloc
dan gratis yang tidak memiliki batasan seperti itu; di Bagian 8.7 kami akan menunjukkan bagaimana mereka bisa
diimplementasikan.

Implementasi termudah adalah dengan mengalokasikan potongan dari array karakter besar yang kita
akan memanggil alokasibuf . Array ini adalah swasta untuk alokasi dan Afree . Karena mereka berurusan dengan pointer, bukan
indeks array, tidak ada kebutuhan rutin lain yang tahu nama array, yang dapat dinyatakan statis
dalam file sumber yang berisi alokasi dan tiga , dan dengan demikian tidak terlihat di luarnya. Secara praktis
implementasi, array mungkin bahkan tidak punya nama; itu mungkin malah diperoleh oleh
memanggil malloc atau dengan meminta sistem operasi untuk pointer ke beberapa blok tanpa nama
penyimpanan.

Informasi lain yang dibutuhkan adalah berapa banyak alokasi yang telah digunakan. Kami menggunakan pointer,
disebut mengalokasikan , yang menunjuk ke elemen bebas berikutnya. Ketika alokasi diminta untuk n karakter, itu
memeriksa untuk melihat apakah ada ruang yang tersisa di alokasibuf . Jika demikian, alokasi mengembalikan nilai saat ini
dari dialokasikan (yaitu, awal dari blok gratis), kemudian menambahkannya dengan n untuk menunjuk ke yang berikutnya
area bebas. Jika tidak ada ruang, alokasikan kembali nol. afree (p) hanya mengatur pengalokasian ke p jika p adalah
di dalam alokasibuf .

Halaman 84

85
#define ALLOCSIZE 10000 / * ukuran ruang yang tersedia * /

alokasi char statis [ALOKASI]; / * penyimpanan untuk alokasi * /


static char * alokasip = alokasibuf; / * Posisi bebas berikutnya * /

char * dialokasikan (int n) / * mengembalikan pointer ke n karakter * /


{
if (alokasikanbuf + ALLOCSIZE - alokasip> = n) {/ * cocok * /
mengalokasikan + = n;
mengembalikan alokasi - n; / * old p * /
} lain / * tidak cukup ruang * /
return 0;
}

batal afree (char * p) / * penyimpanan gratis yang ditunjukkan oleh p * /


{
if (p> = dialokasikanbuf && p <dialokasikanbuf + ALLOCSIZE)
mengalokasikan = p;
}
Secara umum pointer dapat diinisialisasi sama seperti variabel lainnya, meskipun biasanya satu-satunya
nilai yang berarti adalah nol atau ekspresi yang melibatkan alamat data yang didefinisikan sebelumnya
jenis yang sesuai. Deklarasi
static char * alokasip = alokasibuf;
mendefinisikan mengalokasikan untuk menjadi penunjuk karakter dan menginisialisasi untuk menunjuk ke awal
dialokasikanbuf , yang merupakan posisi bebas berikutnya ketika program dimulai. Ini juga bisa saja
tertulis
static char * alokasip = & alokasibuf [0];
karena nama array adalah alamat elemen zeroth.
Ujian

if (alokasikanbuf + ALLOCSIZE - alokasip> = n) {/ * cocok * /


memeriksa apakah ada cukup ruang untuk memenuhi permintaan untuk n karakter. Jika ada, nilai baru dari
Alokasi akan paling banyak melampaui akhir alokasi . Jika permintaan bisa dipenuhi,
mengalokasikan mengembalikan pointer ke awal blok karakter (perhatikan deklarasi
berfungsi sendiri). Jika tidak, alokasi harus mengembalikan beberapa sinyal bahwa tidak ada ruang yang tersisa. Jaminan C.
bahwa nol tidak pernah merupakan alamat yang valid untuk data, sehingga nilai balik nol dapat digunakan untuk memberi sinyal
Peristiwa abnormal, dalam hal ini tidak ada ruang.
Pointer dan integer tidak dapat saling dipertukarkan. Nol adalah satu-satunya pengecualian: konstanta nol dapat
ditugaskan ke pointer, dan pointer dapat dibandingkan dengan nol konstan. Simbolis

Halaman 85

86
konstanta NULL sering digunakan sebagai pengganti nol, sebagai mnemonik untuk menunjukkan dengan lebih jelas bahwa ini adalah
nilai khusus untuk sebuah pointer. NULL didefinisikan dalam <stdio.h> . Kami akan menggunakan NULL untuk selanjutnya.

Tesnya seperti

if (alokasikanbuf + ALLOCSIZE - alokasip> = n) {/ * cocok * /


dan

if (p> = dialokasikanbuf && p <dialokasikanbuf + ALLOCSIZE)


menunjukkan beberapa aspek penting dari pointer aritmatika. Pertama, pointer dapat dibandingkan di bawah
keadaan tertentu. Jika p dan q arahkan ke anggota array yang sama, maka hubungan seperti == , ! = ,
< , > = , dll., berfungsi dengan baik. Sebagai contoh,
p <q
benar jika p menunjuk ke elemen array yang lebih awal dari q . Pointer apa saja bisa
dibandingkan secara bermakna untuk kesetaraan atau ketidaksetaraan dengan nol. Tetapi perilaku itu tidak terdefinisi
aritmatika atau perbandingan dengan pointer yang tidak menunjuk ke anggota array yang sama.
(Ada satu pengecualian: alamat elemen pertama yang melewati akhir array dapat digunakan di
aritmatika penunjuk.)
Kedua, kita telah mengamati bahwa pointer dan integer dapat ditambahkan atau dikurangi.
Konstruksi
p + n
berarti alamat objek ke- n di luar satu p yang sekarang ditunjukkan. Ini benar
terlepas dari jenis objek p menunjuk ke; n diskalakan sesuai dengan ukuran objek p
menunjuk ke, yang ditentukan oleh deklarasi hal . Jika int adalah empat byte, misalnya, the
int akan diskalakan oleh empat.
Pengurangan Pointer juga valid: jika p dan q menunjuk ke elemen-elemen dari array yang sama, dan p <q , maka
q-p + 1 adalah jumlah elemen dari p hingga q inklusif. Fakta ini belum bisa digunakan untuk menulis
versi lain dari strlen :

/ * strlen: mengembalikan panjang string s * /


int strlen (char * s)
{
char * p = s;

while (* p! = '\ 0')


p ++;
kembali p - s;
}
Dalam deklarasi, p diinisialisasi ke s , yaitu, untuk menunjuk ke karakter pertama dari string. Dalam
saat loop, setiap karakter pada gilirannya diperiksa sampai '\ 0' di akhir terlihat. Karena p
menunjuk ke karakter, p ++ memajukan p ke karakter berikutnya setiap kali, dan ps memberikan nomor
karakter di atas, yaitu panjang string. (Jumlah karakter dalam string
bisa terlalu besar untuk disimpan dalam int . Header <stddef.h> mendefinisikan tipe ptrdiff_t yang
cukup besar untuk menampung perbedaan nilai pointer yang ditandatangani. Jika kita berhati-hati,
Namun, kami akan menggunakan size_t untuk nilai kembali strlen , untuk mencocokkan perpustakaan standar
Versi: kapan. size_t adalah tipe integer yang tidak ditandatangani yang dikembalikan oleh sizeof operator.
Pointer aritmatika konsisten: jika kita telah berurusan dengan float , yang menempati lebih banyak
penyimpanan karakter itu, dan jika p adalah pointer ke float , p ++ akan maju ke float berikutnya .
Dengan demikian kita dapat menulis versi alokasi lain yang mempertahankan float , bukan char , semata-mata
dengan mengubah char untuk mengapung di seluruh alokasi dan bebas . Semua manipulasi pointer
secara otomatis memperhitungkan ukuran objek yang ditunjuk.

Halaman 86

87
Operasi pointer yang valid adalah penugasan pointer dari tipe yang sama, menambah atau mengurangi
pointer dan integer, kurangi atau bandingkan dua pointer ke anggota array yang sama,
dan menugaskan atau membandingkan dengan nol. Semua aritmatika pointer lainnya adalah ilegal. Tidak sah untuk menambahkan
dua pointer, atau kalikan atau membagi atau shift atau topeng mereka, atau untuk menambahkan mengapung atau ganda untuk
mereka, atau bahkan, kecuali void * , untuk menetapkan pointer dari satu tipe ke pointer dari tipe lain
tanpa gips.

5.5 Pointer Karakter dan Fungsi


Sebuah konstan String , ditulis sebagai
"Aku seorang string"
adalah array karakter. Dalam representasi internal, array diakhiri dengan nol
karakter '\ 0' sehingga program dapat menemukan akhir. Dengan demikian panjang dalam penyimpanan lebih dari satu
jumlah karakter antara tanda kutip ganda.
Mungkin kemunculan konstanta string yang paling umum adalah sebagai argumen untuk fungsi, seperti pada

printf ("hello, world \ n");


Ketika string karakter seperti ini muncul dalam suatu program, akses ke sana adalah melalui karakter
penunjuk; printf menerima pointer ke awal array karakter. Itu adalah sebuah string
konstanta diakses oleh pointer ke elemen pertama.
Konstanta string tidak harus berupa argumen fungsi. Jika pmessage dinyatakan sebagai

char * pmessage;
lalu pernyataan itu
pmessage = "sekarang adalah waktunya";
menetapkan untuk pmessage pointer ke array karakter. Ini bukan salinan string; hanya petunjuk
terlibat. C tidak menyediakan operator apa pun untuk memproses seluruh rangkaian karakter
sebuah unit.
Ada perbedaan penting antara definisi-definisi ini:

char amessage [] = "sekarang adalah waktunya"; / * sebuah array * /


char * pmessage = "sekarang adalah waktunya"; / * sebuah pointer * /
amessage adalah sebuah array, cukup besar untuk menampung urutan karakter dan '\ 0' itu
menginisialisasi itu. Karakter individu dalam array dapat diubah tetapi amessage akan selalu
lihat penyimpanan
string yangselanjutnya
konstan; pointer sama. Di sisi lain,
dapat pmessage adalah
dimodifikasi ke titikpointer, diinisialisasi
di tempat lain, tetapiuntuk menunjuk
hasilnya adalah ke a
tidak terdefinisi jika Anda mencoba mengubah konten string.

Kami akan menggambarkan lebih banyak aspek pointer dan array dengan mempelajari dua versi yang bermanfaat
fungsi diadaptasi dari perpustakaan standar. Fungsi pertama adalah strcpy (s, t) , yang menyalin
string t ke string s . Akan menyenangkan hanya untuk mengatakan s = t tetapi ini menyalin pointer, bukan
karakter. Untuk menyalin karakter, kita perlu loop. Versi array yang pertama:

Halaman 87

88
/ * strcpy: salin t ke s; versi subscript array * /
membatalkan strcpy (char * s, char * t)
{
int i;

i = 0;
while ((s [i] = t [i])! = '\ 0')
i ++;
}
Sebagai kontras, berikut adalah versi strcpy dengan pointer:
/ * strcpy: salin t ke s; versi penunjuk * /
membatalkan strcpy (char * s, char * t)
{
int i;

i = 0;
while ((* s = * t)! = '\ 0') {
s ++;
t ++;
}
}
Karena argumen dilewatkan oleh nilai, strcpy dapat menggunakan parameter s dan t dengan cara apa pun
menyenangkan. Di sini mereka dengan mudah diinisialisasi pointer, yang berbaris sepanjang array a
karakter sekaligus, hingga '\ 0' yang mengakhiri t telah disalin ke s .
Dalam praktiknya, strcpy tidak akan ditulis seperti yang kami tunjukkan di atas. Pemrogram C berpengalaman
lebih suka

/ * strcpy: salin t ke s; pointer versi 2 * /


membatalkan strcpy (char * s, char * t)
{
while ((* s ++ = * t ++)! = '\ 0')
;
}
Ini memindahkan kenaikan s dan t ke bagian uji loop. Nilai * t ++ adalah
karakter yang t ditunjukkan sebelum t bertambah; postfix ++ tidak berubah t sampai
setelah karakter ini diambil. Dengan cara yang sama, karakter tersebut disimpan ke dalam lama s
posisi sebelum s bertambah. Karakter ini juga merupakan nilai yang dibandingkan dengan '\ 0'
untuk mengontrol loop. Efek bersihnya adalah bahwa karakter disalin dari t ke s , atas dan termasuk
terminating '\ 0' .
Sebagai singkatan terakhir, amati bahwa perbandingan terhadap '\ 0' berlebihan, karena
pertanyaan hanyalah apakah ungkapan itu nol. Jadi fungsi tersebut kemungkinan ditulis sebagai

/ * strcpy: salin t ke s; pointer versi 3 * /


membatalkan strcpy (char * s, char * t)
{
while (* s ++ = * t ++)
;
}
Meskipun ini mungkin tampak samar pada pandangan pertama, kenyamanan notasi cukup besar, dan
idiom harus dikuasai, karena Anda akan sering melihatnya dalam program C.
The strcpy di perpustakaan standar ( <string.h> ) mengembalikan string sasaran sebagai nilai fungsinya.

Rutin kedua yang akan kita periksa adalah strcmp (s, t) , yang membandingkan karakter
string s dan t , dan mengembalikan negatif, nol atau positif jika s secara leksikografis kurang dari, sama
ke, atau lebih besar dari t . Nilai diperoleh dengan mengurangi karakter di posisi pertama
dimana s dan t tidak setuju.
/ * strcmp: return <0 if s <t, 0 if s == t,> 0 if s> t * /

Halaman 88

89
int strcmp (char * s, char * t)
{
int i;

untuk (i = 0; s [i] == t [i]; i ++)


if (s [i] == '\ 0')
return 0;
return s [i] - t [i];
}
Versi penunjuk strcmp :

/ * strcmp: return <0 if s <t, 0 if s == t,> 0 if s> t * /


int strcmp (char * s, char * t)
{
untuk (; * s == * t; s ++, t ++)
if (* s == '\ 0')
return 0;
return * s - * t;
}
Karena ++ dan - adalah operator awalan atau postfix, kombinasi lain dari * dan ++ dan -
terjadi, walaupun lebih jarang. Sebagai contoh,
* - hal
mengurangi p sebelum mengambil karakter yang p menunjuk ke. Bahkan, pasangan ekspresi

* p ++ = val; / * dorong val ke tumpukan * /


val = * - p; / * pop top stack ke dalam val * /
adalah ungkapan standar untuk mendorong dan membuka tumpukan; lihat Bagian 4.3 .
Header <string.h> berisi deklarasi untuk fungsi yang disebutkan di bagian ini, plus
berbagai fungsi penanganan string lainnya dari perpustakaan standar.

Latihan 5-3. Tulis versi pointer dari fungsi strcat yang kami tunjukkan di Bab 2 :
strcat (s, t) menyalin string t ke akhir s .

Latihan 5-4. Tulis fungsi strend (s, t) , yang mengembalikan 1 jika string t muncul pada
akhir string s , dan nol sebaliknya.

Latihan 5-5. Tulis versi fungsi perpustakaan strncpy , strncat , dan strncmp , yang
beroperasi paling banyak n karakter pertama dari string argumen mereka. Sebagai contoh,
strncpy (s, t, n) menyalin paling banyak n karakter dari t ke s . Deskripsi penuh dalam Lampiran B .

Latihan 5-6. Menulis ulang program yang sesuai dari bab sebelumnya dan latihan dengan pointer
bukannya pengindeksan array. Kemungkinan yang baik termasuk getline ( Bab 1 dan 4 ), atoi , itoa ,
dan variannya ( Bab 2 , 3 , dan 4 ), mundur ( Bab 3 ), dan strindex dan getop
( Bab 4 ).

5.6 Array Pointer; Pointer ke Pointer


Karena pointer adalah variabel itu sendiri, mereka dapat disimpan dalam array seperti halnya variabel lain.
Mari kita ilustrasikan dengan menulis program yang akan mengurutkan satu set baris teks ke dalam urutan abjad, a
versi sederhana dari jenis program UNIX .
Dalam Bab 3 , kami menyajikan fungsi pengurutan Shell yang akan mengurutkan array bilangan bulat, dan di
Bab 4 kita memperbaikinya dengan quicksort. Algoritma yang sama akan berfungsi, kecuali sekarang
kita harus berurusan dengan baris teks, yang panjangnya berbeda, dan yang, tidak seperti bilangan bulat,
tidak dapat dibandingkan atau dipindahkan dalam satu operasi. Kami membutuhkan representasi data yang akan mengatasinya
efisien dan nyaman dengan garis teks panjang variabel.

Di sinilah array pointer masuk. Jika garis yang akan disortir disimpan end-to-end dalam satu
array karakter yang panjang, maka setiap baris dapat diakses oleh pointer ke karakter pertamanya. Itu

Halaman 89

90
pointer sendiri dapat disimpan dalam array. Dua garis dapat dibandingkan dengan melewati mereka
pointer ke strcmp . Ketika dua baris out-of-order harus dipertukarkan, pointer di
pointer array dipertukarkan, bukan baris teks itu sendiri.
Ini menghilangkan masalah kembar dari manajemen penyimpanan yang rumit dan overhead yang tinggi
akan pergi dengan memindahkan garis sendiri.

Proses penyortiran memiliki tiga langkah:

baca semua baris input


urutkan mereka
cetak secara berurutan

Seperti biasa, yang terbaik adalah membagi program menjadi fungsi yang sesuai dengan pembagian alami ini, dengan
rutin utama mengendalikan fungsi-fungsi lainnya. Mari kita menunda langkah penyortiran sejenak, dan
berkonsentrasi pada struktur data dan input dan output.

Input rutin harus mengumpulkan dan menyimpan karakter dari setiap baris, dan membangun array
pointer ke garis. Itu juga harus menghitung jumlah jalur input, karena informasi itu
diperlukan untuk menyortir dan mencetak. Karena fungsi input hanya dapat mengatasi angka yang terbatas
jalur input, dapat mengembalikan beberapa penghitungan ilegal seperti -1 jika terlalu banyak input disajikan.

Output rutin hanya harus mencetak garis dalam urutan di mana mereka muncul dalam array
pointer.

#termasuk <stdio.h>
#termasuk <string.h>

#define MAXLINES 5000 / * maks #lines untuk disortir * /

char * lineptr [MAXLINES]; / * pointer ke baris teks * /

int readlines (char * lineptr [], int nlines);


membatalkan penulisan (char * lineptr [], int nlines);

membatalkan qsort (char * lineptr [], int left, int right);

/ * urutkan jalur input * /


utama()
{
int nlines; / * jumlah baris input baca * /

if ((nlines = readlines (lineptr, MAXLINES))> = 0) {


qsort (lineptr, 0, nlines-1);
writelines (lineptr, nlines);
return 0;
} lain {
printf ("error: input terlalu besar untuk disortir \ n");
return 1;
}
}

#define MAXLEN 1000 / * panjang maksimal dari setiap jalur input * /


int getline (char *, int);

Halaman 90

91
char * alokasi (int);

/ * readlines: baca baris input * /


int readlines (char * lineptr [], int maxlines)
{
Int len, nlines;
char * p, baris [MAXLEN];

nlines = 0;
while ((len = getline (line, MAXLEN))> 0)
if (nlines> = maxlines || p = alokasi (len) == NULL)
return -1;
lain {
baris [len-1] = '\ 0'; / * hapus baris baru * /
strcpy (p, line);
lineptr [nlines ++] = p;
}
mengembalikan nlines;
}

/ * writelines: tulis baris output * /


membatalkan tulisan tangan (char * lineptr [], int nlines)
{
int i;
untuk (i = 0; i <nlines; i ++)
printf ("% s \ n", lineptr [i]);
}
Fungsi getline berasal dari Bagian 1.9 .
Hal baru utama adalah deklarasi untuk lineptr :

char * lineptr [MAXLINES]


mengatakan bahwa lineptr adalah array elemen MAXLINES , yang masing-masing elemennya adalah pointer ke a
char . Yaitu, lineptr [i] adalah penunjuk karakter, dan * lineptr [i] adalah karakter yang ditunjukkannya
untuk, karakter pertama dari baris teks ke- i yang disimpan.
Karena lineptr sendiri merupakan nama dari array, ia dapat diperlakukan sebagai pointer dengan cara yang sama
seperti dalam contoh kami sebelumnya, dan writelines dapat ditulis bukan sebagai

/ * writelines: tulis baris output * /


membatalkan tulisan tangan (char * lineptr [], int nlines)
{
while (nlines--> 0)
printf ("% s \ n", * lineptr ++);
}
Awalnya, * lineptr menunjuk ke baris pertama; setiap elemen memajukannya ke penunjuk baris berikutnya
sementara nlines dihitung mundur.
Dengan input dan output terkendali, kita dapat melanjutkan untuk menyortir. Quicksort dari Bab 4
perlu perubahan kecil: deklarasi harus dimodifikasi, dan operasi perbandingan harus
dilakukan dengan memanggil strcmp . Algoritma tetap sama, yang memberi kita kepercayaan diri
bahwa itu masih akan berfungsi.

/ * qsort: urutkan v [kiri] ... v [kanan] ke urutan yang meningkat * /


void qsort (char * v [], int kiri, int kanan)
{
int i, last;
void swap (char * v [], int i, int j);

if (kiri> = kanan) / * tidak melakukan apa-apa jika array mengandung * /


kembali; / * kurang dari dua elemen * /
swap (v, kiri, (kiri + kanan) / 2);
terakhir = kiri;

Halaman 91

92
untuk (i = kiri + 1; i <= kanan; i ++)
if (strcmp (v [i], v [left]) <0)
swap (v, ++ terakhir, i);
swap (v, kiri, terakhir);
qsort (v, kiri, terakhir-1);
qsort (v, +1 terakhir, kanan);
}
Demikian pula, rutinitas swap hanya perlu perubahan sepele:

/ * swap: interchange v [i] dan v [j] * /


batal swap (char * v [], int i, int j)
{
char * temp;

temp = v [i];
v [i] = v [j];
v [j] = temp;
}
Karena setiap elemen individual v (alias lineptr ) adalah penunjuk karakter, temp harus juga demikian
satu dapat disalin ke yang lain.
Latihan 5-7. Tulis ulang readlines untuk menyimpan baris dalam array yang disediakan oleh main , bukan
alokasi panggilan untuk mempertahankan penyimpanan. Seberapa cepat programnya?

5.7 Array Multi Dimensi


C menyediakan array multi-dimensi persegi panjang, meskipun dalam praktiknya mereka jauh lebih sedikit digunakan
dari array pointer. Di bagian ini, kami akan menunjukkan beberapa propertinya.
Pertimbangkan masalah konversi tanggal, dari hari bulan ke hari tahun dan sebaliknya
sebaliknya. Misalnya, 1 Maret adalah hari ke-60 tahun non-kabisat, dan hari ke-61 tahun kabisat.
Mari kita mendefinisikan dua fungsi untuk melakukan konversi: day_of_year mengonversi bulan dan hari
menjadi hari dalam setahun, dan month_day mengubah hari tahun menjadi bulan dan hari.
Karena fungsi terakhir ini menghitung dua nilai, argumen bulan dan hari akan menjadi pointer:

month_day (1988, 60, & m, & d)


set m ke 2 dan d ke 29 (29 Februari).
Kedua fungsi ini membutuhkan informasi yang sama, tabel jumlah hari dalam setiap bulan
(`` tiga puluh hari sebelum September ... ''). Karena jumlah hari per bulan berbeda untuk tahun kabisat
dan tahun non-kabisat, lebih mudah untuk memisahkan mereka menjadi dua baris array dua dimensi daripada
untuk melacak apa yang terjadi pada Februari selama perhitungan. Array dan fungsi untuk
melakukan transformasi adalah sebagai berikut:

daytab char statis [2] [13] = {


{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};

/ * day_of_year: set hari tahun dari bulan & hari * /


int day_of_year (tahun int, bulan int, hari int)
{
int i, lompatan;
leap = tahun% 4 == 0 && tahun% 100! = 0 || tahun% 400 == 0;
untuk (i = 1; i <bulan; i ++)
hari + = daytab [lompatan] [i];
hari kembali;
}

/ * month_day: atur bulan, hari dari hari tahun * /


batal month_day (int year, int yearday, int * pmonth, int * pday)
{
int i, lompatan;

Halaman 92

93

leap = tahun% 4 == 0 && tahun% 100! = 0 || tahun% 400 == 0;


untuk (i = 1; hari kerja> daytab [lompatan] [i]; i ++)
hari kerja - = daytab [lompatan] [i];
* pmonth = i;
* pday = hari kerja;
}
Ingat bahwa nilai aritmatika dari ekspresi logis, seperti yang untuk lompatan , adalah nol
(false) atau one (true), sehingga dapat digunakan sebagai subscript dari daytab array .
Daytab array harus eksternal untuk day_of_year dan month_day , sehingga keduanya bisa
Gunakan. Kami membuatnya char untuk menggambarkan penggunaan sah char untuk menyimpan non-karakter kecil
bilangan bulat.

daytab adalah array dua dimensi pertama yang telah kita bahas . Dalam C, array dua dimensi adalah
benar-benar array satu dimensi, masing-masing elemen yang merupakan array. Karenanya subskrip adalah
ditulis sebagai

daytab [i] [j] / * [row] [col] * /


daripada

daytab [i, j] / * SALAH * /


Selain perbedaan notasi ini, array dua dimensi dapat diperlakukan dalam banyak hal yang sama
cara seperti dalam bahasa lain. Elemen disimpan oleh baris, jadi subskrip paling kanan, atau kolom,
bervariasi tercepat saat elemen diakses dalam urutan penyimpanan.
Array diinisialisasi oleh daftar inisialisasi dalam kawat gigi; setiap baris array dua dimensi adalah
diinisialisasi oleh sub-daftar yang sesuai. Kami memulai array daytab dengan kolom nol begitu
angka bulan itu dapat berjalan dari natural 1 hingga 12 bukannya 0 hingga 11. Karena ruang tidak berada pada a
premium di sini, ini lebih jelas daripada menyesuaikan indeks.

Jika array dua dimensi akan diteruskan ke fungsi, deklarasi parameter dalam
fungsi harus menyertakan jumlah kolom; jumlah baris tidak relevan, karena apa yang ada
berlalu adalah, seperti sebelumnya, pointer ke array baris, di mana setiap baris adalah array 13 int s. Di
kasus khusus ini, ini adalah pointer ke objek yang array 13 int s. Jadi jika array
daytab akan diteruskan ke fungsi f , deklarasi f adalah:

f (int daytab [2] [13]) {...}


Bisa juga begitu

f (int daytab [] [13]) {...}


karena jumlah baris tidak relevan, atau bisa jadi

f (int (* daytab) [13]) {...}


yang mengatakan bahwa parameter adalah penunjuk ke array 13 bilangan bulat. Tanda kurung adalah
diperlukan karena tanda kurung [] memiliki prioritas lebih tinggi daripada * . Tanpa tanda kurung,
pernyataan

int * daytab [13]


adalah array 13 pointer ke integer. Lebih umum, hanya dimensi pertama (subskrip) dari suatu
array gratis; semua yang lain harus ditentukan.
Bagian 5.12 membahas lebih lanjut tentang deklarasi yang rumit.

Latihan 5-8. Tidak ada kesalahan saat memeriksa di day_of_year atau month_day . Atasi cacat ini.
5.8 Inisialisasi Array Pointer

Halaman 93

94
Pertimbangkan masalah penulisan function month_name (n) , yang mengembalikan pointer ke a
string karakter yang berisi nama bulan ke- n . Ini adalah aplikasi yang ideal untuk
array statis internal . month_name berisi larik pribadi string karakter, dan mengembalikan a
arahkan ke yang tepat saat dipanggil. Bagian ini menunjukkan bagaimana susunan nama itu
diinisialisasi.
Sintaksnya mirip dengan inisialisasi sebelumnya:

/ * month_name: mengembalikan nama bulan ke-n * *


char * month_name (int n)
{
static char * name [] = {
"Bulan ilegal",
"Januari Februari Maret",
"April Mei Juni",
"Juli", "Agustus", "September",
"Oktober", "November", "Desember"
};

return (n <1 || n> 12)? name [0]: name [n];


}
Deklarasi nama , yang merupakan array dari pointer karakter, sama dengan lineptr dalam
contoh penyortiran. Penginisialisasi adalah daftar string karakter; masing - masing ditugaskan ke
posisi yang sesuai dalam array. Karakter dari string ke- i ditempatkan di suatu tempat,
dan pointer ke mereka disimpan dalam nama [i] . Karena ukuran nama array tidak ditentukan,
kompiler menghitung inisialisasi dan mengisi angka yang benar.

5.9 Pointer vs. Array Multi-dimensi


Pendatang baru ke C terkadang bingung tentang perbedaan antara dua dimensi
array dan array pointer, seperti nama pada contoh di atas. Diberi definisi

int a [10] [20];


int * b [10];
maka a [3] [4] dan b [3] [4] keduanya adalah referensi hukum yang sah secara sintaksis untuk satu int . Tapi a adalah a
array dua dimensi yang benar: 200 lokasi int- ukuran telah disisihkan, dan konvensional
perhitungan subskrip persegi panjang 20 * baris + col digunakan untuk menemukan elemen [baris, col] . Untuk b ,
Namun, definisi hanya mengalokasikan 10 petunjuk dan tidak menginisialisasi mereka; inisialisasi
harus dilakukan secara eksplisit, baik secara statis atau dengan kode. Dengan asumsi bahwa setiap elemen b tidak
arahkan ke array dua puluh elemen, maka akan ada 200 int disisihkan, ditambah sepuluh sel untuk
pointer. Keuntungan penting dari array pointer adalah bahwa barisan array mungkin berasal
panjang yang berbeda. Artinya, setiap elemen b tidak perlu menunjuk ke vektor dua puluh elemen; beberapa
dapat menunjuk ke dua elemen, beberapa sampai lima puluh, dan beberapa tidak sama sekali.
Meskipun kami telah mengutarakan diskusi ini dalam hal bilangan bulat, sejauh ini penggunaan yang paling sering
array pointer adalah untuk menyimpan string karakter dengan panjang yang beragam, seperti pada fungsinya
nama_bulan . Bandingkan deklarasi dan gambar untuk array pointer:

char * name [] = {"Bulan ilegal", "Jan", "Feb", "Mar"};

Halaman 94

95
dengan yang untuk array dua dimensi:

char aname [] [15] = {"Bulan ilegal", "Jan", "Feb", "Mar"};


Latihan 5-9. Tulis ulang rutinitas day_of_year dan month_day dengan pointer alih-alih
pengindeksan.

5.10 Argumen baris perintah


Di lingkungan yang mendukung C, ada cara untuk melewatkan argumen atau parameter baris perintah
ke program ketika mulai dijalankan. Ketika main dipanggil, ia disebut dengan dua argumen.
Yang pertama (secara konvensional disebut argc , untuk jumlah argumen) adalah jumlah baris perintah
argumen yang digunakan oleh program; yang kedua ( argv , untuk vector argumen) adalah sebuah pointer
ke array string karakter yang berisi argumen, satu per string. Kami biasanya menggunakan
beberapa level pointer untuk memanipulasi string karakter ini.
Ilustrasi paling sederhana adalah gema program , yang menggemakan argumen command-line-nya pada a
satu baris, dipisahkan oleh bagian yang kosong. Itulah perintahnya

gema halo, dunia


mencetak hasilnya

Halo Dunia
Dengan konvensi, argv [0] adalah nama yang digunakan oleh program, jadi argc setidaknya 1.
Jika argc adalah 1, tidak ada argumen baris perintah setelah nama program. Dalam contoh
di atas, argc adalah 3, dan argv [0] , argv [1] , dan argv [2] adalah "echo" , "hello," , dan "world"
masing-masing. Argumen opsional pertama adalah argv [1] dan yang terakhir adalah argv [argc-1] ;
selain itu, standar mengharuskan argv [argc] menjadi pointer nol.

Versi pertama dari echo memperlakukan argv sebagai array dari pointer karakter:

#termasuk <stdio.h>

/ * argumen baris perintah gema; Versi 1 * /


main (int argc, char * argv [])
{
int i;

untuk (i = 1; i <argc; i ++)


printf ("% s% s", argv [i], (i <argc-1)? "": "");
printf ("\ n");
return 0;

Halaman 95

96
}
Karena argv adalah pointer ke array pointer, kita dapat memanipulasi pointer daripada indeks
array. Varian berikutnya didasarkan pada incvementing argv , yang merupakan pointer ke pointer
char , sedangkan argc dihitung mundur:

#termasuk <stdio.h>

/ * argumen baris perintah gema; Versi 2 * /


main (int argc, char * argv [])
{
while (--argc> 0)
printf ("% s% s", * ++ argv, (argc> 1)? "": "");
printf ("\ n");
return 0;
}
Karena argv adalah penunjuk ke awal array string argumen, menambahkannya dengan 1
( ++ argv ) menunjukkannya pada argv asli [1] alih-alih argv [0] . Masing-masing berturut-turut
increment memindahkannya ke argumen berikutnya; * Argv kemudian adalah pointer ke argumen itu. Di
pada saat yang sama, argc dikurangi; ketika menjadi nol, tidak ada argumen yang tersisa
mencetak.
Atau, kita bisa menulis pernyataan printf sebagai

printf ((argc> 1)? "% s": "% s", * ++ argv);


Ini menunjukkan bahwa format argumen printf juga bisa berupa ekspresi.
Sebagai contoh kedua, mari kita buat beberapa peningkatan untuk program pencarian pola dari
Bagian 4.1 . Jika Anda ingat, kami menghubungkan pola pencarian jauh ke dalam program, jelas
pengaturan yang tidak memuaskan. Mengikuti jejak grep program UNIX , mari kita tingkatkan
program sehingga pola yang akan dicocokkan ditentukan oleh argumen pertama pada baris perintah.

#termasuk <stdio.h>
#termasuk <string.h>
#define MAXLINE 1000

int getline (char * line, int max);

/ * find: mencetak garis yang cocok dengan pola dari 1st arg * /
main (int argc, char * argv [])
{
baris char [MAXLINE];
int ditemukan = 0;

if (argc! = 2)
printf ("Penggunaan: temukan pola \ n");
lain
while (getline (line, MAXLINE)> 0)
if (strstr (line, argv [1])! = NULL) {
printf ("% s", line);
ditemukan ++;
}
kembali ditemukan;
}
Strstr fungsi pustaka standar (s, t) mengembalikan pointer ke kemunculan pertama
string t dalam string s , atau NULL jika tidak ada. Ini dideklarasikan dalam <string.h> .
Model sekarang dapat dielaborasi untuk menggambarkan konstruksi pointer lebih lanjut. Misalkan kita mau
untuk memungkinkan dua argumen opsional. Seseorang mengatakan `` cetak semua baris kecuali yang cocok dengan
pola; '' yang kedua mengatakan `` mendahului setiap baris yang dicetak dengan nomor barisnya. ''

Halaman 96

97
Konvensi umum untuk program C pada sistem UNIX adalah argumen yang dimulai dengan
tanda minus memperkenalkan flag atau parameter opsional. Jika kita memilih -x (untuk `` kecuali '') untuk memberi sinyal
inversi, dan -n (`` number '') untuk meminta penomoran baris, lalu perintah

temukan pola -x -n
akan mencetak setiap baris yang tidak cocok dengan pola, didahului dengan nomor barisnya.
Argumen opsional harus diizinkan dalam urutan apa pun, dan sisanya dari program harus
terlepas dari jumlah argumen yang kami sajikan. Selain itu, nyaman untuk
pengguna jika argumen opsi dapat digabungkan, seperti dalam

temukan pola -nx


Inilah programnya:
#termasuk <stdio.h>
#termasuk <string.h>
#define MAXLINE 1000

int getline (char * line, int max);

/ * find: mencetak garis yang cocok dengan pola dari 1st arg * /
main (int argc, char * argv [])
{
baris char [MAXLINE];
long lineno = 0;
int c, kecuali = 0, angka = 0, ditemukan = 0;

while (--argc> 0 && (* ++ argv) [0] == '-')


while (c = * ++ argv [0])
beralih (c) {
huruf 'x':
kecuali = 1;
istirahat;
huruf 'n':
angka = 1;
istirahat;
default:
printf ("temukan: opsi ilegal% c \ n", c);
argc = 0;
ditemukan = -1;
istirahat;
}
if (argc! = 1)
printf ("Penggunaan: temukan -x -n pola \ n");
lain
while (getline (line, MAXLINE)> 0) {
lineno ++;
if ((strstr (line, * argv)! = NULL)! = kecuali) {
jika (angka)
printf ("% ld:", lineno);
printf ("% s", line);
ditemukan ++;
}
}
kembali ditemukan;
}
argc dikurangi dan argv ditambahkan sebelum setiap argumen opsional. Di akhir
loop, jika tidak ada kesalahan, argc memberi tahu berapa banyak argumen tetap tidak diproses dan argv
menunjuk ke yang pertama dari ini. Jadi argc harus 1 dan * argv harus menunjuk pada pola.
Perhatikan bahwa * ++ argv adalah pointer ke string argumen, jadi (* ++ argv) [0] adalah karakter pertamanya.
(Bentuk valid alternatif adalah ** ++ argv .) Karena [] mengikat lebih ketat dari * dan ++ , the
tanda kurung diperlukan; tanpa mereka ekspresi akan diambil sebagai * ++ (argv [0]) . Di

Halaman 97

98
sebenarnya, itulah yang kami gunakan di loop dalam, di mana tugasnya adalah berjalan di sepanjang spesifik
argumen string. Di loop dalam, ekspresi * ++ argv [0] menambah pointer
argv [0] !
Jarang seseorang menggunakan ekspresi pointer lebih rumit dari ini; dalam beberapa kasus,
membaginya menjadi dua atau tiga langkah akan lebih intuitif.

Latihan 5-10. Tulis expr program , yang mengevaluasi ekspresi Polandia terbalik dari
baris perintah, di mana setiap operator atau operan adalah argumen yang terpisah. Sebagai contoh,

expr 2 3 4 + *
mengevaluasi 2 * (3 + 4).
Latihan 5-11. Memodifikasi program entab dan detab (ditulis sebagai latihan dalam Bab 1 ) ke
menerima daftar berhenti tab sebagai argumen. Gunakan pengaturan tab default jika tidak ada argumen.

Latihan 5-12. Perpanjang entab dan detab untuk menerima steno

entab -m + n
berarti tab berhenti setiap n kolom, mulai dari kolom m . Pilih nyaman (untuk pengguna)
perilaku standar.
Latihan 5-13. Tulis ekor program , yang mencetak n baris terakhir inputnya. Secara default, n
diatur ke 10, katakanlah, tetapi dapat diubah dengan argumen opsional sehingga

ekor -n
mencetak n baris terakhir . Program harus berperilaku rasional tidak peduli seberapa tidak masuk akalnya
input atau nilai n . Tuliskan programnya agar memanfaatkan penyimpanan yang tersedia sebaik-baiknya; garis
harus disimpan seperti dalam program penyortiran Bagian 5.6 , bukan dalam array dua dimensi
ukuran tetap.

5.11 Pointer ke Fungsi


Dalam C, fungsi itu sendiri bukan variabel, tetapi dimungkinkan untuk menentukan pointer ke fungsi, yang
dapat ditugaskan, ditempatkan dalam array, diteruskan ke fungsi, dikembalikan oleh fungsi, dan sebagainya. Kita
akan menggambarkan ini dengan memodifikasi prosedur penyortiran yang ditulis sebelumnya dalam bab ini sehingga jika
argumen opsional -n diberikan, ia akan mengurutkan garis input secara numerik alih-alih
leksikografis.
Semacam sering terdiri dari tiga bagian - perbandingan yang menentukan pemesanan setiap pasangan
objek, pertukaran yang membalik urutannya, dan algoritma penyortiran yang membuat perbandingan
dan bertukar sampai benda-benda berada dalam urutan. Algoritma pengurutan tidak tergantung pada
operasi perbandingan dan pertukaran, jadi dengan melewati perbandingan dan pertukaran yang berbeda
fungsinya, kita dapat mengatur untuk mengurutkan berdasarkan kriteria yang berbeda. Ini adalah pendekatan yang diambil di kami
jenis baru.

Perbandingan leksikografis dua garis dilakukan oleh strcmp , seperti sebelumnya; kita juga akan membutuhkan
numcmp rutin yang membandingkan dua baris berdasarkan nilai numerik dan mengembalikan yang sama
semacam indikasi kondisi seperti strcmp . Fungsi-fungsi ini dideklarasikan di depan utama dan a
pointer ke yang sesuai diteruskan ke qsort . Kami telah berhemat pada pemrosesan kesalahan untuk
argumen, sehingga dapat berkonsentrasi pada masalah utama.
#termasuk <stdio.h>
#termasuk <string.h>

#define MAXLINES 5000 / * maks #lines untuk disortir * /


char * lineptr [MAXLINES]; / * pointer ke baris teks * /

Halaman 98

99
int readlines (char * lineptr [], int nlines);
membatalkan penulisan (char * lineptr [], int nlines);

membatalkan qsort (void * lineptr [], int left, int right,


int (* comp) (void *, void *));
int numcmp (char *, char *);

/ * urutkan jalur input * /


main (int argc, char * argv [])
{
int nlines; / * jumlah baris input baca * /
int numeric = 0; / * 1 jika jenis numerik * /

if (argc> 1 && strcmp (argv [1], "-n") == 0)


numerik = 1;
if ((nlines = readlines (lineptr, MAXLINES))> = 0) {
qsort ((batal **) lineptr, 0, nlines-1,
(int (*) (void *, void *)) (numerik? numcmp: strcmp));
writelines (lineptr, nlines);
return 0;
} lain {
printf ("masukan terlalu besar untuk disortir \ n");
return 1;
}
}
Dalam panggilan ke qsort , strcmp dan numcmp adalah alamat fungsi. Karena mereka diketahui
menjadi fungsi, & tidak diperlukan, dengan cara yang sama tidak diperlukan sebelum nama array.
Kami telah menulis qsort sehingga dapat memproses semua tipe data, bukan hanya string karakter. Seperti yang ditunjukkan
berdasarkan prototipe fungsi, qsort mengharapkan array pointer, dua integer, dan sebuah fungsi
dengan dua argumen pointer. Jenis pointer generik void * digunakan untuk pointer
argumen. Setiap pointer dapat dilemparkan untuk membatalkan * dan kembali lagi tanpa kehilangan informasi, jadi
kita dapat memanggil qsort dengan melemparkan argumen untuk membatalkan * . Pemain rumit fungsi
Argumen melemparkan argumen fungsi perbandingan. Ini umumnya tidak akan berpengaruh
pada representasi aktual, tetapi meyakinkan kompiler bahwa semuanya baik-baik saja.

/ * qsort: urutkan v [kiri] ... v [kanan] ke urutan yang meningkat * /


membatalkan qsort (void * v [], int kiri, int kanan,
int (* comp) (void *, void *))
{
int i, last;

void swap (void * v [], int, int);

if (kiri> = kanan) / * tidak melakukan apa-apa jika array mengandung * /


kembali; / * kurang dari dua elemen * /
swap (v, kiri, (kiri + kanan) / 2);
terakhir = kiri;
untuk (i = kiri + 1; i <= kanan; i ++)
if ((* comp) (v [i], v [kiri]) <0)
swap (v, ++ terakhir, i);
swap (v, kiri, terakhir);
qsort (v, kiri, terakhir-1, comp);
qsort (v, last 1, right, comp);
}
Deklarasi harus dipelajari dengan hati-hati. Parameter keempat dari qsort adalah

int (* comp) (void *, void *)


yang mengatakan bahwa comp adalah pointer ke fungsi yang memiliki dua argumen * kosong dan mengembalikan sebuah
int .
Penggunaan comp di baris

if ((* comp) (v [i], v [kiri]) <0)

Halaman 99

100
konsisten dengan deklarasi: comp adalah penunjuk ke fungsi, * comp adalah fungsi, dan
(* comp) (v [i], v [kiri])
adalah panggilan untuk itu. Tanda kurung diperlukan agar komponen terkait dengan benar;
tanpa mereka,
int * comp (batal *, batal *) / * SALAH * /
mengatakan bahwa comp adalah fungsi yang mengembalikan pointer ke int , yang sangat berbeda.
Kami telah menunjukkan strcmp , yang membandingkan dua string. Inilah numcmp , yang
membandingkan dua string pada nilai numerik terkemuka, dihitung dengan memanggil atof :

#termasuk <stdlib.h>

/ * numcmp: bandingkan s1 dan s2 secara numerik * /


int numcmp (char * s1, char * s2)
{
v1 ganda, v2;

v1 = atof (s1);
v2 = Atof (s2);
jika (v1 <v2)
return -1;
lain jika (v1> v2)
return 1;
lain
return 0;
}
Fungsi swap , yang bertukar dua pointer, identik dengan apa yang kami sajikan sebelumnya
bab, kecuali bahwa deklarasi diubah menjadi batal * .

membatalkan swap (void * v [], int i, int j;)


{
batal * temp;

temp = v [i];
v [i] = v [j];
v [j] = temp;
}
Berbagai opsi lain dapat ditambahkan ke program pengurutan; beberapa membuat tantangan
latihan.
Latihan 5-14. Ubah program pengurutan untuk menangani tanda -r , yang menunjukkan pengurutan secara terbalik
(menurun) pesanan. Pastikan bahwa -r bekerja dengan -n .

Latihan 5-15. Tambahkan opsi -f untuk melipat huruf besar dan kecil bersama-sama, sehingga huruf besar
perbedaan tidak dibuat selama penyortiran; misalnya, a dan A bandingkan sama.

Latihan 5-16. Tambahkan opsi -d (`` urutan direktori ''), yang membuat perbandingan hanya aktif
huruf, angka, dan kosong. Pastikan itu bekerja sama dengan -f .

Latihan 5-17. Tambahkan kemampuan pencarian bidang, jadi penyortiran mungkin dilakukan pada bidang dalam garis,
setiap bidang diurutkan sesuai dengan serangkaian opsi independen. (Indeks untuk buku ini adalah
diurutkan dengan -df untuk kategori indeks dan -n untuk nomor halaman.)

5.12 Deklarasi Rumit


C kadang-kadang dikecam untuk sintaks deklarasi, terutama yang melibatkan
pointer ke fungsi. Sintaks adalah upaya untuk membuat deklarasi dan penggunaannya setuju; Itu
berfungsi dengan baik untuk kasus-kasus sederhana, tetapi dapat membingungkan untuk yang lebih sulit, karena deklarasi
tidak dapat dibaca dari kiri ke kanan, dan karena tanda kurung terlalu banyak digunakan. Perbedaan antara

Halaman 100

101

int * f (); / * f: berfungsi mengembalikan pointer ke int * /


dan
int (* pf) (); / * pf: pointer berfungsi mengembalikan int * /
mengilustrasikan masalah: * adalah operator awalan dan memiliki prioritas lebih rendah dari () , jadi
tanda kurung diperlukan untuk memaksa hubungan yang tepat.
Meskipun deklarasi yang benar-benar rumit jarang muncul dalam praktik, penting untuk mengetahui caranya
memahaminya, dan, jika perlu, cara membuatnya. Satu cara yang baik untuk mensintesis
deklarasi dalam langkah-langkah kecil dengan typedef , yang dibahas dalam Bagian 6.7 . Sebagai
alternatif, di bagian ini kami akan menyajikan sepasang program yang mengkonversi dari C yang valid ke a
deskripsi kata dan kembali lagi. Deskripsi kata dibaca dari kiri ke kanan.

Yang pertama, dcl , adalah yang lebih kompleks. Itu mengubah pernyataan C menjadi deskripsi kata, seperti pada
contoh-contoh ini:

char ** argv
argv: pointer ke char
int (* daytab) [13]
daytab: pointer ke array [13] int
int * daytab [13]
daytab: array [13] dari pointer ke int
batal * comp ()
comp: berfungsi mengembalikan pointer ke void
batal (* comp) ()
comp: pointer berfungsi mengembalikan kekosongan
char (* (* x ()) []) ()
x: fungsi mengembalikan pointer ke array [] dari
pointer ke fungsi mengembalikan char
char (* (* x [3]) ()) [5]
x: array [3] pointer berfungsi mengembalikan
arahkan ke array [5] dari char
dcldidasarkan pada tata bahasa yang menentukan deklarator, yang dijabarkan dengan tepat di
Lampiran A, Bagian 8.5 ; ini adalah bentuk yang disederhanakan:

dcl: direct-dcl opsional *


nama direct-dcl
(dcl)
direct-dcl ()
direct-dcl [ukuran opsional]

Dengan kata lain, dcl adalah direct-dcl , mungkin didahului oleh *. Sebuah langsung dcl adalah nama, atau
dcl tanda kurung , atau dcl langsung diikuti oleh tanda kurung, atau dcl langsung diikuti oleh tanda kurung
dengan ukuran opsional.
Tata bahasa ini bisa digunakan untuk mengurai fungsi. Sebagai contoh, pertimbangkan deklarator ini:

(* pfa []) ()
pfa akan diidentifikasi sebagai nama dan dengan demikian sebagai direct-dcl . Kemudian pfa [] juga merupakan direct-dcl . Kemudian
* pfa [] dikenali sebagai dcl , jadi (* pfa []) adalah direct-dcl . Maka (* pfa []) () adalah direct-dcl
dan dengan demikian dcl . Kita juga dapat menggambarkan parse dengan pohon seperti ini (di mana direct-dcl berada
disingkat menjadi dir-dcl ):

Halaman 101

102

Inti dari program dcl adalah sepasang fungsi, dcl dan dirdcl , yang mengurai deklarasi
menurut tata bahasa ini. Karena tata bahasa didefinisikan secara rekursif, fungsi memanggil masing-masing
lainnya secara rekursif karena mereka mengakui potongan-potongan deklarasi; program ini disebut rekursif-
parser keturunan.
/ * dcl: parse declarator * /
batal dcl (batal)
{
int ns;

untuk (ns = 0; gettoken () == '*';) / * count * 's * /


ns ++;
dirdcl ();
while (ns--> 0)
strcat (out, "pointer to");
}

/ * dirdcl: parsing sebuah deklarator langsung * /


void dirdcl (void)
{
tipe int;

if (tokentype == '(') { / * (dcl) * /


dcl ();
if (tokentype! = ')')
printf ("error: missing) \ n");
} lain jika (tokenype == NAME) / * nama variabel * /
strcpy (nama, token);
lain
printf ("kesalahan: nama yang diharapkan atau (dcl) \ n");
while ((type = gettoken ()) == PARENS || type == BRACKETS)

Halaman 102

103
if (type == PARENS)
strcat (keluar, "fungsi kembali");
lain {
strcat (out, "array");
strcat (out, token);
strcat (out, "of");
}
}
Karena program-program ini dimaksudkan sebagai ilustrasi, bukan anti peluru, ada beberapa yang signifikan
pembatasan dcl . Itu hanya dapat menangani char tipe data sederhana atau int . Itu tidak menangani
tipe argumen dalam fungsi, atau kualifikasi seperti const . Kosong palsu membingungkannya. Itu tidak berhasil
banyak pemulihan kesalahan, sehingga deklarasi yang tidak valid juga akan membingungkannya. Perbaikan ini dibiarkan
sebagai latihan.
Berikut adalah variabel global dan rutinitas utama:

#termasuk <stdio.h>
#termasuk <string.h>
#termasuk <ctype.h>

#define MAXTOKEN 100

enum {NAME, PARENS, BRACKETS};

batal dcl (batal);


void dirdcl (void);

int gettoken (batal);


intententype; / * jenis token terakhir * /
token char [MAXTOKEN]; / * string token terakhir * /
nama char [MAXTOKEN]; / * nama pengenal * /
char datatype [MAXTOKEN]; / * tipe data = char, int, dll. * /
hangus [1000];

main () / * konversikan deklarasi menjadi kata-kata * /


{
while (gettoken ()! = EOF) {/ * Token pertama pada baris * /
strcpy (datatype, token); / * adalah tipe data * /
out [0] = '\ 0';
dcl (); / * parse sisa baris * /
if (tokentype! = '\ n')
printf ("kesalahan sintaks \ n");
printf ("% s:% s% s \ n", nama, keluar, tipe data);
}
return 0;
}
Fungsi gettoken melompati blank dan tab, kemudian menemukan token berikutnya dalam input; a `` token ''
adalah nama, sepasang tanda kurung, sepasang tanda kurung yang mungkin termasuk angka, atau lainnya
karakter tunggal.

int gettoken (void) / * kembali token berikutnya * /


{
int c, getch (void);
membatalkan ungetch (int);
char * p = token;
while ((c = getch ()) == '' || c == '\ t')
;
if (c == '(') {
if ((c = getch ()) == ')') {
strcpy (token, "()");
return tokentype = PARENS;
} lain {
ungetch (c);
return tokentype = '(';

Halaman 103

104
}
} lain jika (c == '[') {
untuk (* p ++ = c; (* p ++ = getch ())! = ']';)
;
* p = '\ 0';
return tokentype = BRACKETS;
} lain jika (isalpha (c)) {
untuk (* p ++ = c; isalnum (c = getch ());)
* p ++ = c;
* p = '\ 0';
ungetch (c);
return tokentype = NAME;
} lain
return tokenype = c;

}
getch dan ungetch dibahas dalam Bab 4 .
Pergi ke arah lain lebih mudah, terutama jika kita tidak khawatir tentang menghasilkan berlebihan
tanda kurung. Program undcl mengonversi deskripsi kata seperti `` x adalah fungsi yang mengembalikan a
pointer ke array pointer ke fungsi yang mengembalikan char , '' yang akan kita nyatakan sebagai

x () * [] * () char
untuk
char (* (* x ()) []) ()
Sintaks input yang disingkat memungkinkan kita menggunakan kembali fungsi gettoken . undcl juga menggunakan hal yang sama
variabel eksternal seperti dcl .

/ * undcl: konversikan deskripsi kata menjadi deklarasi * /


utama()
{
tipe int;
char temp [MAXTOKEN];

while (gettoken ()! = EOF) {


strcpy (keluar, token);
while ((type = gettoken ())! = '\ n')
if (type == PARENS || type == BRACKETS)
strcat (out, token);
lain jika (ketik == '*') {
sprintf (temp, "(*% s)", out);
strcpy (keluar, temp);
} lain jika (ketik == NAME) {
sprintf (temp, "% s% s", token, out);
strcpy (keluar, temp);
} lain
printf ("input tidak valid di% s \ n", token);
}
return 0;
}
Latihan 5-18. Buat dcl pulih dari kesalahan input.
Latihan 5-19. Ubah undcl sehingga tidak menambahkan tanda kurung yang berlebihan ke deklarasi.

Latihan 5-20. Luaskan dcl untuk menangani deklarasi dengan tipe argumen fungsi, seperti kualifikasi
const , dan sebagainya.

Halaman 104

105
Bab 6 - Struktur
Struktur adalah kumpulan dari satu atau lebih variabel, mungkin dari tipe yang berbeda, dikelompokkan
bersama di bawah satu nama untuk penanganan yang mudah. (Struktur disebut `` catatan '' dalam beberapa
bahasa, terutama Pascal.) Struktur membantu mengatur data yang rumit, terutama dalam jumlah besar
program, karena mereka mengizinkan sekelompok variabel terkait untuk diperlakukan sebagai unit bukan sebagai
entitas yang terpisah.
Salah satu contoh tradisional dari suatu struktur adalah catatan penggajian: seorang karyawan dideskripsikan dengan satu set
atribut seperti nama, alamat, nomor jaminan sosial, gaji, dll. Beberapa di antaranya pada gilirannya
bisa berupa struktur: nama memiliki beberapa komponen, seperti halnya alamat dan bahkan gaji.
Contoh lain, lebih tipikal untuk C, berasal dari grafik: suatu titik adalah pasangan koordinat, a
persegi panjang adalah sepasang poin, dan seterusnya.

Perubahan utama yang dibuat oleh standar ANSI adalah untuk menentukan penugasan struktur - struktur mungkin
disalin dan ditugaskan, diteruskan ke fungsi, dan dikembalikan oleh fungsi. Ini sudah
didukung oleh sebagian besar kompiler selama bertahun-tahun, tetapi properti sekarang didefinisikan dengan tepat.
Struktur dan susunan otomatis sekarang juga dapat diinisialisasi.

6.1 Dasar-Dasar Struktur


Mari kita buat beberapa struktur yang cocok untuk grafik. Objek dasar adalah sebuah titik, yang kita inginkan
anggap memiliki koordinat x dan koordinat y , keduanya bilangan bulat.

Dua komponen dapat ditempatkan dalam struktur yang dideklarasikan seperti ini:

titik struct {
int x;
int y;
};
Kata kunci struct memperkenalkan deklarasi struktur, yang merupakan daftar deklarasi terlampir
di kawat gigi. Nama opsional yang disebut tag struktur dapat mengikuti kata struct (seperti pada poin
sini). Tag memberi nama struktur semacam ini, dan selanjutnya dapat digunakan sebagai singkatan
bagian dari deklarasi di kawat gigi.
Variabel yang disebutkan dalam struktur disebut anggota . Anggota atau tag struktur dan
variabel biasa (yaitu, non-anggota) dapat memiliki nama yang sama tanpa konflik, karena mereka dapat
selalu dibedakan berdasarkan konteks. Selanjutnya, nama anggota yang sama dapat muncul di
struktur yang berbeda, meskipun sebagai gaya biasanya orang akan menggunakan nama yang sama saja
untuk benda yang terkait erat.

Sebuah struct deklarasi mendefinisikan tipe. Penjepit kanan yang mengakhiri daftar anggota mungkin
diikuti oleh daftar variabel, sama seperti untuk semua tipe dasar. Itu adalah,

Halaman 105

106

struct {...} x, y, z;
secara analogis dengan

int x, y, z;
dalam arti bahwa setiap pernyataan menyatakan x , y dan z sebagai variabel dari tipe dan nama
menyebabkan ruang disisihkan untuk mereka.
Deklarasi struktur yang tidak diikuti oleh daftar variabel tidak menyimpan; itu hanya
menjelaskan templat atau bentuk struktur. Namun, jika deklarasi ditandai, tag dapat
digunakan nanti dalam definisi instance dari struktur. Misalnya, diberi deklarasi
poin di atas,

pt titik struct;
mendefinisikan pt variabel yang merupakan struktur dari tipe struct point . Struktur dapat diinisialisasi
dengan mengikuti definisinya dengan daftar inisialisasi, masing-masing ekspresi konstan, untuk
anggota:
struct maxpt = {320, 200};
Struktur otomatis juga dapat diinisialisasi dengan penugasan atau dengan memanggil fungsi itu
mengembalikan struktur tipe yang tepat.
Seorang anggota struktur tertentu disebut dalam ekspresi oleh konstruksi formulir

struktur-nama

Operator anggota struktur ``. '' Menghubungkan nama struktur dan nama anggota. Untuk
cetak koordinat titik pt , misalnya,

printf ("% d,% d", pt.x, pt.y);


atau untuk menghitung jarak dari titik asal (0,0) ke pt ,

double dist, sqrt (double);

dist = sqrt ((double) pt.x * pt.x + (double) pt.y * pt.y);


Struktur dapat bersarang. Salah satu representasi dari persegi panjang adalah sepasang titik yang menunjukkan
sudut diagonal berlawanan:

struct rect {
titik struct pt1;
titik struct pt2;
};
The rect struktur berisi dua titik struktur. Jika kami menyatakan layar sebagai

layar persegi struct;


kemudian

screen.pt1.x

Halaman 106

107
merujuk ke koordinat x dari pt1 anggota layar .

6.2 Struktur dan Fungsi


Satu-satunya operasi hukum pada suatu struktur adalah menyalin atau menugaskannya sebagai suatu unit, mengambilnya
alamat dengan & , dan mengakses anggotanya. Salinan dan tugas termasuk melewati argumen ke
fungsi dan mengembalikan nilai dari fungsi juga. Struktur tidak dapat dibandingkan. SEBUAH
struktur dapat diinisialisasi dengan daftar nilai anggota yang konstan; struktur otomatis mungkin
juga diinisialisasi dengan penugasan.
Mari kita selidiki struktur dengan menulis beberapa fungsi untuk memanipulasi titik dan persegi panjang.
Setidaknya ada tiga pendekatan yang mungkin: lewati komponen secara terpisah, lewati keseluruhan
struktur, atau meneruskan pointer ke sana. Masing-masing memiliki poin baik dan buruk.

Fungsi pertama, makepoint , akan mengambil dua bilangan bulat dan mengembalikan struktur titik :

/ * makepoint: membuat poin dari komponen x dan y * /


makepoint titik struct (int x, int y)
{
suhu titik struct;

temp.x = x;
temp.y = y;
temp kembali;
}
Perhatikan bahwa tidak ada konflik antara nama argumen dan anggota yang sama
nama; memang penggunaan kembali nama menekankan hubungan.
makepoint sekarang dapat digunakan untuk menginisialisasi setiap struktur secara dinamis, atau untuk menyediakan struktur
argumen ke suatu fungsi:

layar persegi struct;


titik struct tengah;
makepoint titik struct (int, int);
screen.pt1 = makepoint (0,0);
screen.pt2 = makepoint (XMAX, YMAX);
middle = makepoint ((screen.pt1.x + screen.pt2.x) / 2,
(screen.pt1.y + screen.pt2.y) / 2);
Langkah selanjutnya adalah serangkaian fungsi untuk melakukan aritmatika pada poin. Contohnya,

/ * addpoints: tambahkan dua poin * /


struct addpoint (struct point p1, struct point p2)
{
p1.x + = p2.x;
p1.y + = p2.y;
kembali p1;
}
Di sini argumen dan nilai kembalian adalah struktur. Kami menambah komponen
di p1 daripada menggunakan variabel sementara eksplisit untuk menekankan parameter struktur itu
dilewatkan oleh nilai seperti yang lainnya.
Sebagai contoh lain, fungsi ptinrect menguji apakah suatu titik berada di dalam persegi panjang, di mana
kami telah mengadopsi konvensi bahwa persegi panjang termasuk sisi kiri dan bawahnya tetapi tidak
sisi atas dan kanan:

/ * benar: kembalikan 1 jika p dalam r, 0 jika tidak * /


int ptinrect (titik struct p, struct rect r)
{
return px> = r.pt1.x && px <r.pt2.x
&& py> = r.pt1.y && py <r.pt2.y;
}

Halaman 107

108
Ini mengasumsikan bahwa persegi panjang disajikan dalam bentuk standar di mana koordinat pt1 berada
kurang dari koordinat pt2 . Fungsi berikut mengembalikan persegi panjang yang dijamin berada di
bentuk kanonik:

#define min (a, b) ((a) <(b)? (a): (b))


#define max (a, b) ((a)> (b)? (a): (b))

/ * canonrect: mengkanoniskan koordinat persegi panjang * /


struct rect canonrect (struct rect r)
{
struct rect temp;

temp.pt1.x = min (r.pt1.x, r.pt2.x);


temp.pt1.y = min (r.pt1.y, r.pt2.y);
temp.pt2.x = maks (r.pt1.x, r.pt2.x);
temp.pt2.y = max (r.pt1.y, r.pt2.y);
temp kembali;
}
Jika sebuah struktur besar akan diteruskan ke suatu fungsi, umumnya lebih efisien untuk melewatkan sebuah pointer
daripada menyalin seluruh struktur. Pointer struktur seperti pointer ke variabel biasa.
Deklarasi

titik struct * pp;


mengatakan bahwa pp adalah pointer ke struktur tipe struct point . Jika poin pp ke titik
struktur, * pp adalah struktur, dan (* pp) .x dan (* pp) .y adalah anggota. Untuk menggunakan pp , kami
mungkin menulis, misalnya,
asal titik struct, * pp;

pp = & asal;
printf ("asal adalah (% d,% d) \ n", (* pp) .x, (* pp) .y);
Tanda kurung diperlukan dalam (* pp) .x karena diutamakan anggota struktur
operator . lebih tinggi dari * . Ekspresi * pp.x berarti * (pp.x) , yang ilegal di sini
karena x bukan pointer.
Pointer ke struktur sangat sering digunakan sehingga notasi alternatif disediakan sebagai
steno. Jika p adalah pointer ke struktur, maka

p-> anggota-struktur
mengacu pada anggota tertentu. Jadi kita bisa menulis saja

printf ("asal adalah (% d,% d) \ n", pp-> x, pp-> y);


Kedua . dan -> kaitkan dari kiri ke kanan, jadi jika sudah
struct rect r, * rp = & r;
maka keempat ekspresi ini setara:

r.pt1.x
rp-> pt1.x
(r.pt1) .x
(rp-> pt1) .x
Operator struktur . dan -> , bersama dengan () untuk panggilan fungsi dan [] untuk subskrip, adalah
di bagian atas hierarki diutamakan dan dengan demikian mengikat sangat erat. Misalnya, diberikan
pernyataan

struct {
int len;
char * str;
} * p;
kemudian

Halaman 108

109
++ p-> len
kenaikan len , bukan p , karena tanda kurung tersirat adalah ++ (p-> len) . Kurung bisa
digunakan untuk mengubah penjilidan: (++ p) -> len kenaikan p sebelum mengakses len , dan (p ++) -> len
kenaikan p sesudahnya. (Set kurung terakhir ini tidak perlu.)
Dengan cara yang sama, * p-> str mengambil apa pun yang ditunjukkan str ; * p-> str ++ bertahap str setelah
mengakses apa pun yang ditunjukkannya (seperti * s ++ ); (* p-> str) ++ meningkatkan apa pun poin str
untuk; dan * p ++ -> peningkatan str p setelah mengakses str poin apa pun yang ditunjukkan.

6.3 Susunan Struktur


Pertimbangkan menulis program untuk menghitung kemunculan setiap kata kunci C. Kami membutuhkan array
string karakter untuk menahan nama, dan array bilangan bulat untuk penghitungan. Satu kemungkinan adalah
untuk menggunakan dua array paralel, kata kunci dan keycount , seperti pada

kata kunci char * [NKEYS];


int keycount [NKEYS];
Tetapi fakta bahwa array adalah paralel menunjukkan organisasi yang berbeda, sebuah array
struktur. Setiap kata kunci adalah pasangan:
char * word;
int cout;
dan ada berbagai pasangan. Deklarasi struktur
kunci struct {
char * word;
int count;
} keytab [NKEYS];
mendeklarasikan kunci tipe struktur , mendefinisikan keytab array struktur tipe ini, dan menyisihkan
penyimpanan untuk mereka. Setiap elemen array adalah struktur. Ini juga bisa ditulis

kunci struct {
char * word;
int count;
};

struct key keytab [NKEYS];


Karena keytab struktur berisi sekumpulan nama yang konstan, paling mudah menjadikannya eksternal
variabel dan inisialisasi sekali dan untuk semua ketika didefinisikan. Inisialisasi struktur adalah
analog dengan yang sebelumnya - definisi diikuti oleh daftar inisialisasi yang terlampir dalam kurung:

kunci struct {
char * word;
int count;
} keytab [] = {
"otomatis", 0,
"istirahat", 0,
"case", 0,
"char", 0,
"const", 0,
"lanjutkan", 0,
"default", 0,
/ * ... * /
"tidak ditandatangani", 0,
"batal", 0,
"mudah menguap", 0,
"while", 0
};
Inisialisasi terdaftar dalam pasangan yang sesuai dengan anggota struktur. Itu akan lebih
tepat untuk melampirkan inisialisasi untuk setiap "baris" atau struktur dalam kawat gigi, seperti pada

{"auto", 0},

Halaman 109
110
{"break", 0},
{"case", 0},
...
tetapi kawat gigi bagian dalam tidak diperlukan ketika inisialisasi adalah variabel sederhana atau string karakter,
dan ketika semua hadir. Seperti biasa, jumlah entri dalam larik keytab akan dihitung
jika inisialisasi ada dan [] dibiarkan kosong.
Program penghitungan kata kunci dimulai dengan definisi keytab . Rutinitas utama berbunyi
input dengan berulang kali memanggil fungsi getword yang mengambil satu kata setiap kali. Setiap kata
terlihat di keytab dengan versi fungsi pencarian biner yang kami tulis di Bab 3 .
Daftar kata kunci harus disortir agar semakin meningkat dalam tabel.

#termasuk <stdio.h>
#termasuk <ctype.h>
#termasuk <string.h>

#define MAXWORD 100

int getword (char *, int);


int binsearch (char *, kunci struct *, int);

/ * hitung kata kunci C * /


utama()
{
int n;
kata char [MAXWORD];

while (getword (word, MAXWORD)! = EOF)


if (isalpha (word [0]))
if ((n = binsearch (word, keytab, NKEYS))> = 0)
keytab [n] .count ++;
untuk (n = 0; n <NKEYS; n ++)
if (keytab [n] .count> 0)
printf ("% 4d% s \ n",
keytab [n] .count, keytab [n] .word);
return 0;
}

/ * binsearch: cari kata di tab [0] ... tab [n-1] * /


int binsearch (char * word, tab kunci struct [], int n)
{
int cond;
int low, high, mid;

rendah = 0;
tinggi = n - 1;
sementara (rendah <= tinggi) {
mid = (rendah + tinggi) / 2;
if ((cond = strcmp (word, tab [mid] .word)) <0)
tinggi = pertengahan - 1;
lain jika (cond> 0)
rendah = pertengahan + 1;
lain
kembali pertengahan;
}
return -1;
}
Kami akan menampilkan fungsi getword sebentar lagi; untuk saat ini sudah cukup untuk mengatakan bahwa setiap panggilan ke
getword menemukan sebuah kata, yang disalin ke dalam array bernama sebagai argumen pertama.
Kuantitas NKEYS adalah jumlah kata kunci di keytab . Meskipun kita bisa menghitungnya dengan
tangan, jauh lebih mudah dan aman untuk melakukannya dengan mesin, terutama jika daftar dapat berubah.
Satu kemungkinan adalah untuk mengakhiri daftar inisialisasi dengan pointer nol, lalu loop sepanjang
keytab sampai akhir ditemukan.

Halaman 110

111
Tetapi ini lebih dari yang diperlukan, karena ukuran array sepenuhnya ditentukan pada saat kompilasi
waktu. Ukuran array adalah ukuran satu entri kali jumlah entri, demikian juga jumlahnya
entri hanya

ukuran keytab / ukuran kunci struct

C menyediakan operator unary waktu kompilasi yang disebut sizeof yang dapat digunakan untuk menghitung ukuran
benda apa pun. Ekspresi

ukuran objek
dan

sizeof ( ketik nama )


menghasilkan bilangan bulat yang sama dengan ukuran objek atau jenis yang ditentukan dalam byte. (Ketat, sizeof
menghasilkan nilai integer yang tidak ditandai yang tipenya, size_t , didefinisikan di header <stddef.h> .)
Objek dapat berupa variabel atau array atau struktur. Nama tipe bisa berupa nama tipe dasar
seperti int atau double , atau tipe turunan seperti struktur atau pointer.
Dalam kasus kami, jumlah kata kunci adalah ukuran array dibagi dengan ukuran satu elemen.
Perhitungan ini digunakan dalam pernyataan #define untuk menetapkan nilai NKEYS :

#define NKEYS (sizeof keytab / sizeof (struct key))


Cara lain untuk menulis ini adalah dengan membagi ukuran array dengan ukuran elemen tertentu:
#define NKEYS (sizetab keytab / sizeof (keytab [0]))
Ini memiliki keuntungan yang tidak perlu diubah jika jenisnya berubah.
Sebuah sizeof tidak dapat digunakan dalam # jika line, karena preprocessor tidak nama-nama jenis tidak parse.
Tetapi ekspresi dalam #define tidak dievaluasi oleh preprocessor, jadi kodenya di sini
hukum.

Sekarang untuk fungsi getword . Kami telah menulis lebih umum getword dari yang diperlukan untuk
program ini, tetapi tidak rumit. getword mengambil `` kata '' berikutnya dari input,
di mana sebuah kata adalah serangkaian huruf dan angka yang dimulai dengan huruf, atau satu
karakter spasi putih. Nilai fungsi adalah karakter pertama kata, atau EOF untuk akhir
file, atau karakter itu sendiri jika tidak alfabet.

/ * getword: dapatkan kata atau karakter berikutnya dari input * /


int getword (char * word, int lim)
{
int c, getch (void);
membatalkan ungetch (int);
char * w = word;

while (isspace (c = getch ()))


;
if (c! = EOF)
* w ++ = c;
if (! isalpha (c)) {
* w = '\ 0';
kembali c;
}
untuk (; --lim> 0; w ++)
if (! isalnum (* w = getch ())) {
ungetch (* w);
istirahat;
}
* w = '\ 0';
kembalikan kata [0];
}

Halaman 111

112

getword menggunakan getch dan ungetch yang kita tulis di Bab 4 . Ketika koleksi sebuah
token alfanumerik berhenti, kata sandi sudah terlalu jauh. Panggilan untuk ungetch
mendorong karakter itu kembali pada input untuk panggilan berikutnya. getword juga menggunakan isspace untuk melewati
spasi putih, isalpha untuk mengidentifikasi huruf, dan isalnum untuk mengidentifikasi huruf dan angka; semua dari
header standar <ctype.h> .
Latihan 6-1. Versi getword kami tidak menangani garis bawah, konstanta string dengan benar,
komentar, atau jalur kontrol preprosesor. Tulis versi yang lebih baik.

6.4 Petunjuk untuk Struktur


Untuk mengilustrasikan beberapa pertimbangan yang terkait dengan pointer ke dan susunan struktur, mari
kami menulis program penghitungan kata kunci lagi, kali ini menggunakan pointer bukan indeks array.
Deklarasi eksternal keytab tidak perlu diubah, tetapi main dan binsearch memang perlu
modifikasi.

#termasuk <stdio.h>
#termasuk <ctype.h>
#termasuk <string.h>
#define MAXWORD 100

int getword (char *, int);


kunci struct * binsearch (char *, kunci struct *, int);

/ * hitung kata kunci C; versi penunjuk * /


utama()
{
kata char [MAXWORD];
kunci struct * p;

while (getword (word, MAXWORD)! = EOF)


if (isalpha (word [0]))
if ((p = binsearch (kata, keytab, NKEYS))! = NULL)
p-> hitung ++;
untuk (p = keytab; p <keytab + NKEYS; p ++)
jika (p-> hitung> 0)
printf ("% 4d% s \ n", p-> count, p-> word);
return 0;
}

/ * binsearch: cari kata di tab [0] ... tab [n-1] * /


kunci struct * binsearch (char * word, key * tab tab, int n)
{
int cond;
kunci struct * rendah = & tab [0];
kunci struct * tinggi = & tab [n];
kunci struct * pertengahan;

while (rendah <tinggi) {


mid = rendah + (tinggi-rendah) / 2;
if ((cond = strcmp (word, mid-> word)) <0)
tinggi = pertengahan;
lain jika (cond> 0)
rendah = pertengahan + 1;
lain
kembali pertengahan;
}
mengembalikan NULL;
}
Ada beberapa hal yang patut diperhatikan di sini. Pertama, deklarasi binsearch harus menunjukkan
bahwa itu mengembalikan pointer ke kunci struct bukan bilangan bulat; ini dinyatakan keduanya dalam

Halaman 112

113
prototipe fungsi dan dalam binsearch . Jika binsearch menemukan kata itu, ia mengembalikan pointer ke sana; jika
gagal, mengembalikan NULL .
Kedua, elemen-elemen keytab sekarang diakses oleh pointer. Ini membutuhkan signifikan
perubahan dalam binsearch .

Inisialisasi untuk rendah dan tinggi sekarang petunjuk ke awal dan hanya melewati akhir
meja.

Perhitungan elemen tengah tidak bisa lagi sederhana

pertengahan = (rendah + tinggi) / 2 / * SALAH * /


karena penambahan pointer adalah ilegal. Pengurangan adalah legal, namun demikian, tinggi-rendah adalah
sejumlah elemen, dan karenanya

mid = rendah + (tinggi-rendah) / 2


setel pertengahan ke elemen di antara rendah dan tinggi .
Perubahan yang paling penting adalah menyesuaikan algoritma untuk memastikan bahwa itu tidak menghasilkan
penunjuk ilegal atau upaya untuk mengakses elemen di luar array. Masalahnya adalah & tab [-1]
dan & tab [n] keduanya di luar batas tab array . Yang pertama benar-benar ilegal, dan memang demikian
ilegal untuk dereferensi yang terakhir. Namun definisi bahasa menjamin penunjuk itu
aritmatika yang melibatkan elemen pertama di luar akhir array (yaitu, & tab [n] ) akan
bekerja dengan benar.

Dalam utama kami menulis

untuk (p = keytab; p <keytab + NKEYS; p ++)


Jika p adalah pointer ke struktur, aritmatika pada p memperhitungkan ukuran struktur, jadi
p ++ meningkatkan p dengan jumlah yang benar untuk mendapatkan elemen berikutnya dari susunan struktur, dan
tes berhenti loop pada waktu yang tepat.
Namun, jangan berasumsi bahwa ukuran struktur adalah jumlah dari ukuran anggotanya.
Karena persyaratan penyelarasan untuk objek yang berbeda, mungkin ada `lubang 'yang tidak disebutkan namanya di a
struktur. Jadi, misalnya, jika char adalah satu byte dan int empat byte, strukturnya

struct {
char c;
int i;
};
mungkin membutuhkan delapan byte, bukan lima. The sizeof Operator mengembalikan nilai yang tepat.
Akhirnya, selain pada format program: ketika suatu fungsi mengembalikan tipe yang rumit seperti a
pointer struktur, seperti pada

kunci struct * binsearch (char * word, struct key * tab, int n)


nama fungsi bisa sulit dilihat, dan ditemukan dengan editor teks. Dengan demikian alternatif
gaya kadang-kadang digunakan:
kunci struct *
binsearch (char * word, kunci struct * tab, int n)
Ini adalah masalah selera pribadi; pilih formulir yang Anda suka dan tahan untuk itu.

6.5 Struktur Referensi-sendiri


Misalkan kita ingin menangani masalah yang lebih umum menghitung kejadian dari semua yang
kata dalam beberapa input. Karena daftar kata-kata tidak diketahui sebelumnya, kami tidak dapat dengan mudah menyortir
dan gunakan pencarian biner. Namun kami tidak dapat melakukan pencarian linier untuk setiap kata yang muncul, untuk melihat apakah
sudah terlihat; program akan memakan waktu terlalu lama. (Lebih tepatnya, waktu menjalankannya adalah

Halaman 113

114
cenderung tumbuh kuadratik dengan jumlah kata input.) Bagaimana kita bisa mengatur data
menyalin secara efisien dengan daftar atau kata-kata sewenang-wenang?
Salah satu solusinya adalah menjaga rangkaian kata yang terlihat sejauh ini diurutkan setiap saat, dengan menempatkan setiap kata
ke posisi yang tepat dalam urutan saat tiba. Ini tidak boleh dilakukan dengan menggeser kata dalam a
array linear, meskipun - itu juga memakan waktu terlalu lama. Sebaliknya kita akan menggunakan struktur data yang disebut a
pohon biner .

Pohon berisi satu `` simpul '' per kata yang berbeda; setiap node berisi

â € ¢ Penunjuk ke teks kata,


â € ¢ Hitungan jumlah kejadian,
â € ¢ Pointer ke simpul anak kiri,
â € ¢ Pointer ke simpul anak kanan.
Tidak ada simpul yang dapat memiliki lebih dari dua anak; mungkin hanya memiliki nol atau satu.
Node dipertahankan sehingga pada setiap simpul subtree kiri hanya berisi kata-kata yang
Secara leksikografis kurang dari kata di simpul, dan subtree kanan hanya berisi kata-kata itu
lebih besar. Ini adalah pohon untuk kalimat `` sekarang adalah waktunya bagi semua orang baik untuk datang ke
bantuan partai mereka '', seperti yang dibangun dengan memasukkan setiap kata saat ditemui:

Untuk mengetahui apakah kata baru sudah ada di pohon, mulailah di root dan bandingkan yang baru
kata ke kata yang disimpan di simpul itu. Jika cocok, pertanyaan dijawab dengan tegas. Jika
catatan baru kurang dari kata pohon, terus mencari anak kiri, jika tidak di
anak yang tepat. Jika tidak ada anak di arah yang diperlukan, kata baru tidak ada di pohon, dan di
sebenarnya slot kosong adalah tempat yang tepat untuk menambahkan kata baru. Proses ini bersifat rekursif, karena
pencarian dari sembarang simpul menggunakan pencarian dari salah satu anaknya. Dengan demikian, rutinitas rekursif
untuk penyisipan dan pencetakan akan paling alami.

Kembali ke deskripsi node, itu paling mudah direpresentasikan sebagai struktur dengan
empat komponen:

struct tnode {/ * simpul pohon: * /


char * word; / * menunjuk ke teks * /
int count; / * jumlah kejadian * /
struct tnode * kiri; / * anak kiri * /
struct tnode * right; / * anak yang tepat * /
};
Deklarasi rekursif node mungkin terlihat untung-untungan, tapi itu benar. Itu ilegal untuk a
struktur mengandung contoh itu sendiri, tetapi
Halaman 114

115
struct tnode * kiri;
menyatakan kiri untuk menjadi penunjuk ke tnode , bukan tnode itu sendiri.
Kadang-kadang, seseorang membutuhkan variasi struktur referensi-diri: dua struktur yang merujuk
satu sama lain. Cara untuk menangani ini adalah:

struct t {
...
struct s * p; / * p menunjuk ke sebuah s * /
};
struct s {
...
struct t * q; / * q menunjuk ke pada * /
};
Kode untuk seluruh program sangat kecil, mengingat beberapa rutinitas pendukung
seperti getword yang sudah kita tulis. Rutin utama membaca kata-kata dengan getword dan
menginstalnya di pohon dengan addtree .

#termasuk <stdio.h>
#termasuk <ctype.h>
#termasuk <string.h>

#define MAXWORD 100


struct tnode * addtree (struct tnode *, char *);
void treeprint (struct tnode *);
int getword (char *, int);

/ * jumlah frekuensi kata * /


utama()
{
struct tnode * root;
kata char [MAXWORD];

root = NULL;
while (getword (word, MAXWORD)! = EOF)
if (isalpha (word [0]))
root = addtree (root, word);
treeprint (root);
return 0;
}
Fungsi addtree bersifat rekursif. Sebuah kata disajikan oleh utama ke tingkat atas (root) dari
pohon. Pada setiap tahap, kata itu dibandingkan dengan kata yang sudah disimpan di simpul, dan itu
meresap ke subtree kiri atau kanan dengan panggilan rekursif ke adtree . Akhirnya,
kata tersebut cocok dengan sesuatu yang sudah ada di pohon (dalam hal ini jumlah bertambah),
atau null pointer ditemui, menunjukkan bahwa suatu simpul harus dibuat dan ditambahkan ke pohon. Jika
simpul baru dibuat, addtree mengembalikan pointer ke sana, yang dipasang di simpul induk.

struct tnode * talloc (void);


char * strdup (char *);

/ * addtree: tambahkan node dengan w, pada atau di bawah p * /


struct treenode * addtree (struct tnode * p, char * w)
{
int cond;

jika (p == NULL) {/ * kata baru telah tiba * /


p = talloc (); / * buat simpul baru * /
p-> word = strdup (w);
p-> hitung = 1;
p-> kiri = p-> kanan = NULL;
} lain jika ((cond = strcmp (w, p-> word)) == 0)
p-> hitung ++; / * kata berulang * /
lain jika (cond <0) / * kurang dari subtree kiri * /
p-> left = addtree (p-> left, w);

Halaman 115

116
lain / * lebih besar dari pada subtree kanan * /
p-> right = addtree (p-> right, w);
return p;
}
Penyimpanan untuk simpul baru diambil oleh talloc rutin , yang mengembalikan pointer ke gratis
ruang yang cocok untuk memegang simpul pohon, dan kata baru disalin ke ruang tersembunyi oleh
strdup . (Kami akan membahas rutinitas ini sebentar lagi.) Hitungannya diinisialisasi, dan keduanya
anak-anak dibuat batal. Bagian kode ini dieksekusi hanya di daun pohon, ketika a
simpul baru sedang ditambahkan. Kami telah (secara tidak bijaksana) menghilangkan kesalahan saat memeriksa nilai yang dikembalikan oleh
strdup dan talloc .
treeprint mencetak pohon dengan urutan terurut; di setiap node, ia mencetak subtree kiri (semua kata
kurang dari kata ini), lalu kata itu sendiri, kemudian subtree kanan (semua kata lebih besar). Jika kamu
merasa goyah tentang bagaimana rekursi bekerja, mensimulasikan treeprint saat beroperasi pada pohon yang ditunjukkan
atas.

/ * treeprint: cetak pohon secara berurutan p * /


void treeprint (struct tnode * p)
{
if (p! = NULL) {
treeprint (p-> kiri);
printf ("% 4d% s \ n", p-> count, p-> word);
treeprint (p-> kanan);
}
}
Catatan praktis: jika pohon menjadi `` tidak seimbang '' karena kata-katanya tidak tiba secara acak
memesan, waktu menjalankan program dapat tumbuh terlalu banyak. Sebagai kasus terburuk, jika kata-kata itu
sudah dalam rangka, program ini melakukan simulasi pencarian linear yang mahal. Ada
generalisasi pohon biner yang tidak menderita dari perilaku terburuk ini, tapi kami akan melakukannya
tidak menggambarkannya di sini.
Sebelum meninggalkan contoh ini, ada baiknya penyimpangan singkat pada masalah yang terkait dengan penyimpanan
pengalokasi. Jelas diinginkan bahwa hanya ada satu pengalokasi penyimpanan dalam suatu program, bahkan
meskipun itu mengalokasikan berbagai jenis objek. Tetapi jika satu pengalokasi adalah untuk memproses permintaan,
katakanlah, pointer ke char dan pointer ke struct tnode s, dua pertanyaan muncul. Pertama, bagaimana caranya
memenuhi persyaratan kebanyakan mesin nyata bahwa objek dari jenis tertentu harus memenuhi perataan
pembatasan (misalnya, bilangan bulat sering harus ditemukan di alamat genap)? Kedua, apa
deklarasi dapat mengatasi kenyataan bahwa pengalokasi harus selalu mengembalikan berbagai jenis
petunjuk?

Persyaratan perataan umumnya dapat dipenuhi dengan mudah, dengan mengorbankan beberapa ruang yang terbuang, oleh
memastikan bahwa pengalokasi selalu mengembalikan pointer yang memenuhi semua batasan penyelarasan. Itu
alokasi dari Bab 5 tidak menjamin keselarasan tertentu, jadi kita akan menggunakan standar
fungsi perpustakaan malloc , yang tidak. Dalam Bab 8 kita akan menunjukkan satu cara untuk mengimplementasikan
malloc .

Pertanyaan tentang tipe deklarasi untuk fungsi seperti malloc adalah pertanyaan yang menjengkelkan bagi siapa pun
bahasa yang menganggap serius pemeriksaan tipenya. Dalam C, metode yang tepat adalah menyatakan itu
malloc mengembalikan pointer ke void , lalu secara eksplisit memaksa pointer ke tipe yang diinginkan dengan a
Pemeran. malloc dan rutinitas terkait dideklarasikan di header standar <stdlib.h> . Jadi
talloc dapat ditulis sebagai

#termasuk <stdlib.h>

/ * talloc: membuat tnode * /


struct tnode * talloc (batal)
{
return (struct tnode *) malloc (sizeof (struct tnode));

Halaman 116

117
}
strdup hanya menyalin string yang diberikan oleh argumennya ke tempat yang aman, diperoleh melalui panggilan
malloc :
char * strdup (char * s) / * membuat duplikat dari s * /
{
char * p;

p = (char *) malloc (strlen (s) +1); / * +1 untuk '\ 0' * /


if (p! = NULL)
strcpy (p, s);
return p;
}
malloc mengembalikan NULL jika tidak ada ruang yang tersedia; strdup meneruskan nilai itu, meninggalkan error-
menangani peneleponnya.
Penyimpanan yang diperoleh dengan menelepon malloc dapat dibebaskan untuk digunakan kembali dengan menelepon gratis ; lihat Bab 8
dan 7 .

Latihan 6-2. Tuliskan program yang membaca program C dan cetak masing-masing dalam urutan abjad
sekelompok nama variabel yang identik dalam 6 karakter pertama, tetapi berbeda di suatu tempat
kemudian. Jangan hitung kata dalam string dan komentar. Jadikan 6 sebagai parameter yang dapat diatur
dari baris perintah.

Latihan 6-3. Tulis referensi silang yang mencetak daftar semua kata dalam dokumen, dan untuk
setiap kata, daftar nomor baris tempat kata itu muncul. Hapus kata-kata bising seperti `the, ''
`` dan, '' dan seterusnya.

Latihan 6-4. Tulis program yang mencetak kata-kata yang berbeda di inputnya yang diurut menjadi menurun
urutan frekuensi kejadian. Awali setiap kata dengan hitungannya.
6.6 Pencarian Tabel
Pada bagian ini kita akan menulis jeroan paket pencarian tabel, untuk menggambarkan lebih banyak aspek
struktur. Kode ini tipikal dari apa yang mungkin ditemukan dalam manajemen tabel simbol
rutinitas prosesor makro atau kompiler. Misalnya, perhatikan pernyataan #define .
Ketika garis suka

#definisikan DALAM 1
ditemui, nama IN dan teks pengganti 1 disimpan dalam sebuah tabel. Nanti, saat itu
nama IN muncul di pernyataan seperti

state = IN;
itu harus diganti dengan 1 .
Ada dua rutinitas yang memanipulasi nama dan teks pengganti. instal (s, t)
mencatat nama s dan penggantian teks t dalam tabel; s dan t hanyalah string karakter.
lookup mencari s di tabel, dan mengembalikan pointer ke tempat ditemukannya, atau
NULL jika tidak ada di sana.

Algoritma adalah pencarian hash - nama yang masuk diubah menjadi non-negatif kecil
integer, yang kemudian digunakan untuk mengindeks ke dalam array pointer. Elemen array menunjuk ke
mulai dari daftar tertaut blok yang menggambarkan nama yang memiliki nilai hash itu. Hal ini NULL jika tidak ada
nama telah di hash ke nilai itu.

Halaman 117

118

Blok dalam daftar adalah struktur yang berisi pointer ke nama, teks pengganti, dan
blok selanjutnya dalam daftar. Pointer nol berikutnya menandai akhir daftar.

struct nlist {/ * entri tabel: * /


struct nlist * selanjutnya; / * entri berikutnya dalam rantai * /
nama karakter; / * nama yang ditentukan * /
char * defn; / * teks pengganti * /
};
Array pointer hanya

#define HASHSIZE 101

static struct nlist * hashtab [HASHSIZE]; / * tabel pointer * /


Fungsi hashing, yang digunakan oleh lookup dan install , menambahkan setiap nilai karakter di
string ke kombinasi acak dari yang sebelumnya dan mengembalikan modulo sisanya
ukuran array. Ini bukan fungsi hash terbaik, tetapi pendek dan efektif.

/ * hash: bentuk nilai hash untuk string s * /


hash tidak ditandatangani (karakter *)
{
hashval yang tidak ditandatangani;

untuk (hashval = 0; * s! = '\ 0'; s ++)


hashval = * s + 31 * hashval;
mengembalikan hashval% HASHSIZE;
}
Aritmatika yang tidak ditandatangani memastikan bahwa nilai hash adalah non-negatif.
Proses hashing menghasilkan indeks awal dalam hashtab array ; jika string dapat ditemukan
di mana saja, itu akan ada dalam daftar blok yang dimulai di sana. Pencarian dilakukan dengan pencarian . Jika
lookup menemukan entri sudah ada, ia mengembalikan pointer ke sana; jika tidak, ia mengembalikan NULL .

/ * lookup: cari s dalam hashtab * /


struct nlist * lookup (char * s)
{
struct nlist * np;
untuk (np = hashtab [hash (s)]; np! = NULL; np = np-> selanjutnya)
if (strcmp (s, np-> name) == 0)
kembali np; /* ditemukan */
mengembalikan NULL; /* tidak ditemukan */
}
The untuk loop dalam lookup adalah idiom standar untuk berjalan di sepanjang sebuah linked list:

untuk (ptr = head; ptr! = NULL; ptr = ptr-> selanjutnya)


...
install menggunakan pencarian untuk menentukan apakah nama yang diinstal sudah ada; jika begitu,
definisi baru akan menggantikan yang lama. Kalau tidak, entri baru akan dibuat. Install
mengembalikan NULL jika karena alasan apa pun tidak ada ruang untuk entri baru.

Halaman 118

119

struct nlist * lookup (char *);


char * strdup (char *);

/ * instal: put (nama, defn) di hashtab * /


struct nlist * instal (char * name, char * defn)
{
struct nlist * np;
hashval yang tidak ditandatangani;

if ((np = lookup (name)) == NULL) {/ * tidak ditemukan * /


np = (struct nlist *) malloc (sizeof (* np));
if (np == NULL || (np-> name = strdup (name)) == NULL)
mengembalikan NULL;
hashval = hash (nama);
np-> next = hashtab [hashval];
hashtab [hashval] = np;
} lain / * sudah ada * /
free ((void *) np-> defn); / * gratis defn sebelumnya * /
if ((np-> defn = strdup (defn)) == NULL)
mengembalikan NULL;
kembali np;
}
Latihan 6-5. Tulis fungsi undef yang akan menghapus nama dan definisi dari tabel
dikelola oleh pencarian dan instal .
Latihan 6-6. Menerapkan versi sederhana dari prosesor #define (yaitu, tidak ada argumen)
cocok untuk digunakan dengan program C, berdasarkan rutinitas bagian ini. Anda mungkin juga menemukan
getch dan ungetch bermanfaat.

6.7 Typedef
C menyediakan fasilitas yang disebut typedef untuk membuat nama tipe data baru. Misalnya,
pernyataan

typedef int Panjang;


menjadikan nama Length sebagai sinonim untuk int . Tipe Panjang dapat digunakan dalam deklarasi,
gips, dll., dengan cara yang persis sama dengan tipe int dapat:

Panjang len, maksimal;


Panjang * panjang [];
Begitu pula dengan deklarasi

typedef char * String;


menjadikan String sebagai sinonim untuk karakter * atau penunjuk karakter, yang kemudian dapat digunakan di
deklarasi dan gips:

String p, lineptr [MAXLINES], alokasi (int);


int strcmp (String, String);
p = (String) malloc (100);
Perhatikan bahwa tipe yang dideklarasikan dalam typedef muncul di posisi nama variabel,
tidak tepat setelah kata typedef . Secara sintaksis, typedef seperti extern kelas penyimpanan ,
statis , dll. Kami telah menggunakan nama kapital untuk typedef , untuk membuatnya menonjol.
Sebagai contoh yang lebih rumit, kita bisa membuat typedef untuk node tree yang ditunjukkan sebelumnya
Bab ini:

typedef struct tnode * Treeptr;

typedef struct tnode {/ * simpul pohon: * /


char * word; / * menunjuk ke teks * /
int count; / * jumlah kejadian * /
struct tnode * kiri; / * anak kiri * /
Halaman 119

120
struct tnode * right; / * anak yang tepat * /
} Treenode;
Ini menciptakan dua kata kunci tipe baru yang disebut Treenode (struktur) dan Treeptr (penunjuk ke
struktur). Kemudian talloc rutin bisa menjadi

Treeptr talloc (batal)


{
return (Treeptr) malloc (sizeof (Treenode));
}
Harus ditekankan bahwa deklarasi typedef tidak membuat tipe baru dalam arti apa pun; Itu
hanya menambahkan nama baru untuk beberapa jenis yang ada. Juga tidak ada semantik baru: variabel
mendeklarasikan cara ini memiliki properti yang sama persis dengan variabel yang deklarasi ejaannya
keluar secara eksplisit. Akibatnya, typedef seperti #define , kecuali itu karena ditafsirkan oleh
kompiler, dapat mengatasi pergantian tekstual yang berada di luar kemampuan
preprosesor. Sebagai contoh,

ketikkan int (* PFI) (char *, char *);


membuat tipe PFI , untuk `` penunjuk berfungsi (dari dua argumen char * ) mengembalikan int , ''
yang dapat digunakan dalam konteks seperti

PFI strcmp, numcmp;


dalam program semacam Bab 5 .
Selain masalah estetika murni, ada dua alasan utama untuk menggunakan typedef . Yang pertama adalah
parameterisasi program terhadap masalah portabilitas. Jika typedef digunakan untuk tipe data itu
mungkin bergantung pada mesin, hanya typedef yang perlu berubah ketika program dipindahkan. Satu
situasi umum adalah menggunakan nama typedef untuk berbagai jumlah integer, lalu buat
set pilihan pendek , int , dan panjang yang sesuai untuk setiap mesin host. Jenis seperti size_t
dan ptrdiff_t dari perpustakaan standar adalah contoh.

Tujuan kedua dari typedef adalah untuk menyediakan dokumentasi yang lebih baik untuk suatu program - suatu tipe
Disebut Treeptr mungkin lebih mudah dipahami daripada yang dinyatakan hanya sebagai pointer ke a
struktur yang rumit.

6.8 Serikat pekerja


Sebuah serikat adalah variabel yang dapat memegang (pada waktu yang berbeda) objek dari berbagai jenis dan ukuran, dengan
compiler mencatat ukuran dan persyaratan pelurusan. Serikat pekerja menyediakan cara untuk
memanipulasi berbagai jenis data dalam satu area penyimpanan, tanpa menyematkan mesin apa pun
informasi dependen dalam program. Mereka analog dengan catatan varian dalam pascal.
Sebagai contoh seperti yang dapat ditemukan di manajer tabel simbol kompiler, anggaplah bahwa a
konstanta dapat berupa int , float , atau pointer karakter. Nilai konstanta tertentu
harus disimpan dalam variabel tipe yang tepat, namun paling nyaman untuk manajemen tabel
jika nilai menempati jumlah penyimpanan yang sama dan disimpan di tempat yang sama tanpa memperhatikan
jenisnya. Ini adalah tujuan dari persatuan - sebuah variabel tunggal yang secara sah dapat menampung salah satu dari variabel tersebut
dari beberapa jenis. Sintaks didasarkan pada struktur:

union u_tag {
int ival;
float fval;
char * sval;
} u;
Variabel u akan cukup besar untuk menampung yang terbesar dari ketiga jenis; ukuran spesifiknya adalah
tergantung pada implementasi. Jenis-jenis ini dapat ditugaskan untuk Anda dan kemudian digunakan di
ekspresi, selama penggunaannya konsisten: tipe yang diambil harus tipe yang paling
baru saja disimpan. Merupakan tanggung jawab programmer untuk melacak tipe yang saat ini

Halaman 120

121
disimpan dalam serikat pekerja; hasilnya tergantung pada implementasi jika sesuatu disimpan sebagai satu jenis
dan diekstraksi sebagai yang lain.
Secara sintaksis, anggota serikat pekerja diakses sebagai

nama serikat pekerja . anggota

atau

union-pointer -> anggota


sepertiorang
maka untukmungkin
struktur.melihat
Jika variabel utype digunakan untuk melacak jenis saat disimpan dalam u ,
kode seperti

if (utype == INT)
printf ("% d \ n", u.ival);
if (utype == FLOAT)
printf ("% f \ n", u.fval);
if (utype == STRING)
printf ("% s \ n", u.sval);
lain
printf ("tipe buruk% d dalam utype \ n", utype);
Serikat pekerja dapat terjadi di dalam struktur dan array, dan sebaliknya. Notasi untuk mengakses a
anggota serikat dalam suatu struktur (atau sebaliknya) identik dengan yang untuk struktur bersarang. Untuk
contoh, dalam array struktur yang didefinisikan oleh

struct {
nama karakter;
bendera int;
int utype;
Persatuan {
int ival;
float fval;
char * sval;
} u;
} symtab [NSYM];
ival anggota disebut sebagai

symtab [i] .u.ival


dan karakter pertama dari string sval oleh salah satu dari

* symtab [i] .u.sval

symtab [i] .u.sval [0]


Akibatnya, serikat pekerja adalah struktur di mana semua anggota telah mengimbangi nol dari basis, yaitu
struktur cukup besar untuk menampung anggota `` terluas '', dan perataan cocok untuk semua
dari jenis dalam serikat. Operasi yang sama diizinkan pada serikat pekerja seperti pada struktur:
penugasan atau menyalin sebagai unit, mengambil alamat, dan mengakses anggota.
Serikat pekerja hanya dapat diinisialisasi dengan nilai tipe anggota pertamanya; dengan demikian persatuan kamu
dijelaskan di atas hanya dapat diinisialisasi dengan nilai integer.

Pengalokasi penyimpanan pada Bab 8 menunjukkan bagaimana suatu serikat dapat digunakan untuk memaksa variabel menjadi
selaras pada jenis batas penyimpanan tertentu.

6.9 Bit-bidang
Ketika ruang penyimpanan sangat mahal, mungkin perlu untuk mengepak beberapa objek menjadi satu
kata mesin; satu penggunaan umum adalah satu set flag bit tunggal dalam aplikasi seperti simbol kompiler
meja. Format data yang diberlakukan secara eksternal, seperti antarmuka ke perangkat perangkat keras, juga sering
membutuhkan kemampuan untuk mendapatkan kata-kata.

Halaman 121

122
Bayangkan fragmen kompiler yang memanipulasi tabel simbol. Setiap pengidentifikasi dalam suatu program
memiliki informasi tertentu yang terkait dengannya, misalnya, apakah itu kata kunci atau tidak
atau tidak itu eksternal dan / atau statis, dan sebagainya. Cara paling ringkas untuk menyandikannya
informasi adalah satu set flag satu-bit dalam satu karakter atau int .

Cara biasa ini dilakukan adalah mendefinisikan satu set `` mask '' yang sesuai dengan bit yang relevan
posisi, seperti pada

#define KEYWORD 01
#define EKSTRENAL 02
#define STATIC 04
atau

enum {KEYWORD = 01, EKSTERNAL = 02, STATIC = 04};


Angka-angka haruslah kekuatan dua. Kemudian mengakses bit menjadi masalah `` bit-
mengutak-atik operator penggeser, penutup, dan pelengkap yang dijelaskan dalam
Bab 2 .
Idiom tertentu sering muncul:

bendera | = EKSTERNAL | STATIS;


mengaktifkan bit EKSTERNAL dan STATIK dalam bendera , sementara

bendera & = ~ (EKSTERNAL | STATIK);


mematikannya, dan

if ((flags & (EXTERNAL | STATIC)) == 0) ...


benar jika kedua bit mati.
Meskipun idiom ini mudah dikuasai, sebagai alternatif C menawarkan kemampuan mendefinisikan
dan mengakses bidang dalam kata secara langsung daripada oleh operator logis bitwise. Bidang bit ,
atau bidang singkatnya, adalah sekumpulan bit yang berdekatan dalam satu unit penyimpanan yang ditentukan implementasi
bahwa kita akan memanggil `` kata. '' Misalnya, tabel simbol #define s di atas dapat diganti
dengan definisi tiga bidang:

struct {
int_signword int unsigned: 1;
int is_extern unsigned: 1;
is_static int unsigned: 1;
} bendera;
Ini mendefinisikan tabel variabel yang disebut flag yang berisi tiga bidang 1-bit. Angka berikut
titik dua mewakili lebar bidang dalam bit. Kolom ini dinyatakan sebagai unsigned int untuk memastikan
bahwa mereka adalah jumlah yang tidak ditandatangani.
Setiap bidang direferensikan dengan cara yang sama dengan anggota struktur lainnya:
flags.is_keyword , flags.is_extern , dll. Bidang berperilaku seperti bilangan bulat kecil, dan mungkin
berpartisipasi dalam ekspresi aritmatika seperti bilangan bulat lainnya. Demikian contoh-contoh sebelumnya mungkin
ditulis lebih alami sebagai

flags.is_extern = flags.is_static = 1;
untuk mengaktifkan bit;

flags.is_extern = flags.is_static = 0;
untuk mematikannya; dan

if (flags.is_extern == 0 && flags.is_static == 0)


...
untuk menguji mereka.

Halaman 122

123
Hampir segala sesuatu tentang bidang tergantung pada implementasi. Apakah suatu bidang dapat tumpang tindih a
batas kata didefinisikan oleh implementasi. Field tidak harus berupa nama; bidang tanpa nama (titik dua
dan lebar saja) digunakan untuk bantalan. Lebar khusus 0 dapat digunakan untuk memaksa penyelarasan pada
kata batas berikutnya.

Fields ditugaskan dari kiri ke kanan pada beberapa mesin dan kanan ke kiri pada yang lain. Ini artinya
Meskipun bidang berguna untuk mempertahankan struktur data yang ditetapkan secara internal, pertanyaan tentang
akhir mana yang pertama harus dipertimbangkan dengan hati-hati ketika memilih secara eksternal
data; program yang bergantung pada hal-hal semacam itu tidak portabel. Bidang dapat dinyatakan hanya sebagai
int ; untuk portabilitas, tentukan ditandatangani atau tidak ditandatangani secara eksplisit. Mereka bukan array dan mereka lakukan
tidak memiliki alamat, sehingga operator & tidak dapat menerapkannya.
Halaman 123

124

Bab 7 - Input dan Output


Input dan output bukan bagian dari bahasa C itu sendiri, jadi kami belum menekankannya dalam bahasa kami
presentasi sejauh ini. Meskipun demikian, banyak program berinteraksi dengan lingkungan mereka
cara rumit dari yang telah kami tunjukkan sebelumnya. Dalam bab ini kita akan menjelaskan
perpustakaan standar, satu set fungsi yang menyediakan input dan output, penanganan string, penyimpanan
manajemen, rutinitas matematika, dan berbagai layanan lain untuk program C. Kami akan
berkonsentrasi pada input dan output
Standar ANSI mendefinisikan fungsi-fungsi pustaka ini secara tepat, sehingga dapat ada di
formulir yang kompatibel pada sistem mana pun di mana C ada. Program yang membatasi interaksi sistem mereka
ke fasilitas yang disediakan oleh perpustakaan standar dapat dipindahkan dari satu sistem ke yang lain tanpa
perubahan.

Properti fungsi perpustakaan ditentukan dalam lebih dari selusin header; Kita sudah
terlihat beberapa di antaranya, termasuk <stdio.h> , <string.h> , dan <ctype.h> . Kami tidak akan hadir
seluruh perpustakaan di sini, karena kami lebih tertarik untuk menulis program C yang menggunakannya. Itu
perpustakaan dijelaskan secara rinci dalam Lampiran B .

7.1 Input dan Output Standar


Seperti yang kami katakan di Bab 1 , perpustakaan mengimplementasikan model input dan output teks sederhana. Sebuah teks
stream terdiri dari urutan garis; setiap baris diakhiri dengan karakter baris baru. Jika sistem
tidak beroperasi seperti itu, perpustakaan melakukan apa pun yang diperlukan untuk membuatnya tampak seolah-olah tidak.
Misalnya, perpustakaan mungkin mengubah carriage return dan linefeed ke baris baru pada input dan
kembali lagi pada keluaran.
Mekanisme input paling sederhana adalah membaca satu karakter pada satu waktu dari input standar ,
biasanya keyboard, dengan getchar :

int getchar (batal)


getchar mengembalikan karakter input berikutnya setiap kali dipanggil, atau EOF saat bertemu berakhir
file. EOF konstanta simbolis didefinisikan dalam <stdio.h> . Nilai biasanya -1, tes bus
harus ditulis dalam bentuk EOF sehingga tidak tergantung pada nilai spesifik.
Di banyak lingkungan, file dapat diganti dengan keyboard dengan menggunakan <konvensi untuk
input redirection: jika prog program menggunakan getchar , maka baris perintah

prog <infile
menyebabkan prog membaca karakter dari infile . Pergantian input dilakukan sedemikian rupa
sebuah cara yang prog sendiri tidak menyadari perubahan; khususnya, string `` <infile '' tidak
termasuk dalam argumen command-line dalam argv . Pergantian input juga tidak terlihat jika input
berasal dari program lain melalui mekanisme pipa: pada beberapa sistem, baris perintah

otherprog | prog
menjalankan dua program otherprog dan prog , dan menyalurkan output standar dari otherprog ke dalam
input standar untuk prog .
Fungsinya

int putchar (int)


digunakan untuk output: putchar (c) menempatkan karakter c pada output standar , yaitu oleh
default layar. putchar mengembalikan karakter yang ditulis, atau EOF adalah kesalahan terjadi. Lagi,
output biasanya dapat diarahkan ke file dengan> nama file : jika prog menggunakan putchar ,
Halaman 124

125
prog> outfile
akan menulis output standar untuk outfile sebagai gantinya. Jika pipa didukung,

prog | program lain


menempatkan output standar prog ke input standar prog lain .
Output yang dihasilkan oleh printf juga menemukan jalan ke output standar. Panggilan untuk putchar dan
printf mungkin disisipkan - output terjadi sesuai urutan panggilan dilakukan.

Setiap file sumber yang merujuk ke fungsi pustaka input / output harus berisi baris

#termasuk <stdio.h>
sebelum referensi pertama. Ketika nama dikurung oleh <dan> pencarian dilakukan untuk
sundulan di set tempat standar (misalnya, pada sistem UNIX, biasanya di direktori
/ usr / termasuk ).
Banyak program hanya membaca satu aliran input dan menulis hanya satu aliran keluaran; untuk itu
program, input dan output dengan getchar , putchar , dan printf mungkin sepenuhnya memadai,
dan tentu saja cukup untuk memulai. Ini terutama benar jika pengalihan digunakan untuk menghubungkan
output dari satu program ke input yang berikutnya. Misalnya, pertimbangkan program lebih rendah ,
yang mengubah inputnya menjadi huruf kecil:

#termasuk <stdio.h>
#termasuk <ctype.h>

main () / * lower: mengonversi input ke huruf kecil * /


{
int c

while ((c = getchar ())! = EOF)


putchar (tolower (c));
return 0;
}
Fungsi tolower didefinisikan dalam <ctype.h> ; itu mengubah huruf besar ke kecil,
dan mengembalikan karakter lain yang tidak tersentuh. Seperti yang kami sebutkan sebelumnya, `` berfungsi '' seperti getchar
dan putchar di <stdio.h> dan tolower di <ctype.h> sering makro, sehingga menghindari
overhead panggilan fungsi per karakter. Kami akan menunjukkan bagaimana hal ini dilakukan di Bagian 8.5 .
Terlepas dari bagaimana fungsi <ctype.h> diimplementasikan pada mesin yang diberikan, program
yang menggunakannya dilindungi dari pengetahuan tentang rangkaian karakter.
Latihan 7-1. Tulis program yang mengubah huruf besar menjadi huruf kecil atau huruf kecil ke atas,
tergantung pada nama yang dipanggil, seperti yang ditemukan di argv [0] .

7.2 Output Terformat - printf


Fungsi keluaran printf menerjemahkan nilai-nilai internal ke karakter. Kami telah menggunakan printf
informal di bab-bab sebelumnya. Deskripsi di sini mencakup kegunaan paling umum tetapi tidak
lengkap; untuk cerita lengkap, lihat Lampiran B .

printf int (format char *, arg1, arg2, ...);


printf mengkonversi, memformat, dan mencetak argumennya pada output standar di bawah kendali
format . Ini mengembalikan jumlah karakter yang dicetak.
String format berisi dua jenis objek: karakter biasa, yang disalin ke
aliran output, dan spesifikasi konversi, yang masing-masing menyebabkan konversi dan pencetakan
argumen berturut-turut berikutnya ke printf . Setiap spesifikasi konversi dimulai dengan% dan
diakhiri dengan karakter konversi. Antara% dan karakter konversi mungkin ada,
dalam urutan:

Halaman 125

126
â € ¢ Tanda minus, yang menentukan penyesuaian kiri dari argumen yang dikonversi.

â € ¢ Angka yang menentukan lebar bidang minimum. Argumen yang dikonversi akan menjadi
dicetak dalam bidang setidaknya selebar ini. Jika perlu itu akan diisi di sebelah kiri (atau kanan, jika
penyesuaian kiri dipanggil untuk) untuk membuat lebar bidang.

â € ¢ Periode, yang memisahkan lebar bidang dari presisi.


â € ¢ Angka, ketepatan, yang menentukan jumlah karakter maksimum yang akan dicetak
dari string, atau jumlah digit setelah titik desimal dari nilai floating-point,
atau jumlah digit minimum untuk bilangan bulat.

â € ¢ Sebuah h jika integer yang akan dicetak sebagai pendek , atau l (surat elo) jika sebagai panjang .
Karakter konversi ditunjukkan pada Tabel 7.1. Jika karakter setelah% bukan konversi
spesifikasi, perilaku tidak terdefinisi.
Tabel 7.1 Konversi Printf Dasar

Karakter Jenis argumen; Dicetak sebagai


d, i int ; angka desimal
Hai int ; angka oktal yang tidak ditandatangani (tanpa nol di depan)
x, X int ; nomor heksadesimal yang tidak ditandai (tanpa 0x atau 0X ), menggunakan abcdef atau
ABCDEF untuk 10, ..., 15.
kamu int ; angka desimal yang tidak ditandatangani
c int ; karakter tunggal
s char * ; cetak karakter dari string hingga '\ 0' atau jumlah karakter
diberikan dengan presisi.

f ganda ; [-] m.dddddd , di mana jumlah d diberikan oleh presisi (default


6).

e, E ganda ; [-] m.dddddd e +/- xx atau [-] m.dddddd E +/- xx , di mana jumlah d 's
diberikan oleh presisi (default 6).
ganda ; gunakan % e atau % E jika eksponen kurang dari -4 atau lebih besar dari atau sama dengan
g, G presisi; jika tidak gunakan % f . Nol trailing dan titik desimal trailing tidak
dicetak.
hal batal * ; pointer (representasi yang bergantung pada implementasi).
% tidak ada argumen yang dikonversi; cetak%

Lebar atau presisi dapat ditentukan sebagai *, dalam hal ini nilai dihitung dengan mengonversi
argumen berikutnya (yang harus berupa int ). Misalnya, untuk mencetak maksimal dari karakter maksimal
sebuah string s ,

printf ("%. * s", maks, s);


Sebagian besar konversi format telah diilustrasikan pada bab sebelumnya. Satu pengecualian adalah
presisi terkait dengan string. Tabel berikut ini menunjukkan efek dari berbagai
spesifikasi dalam pencetakan `` hello, world '' (12 karakter). Kami telah menempatkan titik dua di sekitar setiap bidang
sehingga Anda bisa melihatnya sejauh.

:% s: :Halo Dunia:
:% 10s: :Halo Dunia:
:%. 10s:: halo, wor:
:% - 10s:: halo, dunia:
:%. 15s:: halo, dunia:
:% - 15s:: halo, dunia:
:% 15.10s:: halo, wor:
:% - 15.10s:: halo, wor:

Halaman 126

127
Peringatan: printf menggunakan argumen pertama untuk memutuskan berapa banyak argumen yang mengikuti dan apa
tipe mereka. Ini akan menjadi bingung, dan Anda akan mendapatkan jawaban yang salah, jika tidak ada cukup
argumen jika mereka adalah tipe yang salah. Anda juga harus menyadari perbedaan antara keduanya
dua panggilan ini:

printf; / * GAGAL jika s mengandung% * /


printf ("% s", s); / * AMAN * /
Fungsi sprintf melakukan konversi yang sama dengan printf , tetapi menyimpan output dalam a
tali:
int sprintf (karakter char *, format char *, arg1, arg2, ...);
sprintf memformat argumen dalam arg1 , arg2 , dll., sesuai dengan format seperti sebelumnya, tetapi di tempat
hasil dalam string bukannya output standar; string harus cukup besar untuk menerima
hasil.
Latihan 7-2. Tulis program yang akan mencetak input sewenang-wenang dengan cara yang masuk akal. Minimal,
harus mencetak karakter non-grafis dalam bentuk oktal atau heksadesimal sesuai dengan kebiasaan setempat, dan
memecah baris teks yang panjang.

7.3 Daftar Argumen Panjang Variabel


Bagian ini berisi implementasi versi minimal printf , untuk menunjukkan cara menulis
fungsi yang memproses daftar argumen panjang variabel dengan cara portabel. Karena kita terutama
tertarik pada pemrosesan argumen, minprintf akan memproses string format dan argumen
tetapi akan memanggil printf nyata untuk melakukan konversi format.
Deklarasi yang tepat untuk printf adalah

int printf (char * fmt, ...)


di mana deklarasi ... berarti jumlah dan jenis argumen ini dapat bervariasi. Itu
deklarasi ... hanya dapat muncul di akhir daftar argumen. Minprintf kami dinyatakan sebagai

batal minprintf (char * fmt, ...)


karena kami tidak akan mengembalikan jumlah karakter yang printf lakukan.
Yang sulit adalah bagaimana minprintf berjalan di sepanjang daftar argumen ketika daftar itu bahkan tidak memiliki
sebuah nama. Header standar <stdarg.h> berisi sekumpulan definisi makro yang menentukan caranya
untuk melangkah melalui daftar argumen. Implementasi header ini akan bervariasi dari mesin ke
mesin, tetapi antarmuka yang dihadirkannya seragam.

Tipe va_list digunakan untuk mendeklarasikan variabel yang akan merujuk ke setiap argumen secara bergantian; di
minprintf , variabel ini disebut ap , untuk `` pointer argumen. '' makro va_start menginisialisasi
ap untuk menunjuk ke argumen tanpa nama pertama. Itu harus dipanggil sekali sebelum ap digunakan. Sana
harus setidaknya satu argumen bernama; argumen final bernama digunakan oleh va_start untuk mendapatkan
mulai.

Setiap panggilan va_arg mengembalikan satu argumen dan langkah ap ke yang berikutnya; va_arg menggunakan nama tipe
untuk menentukan jenis yang akan dikembalikan dan seberapa besar langkah yang harus diambil. Akhirnya, va_end melakukan apa pun
diperlukan pembersihan. Itu harus dipanggil sebelum program kembali.

Properti ini membentuk dasar dari printf kami yang disederhanakan :

#termasuk <stdarg.h>

/ * minprintf: printf minimal dengan daftar argumen variabel * /


batal minprintf (char * fmt, ...)
{
va_list ap; / * menunjuk ke masing-masing arg tanpa nama pada gilirannya * /
char * p, * sval;

Halaman 127

128
int ival;
dval ganda;

va_start (ap, fmt); / * membuat titik ap ke arg tanpa nama pertama * /


untuk (p = fmt; * p; p ++) {
if (* p! = '%') {
putchar (* p);
terus;
}
beralih (* ++ p) {
huruf 'd':
ival = va_arg (ap, int);
printf ("% d", ival);
istirahat;
huruf 'f':
dval = va_arg (ap, dobel);
printf ("% f", dval);
istirahat;
huruf 's':
untuk (sval = va_arg (ap, char *); * sval; sval ++)
putchar (* sval);
istirahat;
default:
putchar (* p);
istirahat;
}
}
va_end (ap); / * Bersihkan saat selesai * /
}
Latihan 7-3. Merevisi minprintf untuk menangani lebih banyak fasilitas printf lainnya .

7.4 Input Terformat - Scanf


Fungsi scanf adalah analog input printf , menyediakan banyak konversi yang sama
fasilitas di arah yang berlawanan.

int scanf (format char *, ...)


scanf membaca karakter dari input standar, menafsirkannya sesuai dengan spesifikasi
dalam format , dan menyimpan hasilnya melalui argumen yang tersisa. Argumen format adalah
dijelaskan di bawah ini; argumen lain, yang masing-masing harus berupa pointer , menunjukkan di mana
input yang dikonversi yang sesuai harus disimpan. Seperti halnya printf , bagian ini adalah ringkasan dari
fitur yang paling berguna, bukan daftar lengkap.
scanf berhenti ketika menguras string formatnya, atau ketika beberapa input gagal untuk mencocokkan kontrol
spesifikasi. Ini mengembalikan nilainya jumlah input berhasil dicocokkan dan ditugaskan
barang. Ini dapat digunakan untuk memutuskan berapa banyak barang yang ditemukan. Di akhir file, EOF adalah
dikembalikan; perhatikan bahwa ini berbeda dari 0, yang berarti bahwa karakter input selanjutnya tidak
cocok dengan spesifikasi pertama dalam format string. Panggilan berikutnya untuk memindai kembali melanjutkan pencarian
segera setelah karakter terakhir sudah dikonversi.

Ada juga fungsi sscanf yang membaca dari string alih-alih input standar:

int sscanf (char * string, format char *, arg1, arg2, ...)


Itu memindai string sesuai dengan format dalam format dan menyimpan nilai yang dihasilkan melalui
arg1 , arg2 , dll. Argumen ini harus berupa pointer.
String format biasanya berisi spesifikasi konversi, yang digunakan untuk mengontrol
konversi input. String format dapat berisi:

â € ¢ Kosong atau tab, yang tidak diabaikan.

Halaman 128

129
â € ¢ Karakter biasa (bukan%), yang diharapkan cocok dengan spasi non-putih berikutnya
karakter aliran input.
â € ¢ Spesifikasi konversi, terdiri dari karakter % , penugasan opsional
karakter penindasan * , nomor opsional yang menentukan lebar bidang maksimum, sebuah
opsional h , l atau L yang mengindikasikan lebar target, dan karakter konversi.
Spesifikasi konversi mengarahkan konversi bidang input berikutnya. Biasanya hasilnya
tempat dalam variabel yang ditunjukkan oleh argumen yang sesuai. Jika penindasan penugasan adalah
ditunjukkan oleh karakter *, namun, bidang input dilewati; tidak ada tugas yang dilakukan. Sebuah
bidang input didefinisikan sebagai string karakter spasi non-putih; itu meluas ke yang berikutnya
karakter spasi putih atau hingga lebar bidang, ditentukan, habis. Ini menyiratkan scanf itu
akan membaca melintasi batas untuk menemukan inputnya, karena baris baru adalah ruang kosong. (Ruang putih
karakter kosong, tab, baris baru, carriage return, tab vertikal, dan formfeed.)
Karakter konversi menunjukkan interpretasi bidang input. Yang sesuai
argumen harus berupa pointer, seperti yang disyaratkan oleh semantik panggilan-oleh-nilai C. Conversion
karakter ditunjukkan pada Tabel 7.2.

Tabel 7.2: Konversi Pemindaian Dasar

Karakter Memasukan data; Jenis argumen


d bilangan bulat desimal; int *
bilangan bulat; int * . Bilangan bulat mungkin dalam oktal (awalan 0 ) atau heksadesimal (awalan
saya
0x atau 0X ).
Hai octal integer (dengan atau tanpa memimpin nol); int *
kamu bilangan bulat desimal yang tidak ditandatangani; int tidak bertanda *
x bilangan bulat heksadesimal (dengan atau tanpa memimpin 0x atau 0X ); int *
karakter; char * . Karakter input berikutnya (default 1) ditempatkan di
c tempat yang ditunjukkan. Ruang putih skip-over normal ditekan; untuk membaca selanjutnya
karakter spasi non-putih, gunakan % 1s

s
string karakter (tidak dikutip); char * , menunjuk ke array karakter yang panjang
cukup untuk string dan terminating '\ 0' yang akan ditambahkan.

e, f, g
angka floating-point dengan tanda opsional, titik desimal opsional dan opsional
eksponen; mengapung *
% % literal; tidak ada tugas yang dilakukan.

Karakter konversi d , i , o , u , dan x dapat didahului oleh h untuk menunjukkan bahwa pointer ke
pendek daripada int muncul dalam daftar argumen, atau dengan l (huruf ell) untuk menunjukkan bahwa sebuah penunjuk
untuk panjang muncul dalam daftar argumen.

Sebagai contoh pertama, kalkulator dasar Bab 4 dapat ditulis dengan scanf to do
konversi input:

#termasuk <stdio.h>

main () / * kalkulator dasar * /


{
jumlah ganda, v;

jumlah = 0;
while (scanf ("% lf", & v) == 1)
printf ("\ t% .2f \ n", jumlah + = v);
return 0;
}
Misalkan kita ingin membaca baris input yang berisi tanggal formulir
Halaman 129

130

25 Des 1988
The scanf pernyataan

hari int, tahun;


char monthname [20];

scanf ("% d% s% d", & hari, nama bulan, & tahun);


Tidak & digunakan dengan nama bulan , karena nama array adalah sebuah pointer.
Karakter literal dapat muncul dalam string format scanf ; mereka harus cocok dengan karakter yang sama
dalam input. Jadi kita bisa membaca tanggal formulir mm / dd / yy dengan pernyataan scanf :

hari int, bulan, tahun;

scanf ("% d /% d /% d", & bulan, & hari, & tahun);


scanf mengabaikan kekosongan dan tab dalam string formatnya. Selanjutnya, ia melompati ruang putih
(kosong, tab, baris baru, dll.) karena mencari nilai input. Untuk membaca input yang formatnya bukan
diperbaiki, seringkali yang terbaik adalah membaca satu baris sekaligus, kemudian mengambilnya dengan scanf . Sebagai contoh,
misalkan kita ingin membaca baris yang mungkin mengandung tanggal di salah satu dari formulir di atas. Lalu kita
bisa menulis

while (getline (line, sizeof (line))> 0) {


if (sscanf (baris, "% d% s% d", & hari, nama bulan, & tahun) == 3)
printf ("valid:% s \ n", line); / * 25 Des 1988 formulir * /
selain itu jika (sscanf (baris, "% d /% d /% d", & bulan, & hari, & tahun) == 3)
printf ("valid:% s \ n", line); / * mm / hh / tttt * * /
lain
printf ("tidak valid:% s \ n", line); / * formulir tidak valid * /
}
Panggilan untuk memindai dapat dicampur dengan panggilan ke fungsi input lainnya. Panggilan selanjutnya ke input apa pun
Fungsi akan dimulai dengan membaca karakter pertama yang tidak dibaca oleh scanf .
Peringatan terakhir: argumen untuk scanf dan sscanf harus berupa pointer. Sejauh ini yang paling
kesalahan umum adalah menulis

scanf ("% d", n);


dari pada

scanf ("% d", & n);


Kesalahan ini umumnya tidak terdeteksi pada waktu kompilasi.
Latihan 7-4. Tuliskan versi pribadi dari scanf yang dianalogikan dengan minprintf dari versi sebelumnya
bagian.

Latihan 5-5. Tulis ulang kalkulator postfix Bab 4 untuk menggunakan scanf dan / atau sscanf untuk dilakukan
konversi input dan angka.

7.5 Akses File


Contoh-contoh sejauh ini semuanya telah membaca input standar dan menulis output standar, yaitu
secara otomatis ditentukan untuk suatu program oleh sistem operasi lokal.
Langkah selanjutnya adalah menulis program yang mengakses file yang belum terhubung ke
program. Salah satu program yang menggambarkan perlunya operasi semacam itu adalah kucing , yang menyatukan
satu set file bernama ke dalam output standar. cat digunakan untuk mencetak file di layar, dan sebagai
kolektor input tujuan umum untuk program yang tidak memiliki kemampuan mengakses file
dengan nama. Misalnya, perintahnya

cat xc yc

Halaman 130

131
mencetak isi file xc dan yc (dan tidak ada yang lain) pada output standar.
Pertanyaannya adalah bagaimana mengatur agar file yang dinamai dibaca - yaitu, bagaimana menghubungkan
nama eksternal yang dipikirkan pengguna untuk pernyataan yang membaca data.

Aturannya sederhana. Sebelum dapat dibaca atau ditulis, file harus dibuka oleh perpustakaan
fungsi fopen . fopen mengambil nama eksternal seperti xc atau yc , melakukan beberapa housekeeping dan
negosiasi
pointer dengan
yang akansistem operasi
digunakan (detail
dalam yang tidak
membaca atau perlu menjadi
menulis perhatian kami), dan mengembalikan a
file selanjutnya.

Pointer ini, yang disebut file pointer , menunjuk ke struktur yang berisi informasi tentang
file, seperti lokasi buffer, posisi karakter saat ini di buffer, apakah
file sedang dibaca atau ditulis, dan apakah kesalahan atau akhir file telah terjadi. Pengguna tidak perlu
untuk mengetahui detailnya, karena definisi yang diperoleh dari <stdio.h> termasuk struktur
deklarasi yang disebut FILE . Satu-satunya pernyataan yang diperlukan untuk penunjuk file dicontohkan oleh

FILE * fp;
FILE * fopen (karakter char *, mode char *);
Ini mengatakan bahwa fp adalah pointer ke FILE , dan fopen mengembalikan pointer ke FILE . Perhatikan itu
FILE adalah nama jenis, seperti int , bukan tag struktur; itu didefinisikan dengan typedef . (Detail tentang bagaimana
fopen dapat diimplementasikan pada sistem UNIX yang diberikan dalam Bagian 8.5 .)
Panggilan untuk fopen dalam sebuah program adalah

fp = fopen (nama, mode);


Argumen pertama fopen adalah string karakter yang berisi nama file. Kedua
argumen adalah mode , juga string karakter, yang menunjukkan bagaimana seseorang berniat menggunakan file.
Mode yang diizinkan meliputi baca ( "r" ), tulis ( "w" ), dan tambahkan ( "a" ). Beberapa sistem membedakan
antara teks dan file biner; untuk yang terakhir, "b" harus ditambahkan ke string mode.
Jika file yang tidak ada dibuka untuk ditulis atau ditambahkan, itu dibuat jika memungkinkan. Pembukaan
file yang ada untuk menulis menyebabkan konten lama dibuang, saat dibuka untuk ditambahkan
melestarikannya. Mencoba membaca file yang tidak ada adalah kesalahan, dan mungkin ada yang lain
penyebab kesalahan juga, seperti mencoba membaca file ketika Anda tidak memiliki izin. Jika ada
kesalahan, fopen akan mengembalikan NULL . (Kesalahan dapat diidentifikasi lebih tepatnya; lihat diskusi tentang
fungsi penanganan kesalahan pada akhir Bagian 1 pada Lampiran B. )

Hal selanjutnya yang diperlukan adalah cara membaca atau menulis file setelah dibuka. getc mengembalikan yang berikutnya
karakter dari file; perlu penunjuk file untuk memberi tahu file mana.

int getc (FILE * fp)


getc mengembalikan karakter berikutnya dari aliran yang disebut oleh fp ; mengembalikan EOF untuk akhir file
atau kesalahan.
putc adalah fungsi output:

int putc (int c, FILE * fp)


putc menulis karakter c ke file fp dan mengembalikan karakter yang ditulis, atau EOF jika ada kesalahan
terjadi. Seperti getchar dan putchar , getc dan putc mungkin makro daripada fungsi.
Ketika program C dimulai, lingkungan sistem operasi bertanggung jawab untuk membuka
tiga file dan memberikan petunjuk untuk mereka. File-file ini adalah input standar, standar
output, dan kesalahan standar; pointer file yang sesuai disebut stdin , stdout , dan
stderr , dan dideklarasikan di <stdio.h> . Biasanya stdin terhubung ke keyboard dan
stdout dan stderr terhubung ke layar, tetapi stdin dan stdout dapat dialihkan ke
file atau pipa seperti yang dijelaskan dalam Bagian 7.1 .

Halaman 131

132

getchar dan putchar dapat didefinisikan dalam istilah getc , putc , stdin , dan stdout sebagai berikut:

#define getchar () getc (stdin)


#define putchar (c) putc ((c), stdout)
Untuk input atau output file yang diformat, fungsi fscanf dan fprintf dapat digunakan. Ini
identik dengan scanf dan printf , kecuali bahwa argumen pertama adalah penunjuk file yang menentukan
file yang akan dibaca atau ditulis; string format adalah argumen kedua.

int fscanf (FILE * fp, format char *, ...)


int fprintf (FILE * fp, format char *, ...)
Dengan pendahuluan ini keluar dari jalan, kita sekarang dalam posisi untuk menulis program cat untuk
file menyatukan. Desainnya adalah desain yang nyaman untuk banyak program. Jika
ada argumen command-line, mereka ditafsirkan sebagai nama file, dan diproses secara berurutan. Jika
tidak ada argumen, input standar diproses.

#termasuk <stdio.h>

/ * cat: file gabungan, versi 1 * /


main (int argc, char * argv [])
{
FILE * fp;
membatalkan filecopy (FILE *, FILE *)

if (argc == 1) / * no args; salin input standar * /


filecopy (stdin, stdout);
lain
while (- argc> 0)
if ((fp = fopen (* ++ argv, "r")) == NULL) {
printf ("cat: tidak dapat membuka% s \ n, * argv);
return 1;
} lain {
filecopy (fp, stdout);
fclose (fp);
}
return 0;
}

/ * filecopy: salin file ifp ke file ofp * /


membatalkan filecopy (FILE * ifp, FILE * ofp)
{
int c;

while ((c = getc (ifp))! = EOF)


putc (c, ofp);
}
Pointer file stdin dan stdout adalah objek bertipe FILE * . Mereka adalah konstanta, namun,
bukan variabel, jadi tidak mungkin untuk menetapkannya.
Fungsinya

int fclose (FILE * fp)


adalah kebalikan dari fopen , itu memutuskan koneksi antara penunjuk file dan nama eksternal
yang didirikan oleh fopen , membebaskan penunjuk file untuk file lain. Karena sebagian besar beroperasi
sistem memiliki beberapa batasan pada jumlah file yang mungkin dibuka oleh suatu program secara bersamaan,
itu adalah ide yang baik untuk membebaskan pointer file ketika mereka tidak lagi diperlukan, seperti yang kami lakukan pada kucing .
Ada juga alasan lain untuk fclose pada file output - itu flushes buffer di mana putc
sedang mengumpulkan output. fclose dipanggil secara otomatis untuk setiap file yang terbuka ketika suatu program
berakhir secara normal. (Anda dapat menutup stdin dan stdout jika tidak diperlukan. Mereka juga bisa
dipindahkan oleh fungsi perpustakaan freopen .)

7.6 Penanganan Kesalahan - Stderr dan Keluar

Halaman 132

133
Perawatan kesalahan pada kucing tidak ideal. Masalahnya adalah jika salah satu file tidak bisa
diakses karena suatu alasan, diagnostik dicetak pada akhir output yang disatukan. Bahwa
mungkin dapat diterima jika output akan ke layar, tetapi tidak jika itu masuk ke file atau ke
program lain melalui saluran pipa.
Untuk menangani situasi ini dengan lebih baik, aliran output kedua, disebut stderr , ditugaskan ke a
program dengan cara yang sama seperti stdin dan stdout . Output ditulis pada stderr secara normal
muncul di layar bahkan jika output standar dialihkan.

Mari kita revisi cat untuk menulis pesan kesalahannya pada kesalahan standar.

#termasuk <stdio.h>

/ * cat: file concatenate, versi 2 * /


main (int argc, char * argv [])
{
FILE * fp;
membatalkan filecopy (FILE *, FILE *);
char * prog = argv [0]; / * nama program untuk kesalahan * /

if (argc == 1) / * no args; salin input standar * /


filecopy (stdin, stdout);
lain
while (--argc> 0)
if ((fp = fopen (* ++ argv, "r")) == NULL) {
fprintf (stderr, "% s: tidak dapat membuka% s \ n",
prog, * argv);
keluar (1);
} lain {
filecopy (fp, stdout);
fclose (fp);
}
if (ferror (stdout)) {
fprintf (stderr, "% s: penulisan kesalahan stdout \ n", prog);
keluar (2);
}
keluar (0);
}
Program memberi sinyal kesalahan dalam dua cara. Pertama, output diagnostik yang dihasilkan oleh fprintf
pergi ke stderr , sehingga menemukan jalan ke layar bukannya menghilang ke bawah pipa atau ke
file keluaran. Kami menyertakan nama program, dari argv [0] , dalam pesan, jadi jika ini
Program digunakan dengan orang lain, sumber kesalahan diidentifikasi.
Kedua, program
eksekusi menggunakan
ketika dipanggil. keluar
Argumen fungsitersedia
keluar perpustakaan standarapa
untuk proses , yang
pun mengakhiri
yang disebutprogram
ini
satu, sehingga keberhasilan atau kegagalan program dapat diuji oleh program lain yang menggunakan ini
satu sebagai sub-proses. Secara konvensional, nilai pengembalian 0 sinyal bahwa semuanya baik-baik saja; tidak nol
nilai biasanya menandakan situasi abnormal. keluar panggilan fclose untuk setiap file output terbuka, untuk menyiram
keluar output buffered.

Di dalam main , return expr sama dengan exit ( expr ). exit memiliki keuntungan seperti itu
dipanggil dari fungsi lain, dan panggilan itu dapat ditemukan dengan program pencarian pola
seperti yang ada di Bab 5 .

Fungsi ferror mengembalikan non-nol jika terjadi kesalahan pada aliran fp .

int ferror (FILE * fp)


Meskipun kesalahan output jarang terjadi, mereka memang terjadi (misalnya, jika disk terisi penuh), maka itu merupakan produksi
Program harus memeriksa ini juga.

Halaman 133

134
Function feof (FILE *) analog dengan ferror ; mengembalikan non-nol jika ujung file memiliki
terjadi pada file yang ditentukan.

int feof (FILE * fp)


Kami umumnya tidak khawatir tentang status keluar di program ilustrasi kecil kami, tetapi ada
program serius harus berhati-hati untuk mengembalikan nilai status yang masuk akal dan bermanfaat.

7.7 Line Input dan Output


Pustaka standar menyediakan input dan output FTC rutin yang mirip dengan getline
fungsi yang telah kita gunakan dalam bab-bab sebelumnya:

karakter char * (baris char *, maxline int, FILE * fp)


Gadget membaca baris input berikutnya (termasuk baris baru) dari file fp ke dalam array karakter
garis ; paling banyak karakter maxline-1 akan dibaca. Baris yang dihasilkan diakhiri dengan '\ 0' .
Biasanya garis pengembalian fgets ; pada akhir file atau kesalahan itu mengembalikan NULL . ( Getline kami mengembalikan
panjang garis, yang merupakan nilai yang lebih berguna; nol berarti akhir file.)
Untuk output, fungsi fput menulis string (yang tidak perlu berisi baris baru) ke file:

int fputs (baris char *, FILE * fp)


Ini mengembalikan EOF jika kesalahan terjadi, dan sebaliknya tidak negatif.
Fungsi perpustakaan mendapat dan menempatkan mirip dengan fgets dan fputs , tetapi beroperasi pada stdin
dan stdout . Secara membingungkan, akan menghapus terminating '\ n' , dan menempatkan menambahkannya.

Untuk menunjukkan bahwa tidak ada yang khusus tentang fungsi seperti fgets dan fputs , di sini mereka,
disalin dari perpustakaan standar di sistem kami:

/ * fgets: dapatkan paling banyak n karakter dari iop * /


char * fgets (char * s, int n, FILE * iop)
{
mendaftar int c;
daftar char * cs;

cs = s;
while (--n> 0 && (c = getc (iop))! = EOF)
if ((* cs ++ = c) == '\ n')
istirahat;
* cs = '\ 0';
return (c == EOF && cs == s)? NULL: s;
}

/ * fputs: meletakkan string s pada file iop * /


int fputs (char * s, FILE * iop)
{
int c;

sementara (c = * s ++)
putc (c, iop);
kembalikan ferror (iop)? EOF: 0;
}
Tanpa alasan yang jelas, standar tersebut menetapkan nilai balik yang berbeda untuk ferror dan fput .
Sangat mudah untuk mengimplementasikan getline kami dari gadget :

/ * getline: baca baris, panjang kembali * /


int getline (char * line, int max)
{
if (fgets (line, max, stdin) == NULL)
lain return 0;

Halaman 134

135
return strlen (line);
}
Latihan 7-6. Tulis sebuah program untuk membandingkan dua file, mencetak baris pertama di mana mereka berbeda.
Latihan 7-7. Ubah program pencarian pola pada Bab 5 untuk mengambil input dari serangkaian
bernama file atau, jika tidak ada file yang dinamai argumen, dari input standar. Haruskah file
nama dicetak ketika garis yang cocok ditemukan?

Latihan 7-8. Tulis sebuah program untuk mencetak satu set file, mulai dari yang baru di halaman baru,
dengan judul dan jumlah halaman yang berjalan untuk setiap file.

7.8 Fungsi Lain-lain


Perpustakaan standar menyediakan beragam fungsi. Bagian ini adalah ringkasan singkat dari
sangat berguna. Lebih detail dan banyak fungsi lainnya dapat ditemukan dalam Lampiran B .
7.8.1 Operasi Tali
Kami telah menyebutkan fungsi string strlen , strcpy , strcat , dan strcmp , ditemukan
di <string.h> . Berikut ini, s dan t adalah char * 's, dan c dan n adalah int .
strcat (s, t) menyatukan t sampai akhir s
strncat (s, t, n) menyatukan n karakter dari t hingga akhir s
strcmp (s, t) kembali negatif, nol, atau positif untuk s <t , s == t , s> t
strncmp (s, t, n) sama dengan strcmp tetapi hanya dalam karakter n pertama
strcpy (s, t) salin t ke s
strncpy (s, t, n) menyalin paling banyak n karakter t to s
strlen kembali panjang s
strchr (s, c) kembalikan pointer ke c pertama dalam s , atau NULL jika tidak ada
strrchr (s, c) kembalikan pointer ke c terakhir dalam s , atau NULL jika tidak ada
7.8.2 Pengujian dan Konversi Kelas Karakter
Beberapa fungsi dari <ctype.h> melakukan pengujian dan konversi karakter. Berikut ini, c
adalah int yang dapat direpresentasikan sebagai char atau EOF yang tidak ditandatangani . Fungsi mengembalikan int .
isalpha (c) non-nol jika c adalah alfabet, 0 jika tidak
isupper (c) non-nol jika c adalah huruf besar, 0 jika tidak
islower (c) non-nol jika c adalah huruf kecil, 0 jika tidak
isdigit (c) non-nol jika c adalah digit, 0 jika tidak
isalnum (c) non-nol jika isalpha (c) atau isdigit (c) , 0 jika tidak
isspace (c) non-nol jika c kosong, tab, baris baru, kembali, formfeed, tab vertikal
toupper (c) mengembalikan c dikonversi ke huruf besar
tolower (c) return c dikonversi ke huruf kecil

7.8.3 Batalkan
Pustaka standar menyediakan versi fungsi yang tidak dibatasi, ungetch yang kami tulis
dalam Bab 4 ; itu disebut ungetc .

int ungetc (int c, FILE * fp)


mendorong karakter c kembali ke file fp , dan mengembalikan c , atau EOF untuk kesalahan. Hanya satu
karakter pushback dijamin per file. ungetc dapat digunakan dengan input apa pun
fungsi seperti scanf , getc , atau getchar .
7.8.4 Eksekusi Perintah
Fungsi sistem (char * s) mengeksekusi perintah yang terkandung dalam string karakter s ,
kemudian melanjutkan eksekusi program saat ini. Isi s sangat tergantung pada lokal
sistem operasi. Sebagai contoh sepele, pada sistem UNIX, pernyataan itu

sistem ("tanggal");

Halaman 135

136
menyebabkan tanggal program dijalankan; itu mencetak tanggal dan waktu hari pada output standar.
sistem mengembalikan status integer yang bergantung pada sistem dari perintah yang dijalankan. Di UNIX
sistem, status pengembalian adalah nilai yang dikembalikan dengan keluar .
7.8.5 Manajemen Penyimpanan
Fungsi malloc dan calloc mendapatkan blok memori secara dinamis.
batal * malloc (size_t n)
mengembalikan pointer ke n byte penyimpanan yang tidak diinisialisasi, atau NULL jika permintaan tidak dapat dipenuhi.

batal * calloc (size_t n, size_t size)


mengembalikan pointer ke ruang kosong yang cukup untuk larik n objek dengan ukuran yang ditentukan, atau NULL jika
permintaan tidak dapat dipenuhi. Penyimpanan diinisialisasi ke nol.
Pointer yang dikembalikan oleh malloc atau calloc memiliki keselarasan yang tepat untuk objek tersebut,
tetapi harus dilemparkan ke jenis yang sesuai, seperti pada

int * ip;

ip = (int *) calloc (n, sizeof (int));


free (p) membebaskan ruang yang ditunjuk oleh p , di mana p awalnya diperoleh dengan panggilan ke malloc
atau calloc . Tidak ada batasan pada urutan di mana ruang dibebaskan, tetapi itu mengerikan
kesalahan untuk membebaskan sesuatu yang tidak diperoleh dengan memanggil malloc atau calloc .
Ini juga merupakan kesalahan untuk menggunakan sesuatu setelah itu dibebaskan. Sepotong kode yang khas tetapi tidak benar
apakah loop ini yang membebaskan item dari daftar:

untuk (p = head; p! = NULL; p = p-> selanjutnya) / * SALAH * /


gratis (p);
Cara yang benar adalah menyimpan apa pun yang diperlukan sebelum membebaskan:
untuk (p = head; p! = NULL; p = q) {
q = p-> selanjutnya;
gratis (p);
}
Bagian 8.7 menunjukkan implementasi pengalokasi penyimpanan seperti malloc , di mana dialokasikan
blok dapat dibebaskan dalam urutan apa pun.
7.8.6 Fungsi Matematika
Ada lebih dari dua puluh fungsi matematika yang dideklarasikan dalam <math.h> ; ini ada beberapa
semakin sering digunakan. Masing-masing mengambil satu atau dua argumen ganda dan mengembalikan ganda .
dosa (x) sinus x , x dalam radian
cos (x) cosinus x , x dalam radian
atan2 (y, x) arctangent dari y / x , dalam radian
exp (x) fungsi eksponensial e x
log (x) logaritma natural (basis e ) dari x (x> 0)
log10 (x) logaritma umum (basis 10) dari x (x> 0)
pow (x, y) xy
sqrt (x) akar kuadrat dari x (x> 0)
hebat (x) nilai absolut x
7.8.7 Pembuatan Angka Acak
Fungsi rand () menghitung urutan bilangan bulat pseudo-acak dalam rentang nol hingga
RAND_MAX , yang didefinisikan dalam <stdlib.h> . Salah satu cara untuk menghasilkan floating-point acak
angka lebih besar dari atau sama dengan nol tetapi kurang dari satu

#define frand () ((double) rand () / (RAND_MAX + 1.0))

Halaman 136

137
(Jika perpustakaan Anda sudah menyediakan fungsi untuk angka acak floating-point, itu kemungkinan besar akan terjadi
memiliki sifat statistik yang lebih baik daripada yang ini.)
Function srand (unsigned) mengatur seed untuk rand . Implementasi portabel rand
dan srand yang disarankan oleh standar muncul di Bagian 2.7 .

Latihan 7-9. Fungsi seperti isupper dapat diimplementasikan untuk menghemat ruang atau menghemat waktu.
Jelajahi kedua kemungkinan itu.
Halaman 137

138

Bab 8 - Antarmuka Sistem UNIX


Sistem operasi UNIX menyediakan layanannya melalui serangkaian panggilan sistem , yang masuk
efek fungsi dalam sistem operasi yang dapat dipanggil oleh program pengguna. Bab ini
menjelaskan cara menggunakan beberapa panggilan sistem paling penting dari program C. Jika Anda menggunakan
UNIX, ini harus langsung membantu, karena kadang-kadang perlu menggunakan panggilan sistem
efisiensi maksimum, atau untuk mengakses beberapa fasilitas yang tidak ada di perpustakaan. Bahkan jika Anda menggunakan C pada a
sistem operasi yang berbeda, bagaimanapun, Anda harus dapat memperoleh wawasan tentang pemrograman C.
dari mempelajari contoh-contoh ini; walaupun detailnya bervariasi, kode yang sama akan ditemukan di sistem apa pun.
Karena perpustakaan ANSI C dalam banyak kasus dimodelkan pada fasilitas UNIX, kode ini dapat membantu Anda
pemahaman tentang perpustakaan juga.
Bab ini dibagi menjadi tiga bagian utama: input / output, sistem file, dan alokasi penyimpanan.
Dua bagian pertama mengasumsikan keakraban sederhana dengan karakteristik eksternal UNIX
sistem.

Bab 7 berkaitan dengan antarmuka input / output yang seragam di seluruh operasi
sistem. Pada sistem tertentu, rutinitas pustaka standar harus ditulis
ketentuan fasilitas yang disediakan oleh sistem host. Pada beberapa bagian berikutnya kita akan menjelaskan
Sistem UNIX membutuhkan input dan output, dan menunjukkan bagaimana bagian dari perpustakaan standar dapat
diimplementasikan dengan mereka.

8.1 Penjelas File


Dalam sistem operasi UNIX, semua input dan output dilakukan dengan membaca atau menulis file, karena
semua perangkat periferal, bahkan keyboard dan layar, adalah file dalam sistem file. Ini berarti bahwa a
antarmuka homogen tunggal menangani semua komunikasi antara program dan perangkat
perangkat.
Dalam kasus yang paling umum, sebelum Anda membaca dan menulis file, Anda harus memberi tahu sistem Anda
niat untuk melakukannya, suatu proses yang disebut membuka file. Jika Anda akan menulis di file itu mungkin juga
diperlukan untuk membuatnya atau membuang konten sebelumnya. Sistem memeriksa hak Anda untuk melakukan
jadi (Apakah file itu ada? Apakah Anda memiliki izin untuk mengaksesnya?) dan jika semuanya baik-baik saja, kembali ke
program bilangan bulat kecil non-negatif yang disebut deskriptor file . Setiap kali input atau output ke
dilakukan pada file, deskriptor file digunakan alih-alih nama untuk mengidentifikasi file. (File
deskriptor analog dengan penunjuk file yang digunakan oleh pustaka standar, atau ke pegangan file
MS-DOS.) Semua informasi tentang file terbuka dikelola oleh sistem; program pengguna
merujuk ke file hanya oleh deskriptor file.

Karena input dan output yang melibatkan keyboard dan layar sangat umum, pengaturan khusus
ada untuk membuat ini nyaman. Ketika interpreter perintah (`shell '') menjalankan suatu program,
tiga file terbuka, dengan file deskriptor 0, 1, dan 2, disebut input standar, standar
output, dan kesalahan standar. Jika suatu program membaca 0 dan menulis 1 dan 2, ia dapat melakukan input dan
output tanpa khawatir tentang membuka file.

Pengguna program dapat mengalihkan I / O ke dan dari file dengan <dan>:

prog <infile> outfile


Dalam kasus ini, shell mengubah tugas default untuk deskriptor file 0 dan 1 menjadi
file bernama. Biasanya file deskriptor 2 tetap melekat pada layar, sehingga pesan kesalahan dapat
pergi kesana. Pengamatan serupa berlaku untuk input atau output yang terkait dengan pipa. Dalam semua kasus,
tugas file diubah oleh shell, bukan oleh program. Program tidak tahu
dari mana inputnya berasal atau dari mana outputnya, asalkan menggunakan file 0 untuk input dan 1
dan 2 untuk output.

Halaman 138

139

8.2 I / O Tingkat Rendah - Baca dan Tulis


Input dan output menggunakan panggilan sistem baca dan tulis , yang diakses dari program C.
melalui dua fungsi yang disebut baca dan tulis . Untuk keduanya, argumen pertama adalah deskriptor file.
Argumen kedua adalah array karakter dalam program Anda di mana data akan pergi ke atau ke
berasal dari. Argumen ketiga adalah nomor adalah jumlah byte yang akan ditransfer.
int n_read = baca (int fd, char * buf, int n);
int n_written = write (int fd, char * buf, int n);
Setiap panggilan mengembalikan hitungan jumlah byte yang ditransfer. Saat membaca, jumlah byte
dikembalikan mungkin kurang dari jumlah yang diminta. Nilai pengembalian nol byte menyiratkan akhir
file, dan -1 menunjukkan kesalahan semacam. Untuk menulis, nilai kembali adalah jumlah byte
tertulis; kesalahan telah terjadi jika ini tidak sama dengan nomor yang diminta.
Sejumlah byte dapat dibaca atau ditulis dalam satu panggilan. Nilai yang paling umum adalah 1, yang
berarti satu karakter pada satu waktu (`` unbuffered ''), dan angka seperti 1024 atau 4096 itu
sesuai dengan ukuran blok fisik pada perangkat periferal. Ukuran yang lebih besar akan lebih efisien
karena lebih sedikit panggilan sistem akan dibuat.

Menyatukan fakta-fakta ini, kita dapat menulis sebuah program sederhana untuk menyalin inputnya ke outputnya, the
setara dengan program penyalinan file yang ditulis untuk Bab 1 . Program ini akan menyalin apa pun
untuk apa pun, karena input dan output dapat dialihkan ke file atau perangkat apa pun.

#sertakan "syscalls.h"

main () / * salin input ke output * /


{
char buf [BUFSIZ];
int n;

while ((n = baca (0, buf, BUFSIZ))> 0)


tulis (1, buf, n);
return 0;
}
Kami telah mengumpulkan prototipe fungsi untuk pemanggilan sistem ke file bernama syscalls.h jadi kami
dapat memasukkannya ke dalam program-program bab ini. Namun nama ini tidak standar.
Parameter BUFSIZ juga didefinisikan di syscalls.h ; nilainya adalah ukuran yang baik untuk lokal
sistem. Jika ukuran file bukan kelipatan BUFSIZ , beberapa pembacaan akan menghasilkan jumlah yang lebih kecil
byte ditulis oleh tulis ; panggilan berikutnya untuk membaca setelah itu akan mengembalikan nol.

Sangat instruktif untuk melihat bagaimana membaca dan menulis dapat digunakan untuk membangun rutinitas tingkat yang lebih tinggi
getchar , putchar , dll. Misalnya, di sini adalah versi getchar yang melakukan input unbuffered,
dengan membaca input standar satu karakter pada satu waktu.

#sertakan "syscalls.h"

/ * getchar: input karakter tunggal tanpa buffer * /


int getchar (batal)
{
char c;

kembali (baca (0, & c, 1) == 1)? (unsigned char) c: EOF;


}
c harus menjadi char , karena membaca membutuhkan penunjuk karakter. Casting c ke unsigned char di
pernyataan pengembalian menghilangkan masalah ekstensi tanda.
Versi kedua getchar melakukan input dalam potongan besar, dan membagikan karakter satu per satu
waktu.

Halaman 139

140
#sertakan "syscalls.h"

/ * getchar: versi buffered sederhana * /


int getchar (batal)
{
static char buf [BUFSIZ];
static char * bufp = buf;
static int n = 0;

jika (n == 0) {/ * buffer kosong * /


n = baca (0, buf, sizeof buf);
bufp = buf;
}
kembali (--n> = 0)? (unsigned char) * bufp ++: EOF;
}
Jika versi getchar ini harus dikompilasi dengan <stdio.h> termasuk, itu akan menjadi
diperlukan untuk membatalkan nama getchar jika itu diterapkan sebagai makro.

8.3 Buka, Buat, Tutup, Putuskan tautan


Selain input standar, output dan kesalahan, Anda harus secara eksplisit membuka file secara berurutan
membaca atau menulisnya. Ada dua sistem panggilan untuk ini, terbuka dan creat [sic].
buka agak seperti fopen yang dibahas dalam Bab 7 , kecuali bahwa alih-alih mengembalikan file
pointer, ia mengembalikan deskriptor file, yang hanya sebuah int . buka pengembalian -1 jika terjadi kesalahan.

#termasuk <fcntl.h>

int fd;
int open (char * name, int flags, int perms);

fd = open (name, flags, perms);


Seperti halnya fopen , argumen nama adalah string karakter yang berisi nama file. Kedua
argumen, flags , adalah int yang menentukan bagaimana file akan dibuka; nilai utamanya adalah
O_RDONLY terbuka hanya untuk membaca
O_WRONLY terbuka hanya untuk menulis
O_RDWR terbuka untuk membaca dan menulis

Konstanta-konstanta ini didefinisikan dalam <fcntl.h> pada sistem System V UNIX, dan dalam <sys / file.h>
pada versi Berkeley (BSD).

Untuk membuka file yang sudah ada untuk dibaca,

fd = terbuka (nama, O_RDONLY, 0);


The perms Argumen selalu nol untuk penggunaan terbuka yang akan kita bahas.
Adalah kesalahan untuk mencoba membuka file yang tidak ada. Pembuat panggilan sistem disediakan untuk
buat file baru, atau tulis ulang file yang lama.

int creat (nama char *, perm perm);

fd = creat (name, perms);


mengembalikan deskriptor file jika ia dapat membuat file, dan -1 jika tidak. Jika file sudah ada,
creat akan memotongnya menjadi nol panjang, sehingga membuang konten sebelumnya; itu bukan kesalahan
untuk membuat file yang sudah ada.
Jika file tersebut belum ada, creat membuatnya dengan izin yang ditentukan oleh perms
argumen. Dalam sistem file UNIX, ada sembilan bit informasi izin yang terkait
dengan file yang mengontrol baca, tulis, dan jalankan akses untuk pemilik file, untuk pemiliknya
grup, dan untuk yang lainnya. Dengan demikian angka oktal tiga digit mudah digunakan untuk menentukan

Halaman 140

141
izin. Misalnya, 0775 menentukan baca, tulis, dan jalankan izin untuk pemilik,
dan membaca dan mengeksekusi izin untuk grup dan semua orang.

Sebagai ilustrasi, berikut adalah versi sederhana dari program UNIX cp , yang menyalin satu file
lain. Versi kami hanya menyalin satu file, itu tidak mengizinkan argumen kedua menjadi
direktori, dan ia menciptakan izin alih-alih menyalinnya.

#termasuk <stdio.h>
#termasuk <fcntl.h>
#sertakan "syscalls.h"
#define PERMS 0666 / * RW untuk pemilik, grup, orang lain * /

void error (char *, ...);

/ * cp: salin f1 ke f2 * /
main (int argc, char * argv [])
{
int f1, f2, n;
char buf [BUFSIZ];

jika (argc! = 3)
kesalahan ("Penggunaan: cp dari ke");
if ((f1 = open (argv [1], O_RDONLY, 0)) == -1)
kesalahan ("cp: tidak dapat membuka% s", argv [1]);
if ((f2 = creat (argv [2], PERMS)) == -1)
kesalahan ("cp: tidak dapat membuat% s, mode% 03o",
argv [2], PERMS);
while ((n = baca (f1, buf, BUFSIZ))> 0)
jika (tulis (f2, buf, n)! = n)
error ("cp: write error on file% s", argv [2]);
return 0;
}
Program ini membuat file output dengan izin tetap 0666 . Dengan panggilan sistem stat ,
dijelaskan dalam Bagian 8.6 , kita dapat menentukan mode file yang ada dan dengan demikian memberikan yang sama
mode ke salinan.
Perhatikan bahwa kesalahan fungsi dipanggil dengan daftar argumen variabel seperti printf . Itu
implementasi kesalahan menggambarkan bagaimana menggunakan anggota keluarga printf lainnya. Itu
fungsi perpustakaan standar vprintf seperti printf kecuali daftar argumen variabel
digantikan oleh argumen tunggal yang telah diinisialisasi dengan memanggil makro va_start .
Demikian pula, vfprintf dan vsprintf cocok dengan fprintf dan sprintf .

#termasuk <stdio.h>
#termasuk <stdarg.h>

/ * error: mencetak pesan kesalahan dan mati * /


kekosongan kesalahan (char * fmt, ...)
{
va_list args;

va_start (args, fmt);


fprintf (stderr, "error:");
vprintf (stderr, fmt, args);
fprintf (stderr, "\ n");
va_end (args);
keluar (1);
}
Ada batasan (seringkali sekitar 20) pada jumlah file yang dapat dibuka oleh suatu program
serentak. Dengan demikian, setiap program yang ingin memproses banyak file harus disiapkan
untuk menggunakan kembali deskriptor file. Fungsi close (int fd) memutuskan koneksi antara file
deskriptor dan file terbuka, dan membebaskan deskriptor file untuk digunakan dengan beberapa file lain; Itu

Halaman 141

142
sesuai dengan fclose di pustaka standar kecuali bahwa tidak ada buffer untuk flush.
Pengakhiran suatu program melalui keluar atau kembali dari program utama menutup semua file yang terbuka.
Fungsi unlink (char * name) menghapus nama file dari sistem file. Itu sesuai
untuk menghapus fungsi perpustakaan standar .

Latihan 8-1. Tulis ulang cat program dari Bab 7 menggunakan baca , tulis , buka , dan tutup
bukannya setara perpustakaan standar mereka. Lakukan percobaan untuk menentukan kerabat
kecepatan dua versi.

8.4 Akses Acak - Lseek


Input dan output biasanya berurutan: setiap membaca atau menulis berlangsung pada posisi di
file tepat setelah yang sebelumnya. Namun, bila perlu, file dapat dibaca atau ditulis dalam bentuk apa pun
pesanan sewenang-wenang. System call lseek menyediakan cara untuk bergerak di dalam file tanpa membaca
atau menulis data apa pun:

lseek panjang (int fd, offset panjang, asal int);


set posisi saat ini dalam file yang deskriptornya adalah fd untuk diimbangi , yang diambil relatif terhadap
lokasi yang ditentukan oleh asal . Membaca atau menulis selanjutnya akan dimulai pada posisi itu.
asal dapat 0, 1, atau 2 untuk menentukan bahwa offset harus diukur dari awal, dari
posisi saat ini, atau dari akhir file masing-masing. Misalnya, untuk menambahkan file (file
redirection >> di UNIX shell, atau "a" untuk fopen ), cari sampai akhir sebelum menulis:

lseek (fd, 0L, 2);


Untuk kembali ke awal (`` mundur ''),

lseek (fd, 0L, 0);


Perhatikan argumen 0L ; bisa juga ditulis sebagai (panjang) 0 atau hanya 0 jika lseek benar
dideklarasikan.
Dengan lseek , dimungkinkan untuk memperlakukan file kurang lebih seperti array, dengan harga akses yang lebih lambat.
Sebagai contoh, fungsi berikut membaca sejumlah byte dari sembarang tempat di a
mengajukan. Ini mengembalikan angka yang dibaca, atau -1 pada kesalahan.

#sertakan "syscalls.h"

/ * get: baca n byte dari posisi pos * /


int get (int fd, long post, char * buf, int n)
{
if (lseek (fd, pos, 0)> = 0) / * sampai ke pos * /
return read (fd, buf, n);
lain
return -1;
}
Nilai balik dari lseek adalah panjang yang memberikan posisi baru dalam file, atau -1 jika ada kesalahan
terjadi. Fungsi pustaka standar fseek mirip dengan lseek kecuali argumen pertama
adalah FILE * dan pengembaliannya bukan nol jika terjadi kesalahan.

8.5 Contoh - Implementasi Fopen dan Getc


Mari kita ilustrasikan bagaimana beberapa bagian ini cocok bersama dengan menunjukkan implementasi dari
rutin perpustakaan standar fopen dan getc .
Ingat bahwa file di perpustakaan standar dijelaskan oleh pointer file daripada deskriptor file.
Penunjuk file adalah penunjuk ke struktur yang berisi beberapa bagian informasi tentang
file: pointer ke buffer, sehingga file dapat dibaca dalam potongan besar; hitungan jumlah
karakter yang tersisa di buffer; pointer ke posisi karakter selanjutnya di buffer; berkas
deskriptor; dan bendera yang menjelaskan mode baca / tulis, status kesalahan, dll.

Halaman 142

143
Struktur data yang menggambarkan file terdapat dalam <stdio.h> , yang harus disertakan (oleh
#include ) dalam file sumber apa pun yang menggunakan rutin dari pustaka input / output standar. Itu juga
disertakan oleh fungsi di perpustakaan itu. Dalam kutipan berikut dari nama <stdio.h> yang khas ,
yang dimaksudkan hanya untuk digunakan oleh fungsi-fungsi perpustakaan dimulai dengan garis bawah sehingga mereka
kecil kemungkinannya bertabrakan dengan nama dalam program pengguna. Konvensi ini digunakan oleh semua standar
rutinitas perpustakaan.

#define NULL 0
#define EOF (-1)
#define BUFSIZ 1024
#define OPEN_MAX 20 / * maks #files buka sekaligus * /

typedef struct _iobuf {


int cnt; / * karakter tersisa * /
char * ptr; / * posisi karakter selanjutnya * /
char * base; / * lokasi penyangga * /
bendera int; / * mode akses file * /
int fd; / * file descriptor * /
} FILE;
extern FILE _iob [OPEN_MAX];

#define stdin (& _iob [0])


#define stdout (& _iob [1])
#define stderr (& _iob [2])

enum _flags {
_READ = 01, / * file terbuka untuk dibaca * /
_WRITE = 02, / * file terbuka untuk ditulis * /
_UNBUF = 04, / * file tidak dibuat-buat * /
_EOF = 010, / * EOF telah terjadi pada file ini * /
_ERR = 020 / * kesalahan terjadi pada file ini * /
};

int _fillbuf (FILE *);


int _flushbuf (int, FILE *);

#define feof (p) ((p) -> flag & _EOF)! = 0)


#define ferror (p) ((p) -> flag & _ERR)! = 0)
#define fileno (p) ((p) -> fd)
#define getc (p) (- (p) -> cnt> = 0 \
? (unsigned char) * (p) -> ptr ++: _fillbuf (p))
#define putc (x, p) (- (p) -> cnt> = 0 \
? * (p) -> ptr ++ = (x): _flushbuf ((x), p))

#define getchar () getc (stdin)


#define putcher (x) putc ((x), stdout)
The getc makro biasanya decrements hitungan, kemajuan pointer, dan mengembalikan
karakter. (Ingat bahwa #define yang panjang dilanjutkan dengan garis miring terbalik). Jika hitungan berjalan
negatif, namun, getc memanggil fungsi _fillbuf untuk mengisi kembali buffer, menginisialisasi ulang
struktur konten, dan kembalikan karakter. Karakter dikembalikan unsigned , yang
memastikan bahwa semua karakter akan positif.
Meskipun kami tidak akan membahas detail apa pun, kami telah memasukkan definisi putc untuk menunjukkan hal itu
ini beroperasi dengan cara yang sama seperti getc , memanggil fungsi _flushbuf ketika buffernya penuh.
Kami juga menyertakan makro untuk mengakses kesalahan dan status file akhir dan file
deskriptor.

Fungsi fopen sekarang dapat ditulis. Sebagian besar fopen berkaitan dengan mendapatkan file
dibuka dan diposisikan di tempat yang tepat, dan pengaturan bit bendera untuk menunjukkan keadaan yang tepat.
fopen tidak mengalokasikan ruang buffer apa pun; ini dilakukan oleh _fillbuf ketika file pertama kali dibaca.

Halaman 143

144
#termasuk <fcntl.h>
#sertakan "syscalls.h"
#define PERMS 0666 / * RW untuk pemilik, grup, orang lain * /

FILE * fopen (mode char * nama, mode char *)


{
int fd;
FILE * fp;

if (* mode! = 'r' && * mode! = 'w' && * mode! = 'a')


mengembalikan NULL;
untuk (fp = _iob; fp <_iob + OPEN_MAX; fp ++)
if ((fp-> flag & (_READ | _WRITE)) == 0)
istirahat; / * ditemukan slot gratis * /
if (fp> = _iob + OPEN_MAX) / * tidak ada slot gratis * /
mengembalikan NULL;

if (* mode == 'w')
fd = creat (nama, PERMS);
lain jika (* mode == 'a') {
if ((fd = open (name, O_WRONLY, 0)) == -1)
fd = creat (nama, PERMS);
lseek (fd, 0L, 2);
} lain
fd = terbuka (nama, O_RDONLY, 0);
jika (fd == -1) / * tidak dapat mengakses nama * /
mengembalikan NULL;
fp-> fd = fd;
fp-> cnt = 0;
fp-> base = NULL;
fp-> flag = (* mode == 'r')? _BACA TULIS;
return fp;
}
Versi fopen ini tidak menangani semua kemungkinan mode akses standar,
meskipun menambahkannya tidak akan mengambil banyak kode. Secara khusus, fopen kami tidak mengenali
`` b '' yang memberi sinyal akses biner, karena itu tidak ada artinya pada sistem UNIX, maupun `` + '' yang
memungkinkan membaca dan menulis.
Panggilan pertama untuk getc untuk file tertentu menemukan hitungan nol, yang memaksa panggilan dari
_fillbuf . Jika _fillbuf menemukan bahwa file tersebut tidak terbuka untuk dibaca, ia segera mengembalikan EOF .
Kalau tidak, ia mencoba mengalokasikan buffer (jika membaca akan buffered).

Setelah buffer dibuat, panggilan _fillbuf membaca untuk mengisinya, menetapkan jumlah dan pointer, dan
mengembalikan karakter di awal buffer. Panggilan selanjutnya ke _fillbuf akan menemukan a
buffer dialokasikan.

#sertakan "syscalls.h"

/ * _fillbuf: mengalokasikan dan mengisi buffer input * /


int _fillbuf (FILE * fp)
{
Integrasi;

if ((fp-> flag & (_ BACA | _EOF_ERR))! = _READ)


mengembalikan EOF;
bufsize = (fp-> flag & _UNBUF)? 1: BUFSIZ;
if (fp-> base == NULL) / * belum ada buffer * /
if ((fp-> base = (char *) malloc (bufsize)) == NULL)
mengembalikan EOF; / * tidak bisa mendapatkan buffer * /
fp-> ptr = fp-> basis;
fp-> cnt = baca (fp-> fd, fp-> ptr, bufsize);
if (--fp-> cnt <0) {
if (fp-> cnt == -1)
fp-> flag | = _EOF;

Halaman 144

145
lain
fp-> flag | = _ERR;
fp-> cnt = 0;
mengembalikan EOF;
}
return (unsigned char) * fp-> ptr ++;
}
Satu-satunya jalan keluar yang tersisa adalah bagaimana semuanya dimulai. Array _iob harus ditentukan
dan diinisialisasi untuk stdin , stdout dan stderr :

FILE _iob [OPEN_MAX] = {/ * stdin, stdout, stderr * /


{0, (char *) 0, (char *) 0, _READ, 0},
{0, (char *) 0, (char *) 0, _WRITE, 1},
{0, (char *) 0, (char *) 0, _WRITE, | _UNBUF, 2}
};
Inisialisasi bagian bendera struktur menunjukkan bahwa stdin harus dibaca, stdout adalah untuk
harus ditulis, dan stderr harus ditulis tidak dibuat-buat.
Latihan 8-2. Tulis ulang fopen dan _fillbuf dengan bidang alih-alih operasi bit eksplisit.
Bandingkan ukuran kode dan kecepatan eksekusi.

Latihan 8-3. Desain dan tulis _flushbuf , fflush , dan fclose .

Latihan 8-4. Fungsi perpustakaan standar

int fseek (FILE * fp, offset panjang, asal int)


identik dengan lseek kecuali bahwa fp adalah penunjuk file, bukan file descriptor dan nilai pengembalian
adalah status int , bukan posisi. Tulis fseek . Pastikan fseek Anda berkoordinasi dengan benar
dengan buffering dilakukan untuk fungsi perpustakaan lainnya.

8.6 Contoh - Daftar Direktori


Berbagai jenis interaksi sistem file kadang-kadang disebut untuk - menentukan informasi
tentang file, bukan apa yang dikandungnya. Program daftar direktori seperti perintah UNIX ls
adalah contoh - ia mencetak nama-nama file dalam direktori, dan, opsional, informasi lainnya,
seperti ukuran, izin, dan sebagainya. Perintah MS-DOS dir adalah analog.
Karena direktori UNIX hanyalah sebuah file, ls hanya perlu membacanya untuk mengambil nama file. Tapi ini
diperlukan untuk menggunakan panggilan sistem untuk mengakses informasi lain tentang file, seperti ukurannya. Di
sistem lain, panggilan sistem mungkin diperlukan bahkan untuk mengakses nama file; ini adalah kasus di MS-
DOS misalnya. Apa yang kita inginkan adalah menyediakan akses ke informasi dalam sistem yang relatif-
cara independen, meskipun implementasi mungkin sangat tergantung pada sistem.

Kami akan mengilustrasikan sebagian dari ini dengan menulis sebuah program bernama fsize . fsize adalah bentuk khusus dari
ls yang mencetak ukuran semua file yang disebutkan dalam daftar argumen commandline-nya. Jika salah satu file
direktori, fsize berlaku sendiri secara rekursif ke direktori itu. Jika tidak ada argumen sama sekali, itu
memproses direktori saat ini.

Mari kita mulai dengan ulasan singkat tentang struktur sistem file UNIX. Sebuah direktori adalah sebuah file yang
berisi daftar nama file dan beberapa indikasi di mana mereka berada. `` Lokasi '' adalah sebuah
indeks ke dalam tabel lain yang disebut daftar inode ``. '' Inode untuk file adalah tempat semua informasi
tentang file kecuali namanya disimpan. Entri direktori umumnya hanya terdiri dari dua item,
nama file dan nomor inode.

Sayangnya, format dan isi direktori yang tepat tidak sama pada semua versi
sistem. Jadi kami akan membagi tugas menjadi dua bagian untuk mencoba mengisolasi bagian yang tidak portabel.
Tingkat luar mendefinisikan struktur yang disebut Dirent dan tiga rutinitas opendir , readdir , dan
closedir untuk menyediakan akses sistem-independen ke nama dan nomor inode dalam direktori
masuk. Kami akan menulis ukuran dengan antarmuka ini. Kemudian kami akan menunjukkan bagaimana menerapkannya pada

Halaman 145

146
sistem yang menggunakan struktur direktori yang sama dengan Versi 7 dan System V UNIX; varian adalah
dibiarkan sebagai latihan.
The Dirent struktur berisi nomor inode dan nama. Panjang maksimum a
komponen nama file adalah NAME_MAX , yang merupakan nilai yang bergantung pada sistem. opendir mengembalikan a
pointer ke struktur yang disebut DIR , analog dengan FILE , yang digunakan oleh readdir dan closedir .
Informasi ini dikumpulkan ke dalam file yang disebut dirent.h .

#define NAME_MAX 14 / * komponen nama file terpanjang; * /


/ * tergantung pada sistem * /

typedef struct {/ * entri direktori portabel * /


lama ino; / * nomor inode * /
nama char [NAME_MAX + 1]; / * nama + '\ 0' terminator * /
} Dirent;

typedef struct {/ * minimal DIR: no buffering, etc. * /


int fd; / * file descriptor untuk direktori * /
Dirent d; / * entri direktori * /
} DIR;

DIR * opendir (char * dirname);


Dirent * readdir (DIR * dfd);
void closedir (DIR * dfd);
Stat panggilan sistem mengambil nama file dan mengembalikan semua informasi dalam inode untuk itu
file, atau -1 jika ada kesalahan. Itu adalah,

nama karakter;
st stat stbuf;
stat int (char *, struct stat *);

stat (nama, & stbuf);


mengisi stbuf struktur dengan informasi inode untuk nama file. Struktur menggambarkan
nilai yang dikembalikan oleh stat ada di <sys / stat.h> , dan biasanya terlihat seperti ini:

struct stat / * informasi inode dikembalikan oleh stat * /


{
dev_t st_dev; / * perangkat inode * /
ino_t st_ino; / * nomor inode * /
st_mode pendek; / * mode bit * /
st_nlink pendek; / * jumlah tautan ke file * /
st_uid pendek; / * id pengguna pemilik * /
st_gid pendek; / * id grup pemilik * /
dev_t st_rdev; / * untuk file khusus * /
off_t st_size; / * ukuran file dalam karakter * /
time_t st_atime; / * waktu terakhir diakses * /
time_t st_mtime; / * waktu terakhir diubah * /
time_t st_ctime; / * waktu awalnya dibuat * /
};
Sebagian besar nilai-nilai ini dijelaskan oleh bidang komentar. Jenis-jenis seperti dev_t dan ino_t adalah
didefinisikan dalam <sys / types.h> , yang harus disertakan juga.
The st_mode entri berisi satu set bendera menggambarkan file. Definisi bendera juga
termasuk dalam <sys / types.h> ; kita hanya perlu bagian yang berhubungan dengan jenis file:

#define S_IFMT 0160000 / * jenis file: * /


#define S_IFDIR 0040000 / * direktori * /
#define S_IFCHR 0020000 / * karakter khusus * /
#define S_IFBLK 0060000 / * blok spesial * /
#define S_IFREG 0010000 / * reguler * /
/ * ... * /

Halaman 146

147
Sekarang kita siap untuk menulis program fsize . Jika mode yang diperoleh dari stat menunjukkan itu
sebuah file bukan direktori, maka ukurannya sudah dekat dan dapat dicetak langsung. Jika namanya adalah a
direktori, bagaimanapun, maka kita harus memproses direktori itu satu file pada satu waktu; mungkin pada gilirannya
berisi sub-direktori, sehingga prosesnya bersifat rekursif.
Rutinitas utama berkaitan dengan argumen baris perintah; itu menyerahkan setiap argumen ke fungsi
berhemat .

#termasuk <stdio.h>
#termasuk <string.h>
#sertakan "syscalls.h"
#sertakan sertakan flag <fcntl.h> / * untuk membaca dan menulis * /
#include <sys / types.h> / * typedefs * /
# include struktur <sys / stat.h> / * yang dikembalikan oleh stat * /
#sertakan "dirent.h"

membatalkan fsize (char *)

/ * cetak nama file * /


main (int argc, char ** argv)
{
if (argc == 1) / * default: direktori saat ini * /
fsize (".");
lain
while (--argc> 0)
fsize (* ++ argv);
return 0;
}
Fungsi fsize mencetak ukuran file. Namun, jika file tersebut adalah direktori, tentukan terlebih dahulu
panggilan dirwalk untuk menangani semua file di dalamnya. Perhatikan bagaimana nama bendera S_IFMT dan S_IFDIR yang
digunakan untuk memutuskan apakah file tersebut adalah direktori. Parenthesization penting, karena diutamakan & adalah
lebih rendah dari == .

stat int (char *, struct stat *);


void dirwalk (char *, void (* fcn) (char *));

/ * fsize: cetak nama file "name" * /


membatalkan batal (char * name)
{
st stat stbuf;

if (stat (name, & stbuf) == -1) {


fprintf (stderr, "fsize: tidak dapat mengakses% s \ n", nama);
kembali;
}
if ((stbuf.st_mode & S_IFMT) == S_IFDIR)
dirwalk (nama, ukuran);
printf ("% 8ld% s \ n", stbuf.st_size, nama);
}
Fungsi dirwalk adalah rutin umum yang menerapkan fungsi ke setiap file dalam direktori. Itu
membuka direktori, loop melalui file di dalamnya, memanggil fungsi pada masing - masing, lalu menutup
direktori dan pengembalian. Sejak fsize panggilan dirwalk pada setiap direktori, dua fungsi masing-masing panggilan
lainnya secara rekursif.

#define MAX_PATH 1024

/ * dirwalk: terapkan fcn ke semua file di dir * /


void dirwalk (char * dir, void (* fcn) (char *))
{
nama char [MAX_PATH];
Dirent * dp;
DIR * dfd;

Halaman 147

148
if ((dfd = opendir (dir)) == NULL) {
fprintf (stderr, "dirwalk: tidak dapat membuka% s \ n", dir);
kembali;
}
while ((dp = readdir (dfd))! = NULL) {
if (strcmp (dp-> name, ".") == 0
|| strcmp (dp-> name, ".."))
terus; / * lewati diri dan orang tua * /
if (strlen (dir) + strlen (dp-> nama) +2> sizeof (nama))
fprintf (stderr, "dirwalk: name% s% s terlalu panjang \ n",
dir, dp-> name);
lain {
sprintf (nama, "% s /% s", dir, dp-> nama);
(* fcn) (nama);
}
}
closedir (dfd);
}
Setiap panggilan ke readdir mengembalikan pointer ke informasi untuk file berikutnya, atau NULL ketika ada
tidak ada file yang tersisa. Setiap direktori selalu berisi entri untuk dirinya sendiri, yang disebut "." , dan induknya, ".." ;
ini harus dilewati, atau program akan berulang selamanya.
Ke tingkat terakhir ini, kode tidak tergantung pada bagaimana direktori diformat. Langkah selanjutnya
adalah untuk menyajikan versi minimal dari opendir , readdir , dan closedir untuk sistem tertentu. Itu
rutinitas berikut adalah untuk sistem Versi 7 dan Sistem V UNIX; mereka menggunakan direktori
informasi di header <sys / dir.h> , yang terlihat seperti ini:

#ifndef DIRSIZ
#definisikan DIRSIZ 14
#berakhir jika
struct direct {/ * entri direktori * /
ino_t d_ino; / * nomor inode * /
char d_name [DIRSIZ]; / * nama panjang tidak memiliki '\ 0' * /
};
Beberapa versi sistem mengizinkan nama yang lebih panjang dan memiliki direktori yang lebih rumit
struktur.
Tipe ino_t adalah typedef yang menggambarkan indeks ke dalam daftar inode. Itu terjadi
unsigned kekurangan pada sistem yang kami gunakan secara teratur, tetapi ini bukan jenis informasi untuk
menanamkan dalam program; mungkin berbeda pada sistem yang berbeda, jadi typedef lebih baik. SEBUAH
set lengkap tipe `` sistem '' ditemukan di <sys / types.h> .

opendir membuka direktori, memverifikasi bahwa file tersebut adalah direktori (kali ini oleh panggilan sistem
fstat , yang seperti stat kecuali berlaku untuk deskriptor file), mengalokasikan direktori
struktur, dan mencatat informasi:

int fstat (int fd, struct stat *);

/ * opendir: buka direktori untuk panggilan readdir * /


DIR * opendir (char * dirname)
{
int fd;
st stat stbuf;
DIR * dp;

if ((fd = open (dirname, O_RDONLY, 0)) == -1


|| fstat (fd, & stbuf) == -1
|| (stbuf.st_mode & S_IFMT)! = S_IFDIR
|| (dp = (DIR *) malloc (sizeof (DIR)))) == NULL)
mengembalikan NULL;
dp-> fd = fd;
return dp;
}

Halaman 148

149

closedir menutup file direktori dan membebaskan ruang:


/ * closedir: direktori tertutup dibuka oleh opendir * /
void closedir (DIR * dp)
{
if (dp) {
tutup (dp-> fd);
gratis (dp);
}
}
Akhirnya, readdir menggunakan read untuk membaca setiap entri direktori. Jika slot direktori saat ini tidak ada di
gunakan (karena file telah dihapus), nomor inode adalah nol, dan posisi ini dilewati.
Jika tidak, nomor dan nama inode ditempatkan dalam struktur statis dan penunjuk ke situ
dikembalikan ke pengguna. Setiap panggilan menimpa informasi dari yang sebelumnya.

#include <sys / dir.h> / * struktur direktori lokal * /

/ * readdir: membaca entri direktori secara berurutan * /


Dirent * readdir (DIR * dp)
{
struct direct dirbuf; / * struktur direktori lokal * /
Dirent statis d; / * return: struktur portabel * /

while (baca (dp-> fd, (char *) & dirbuf, sizeof (dirbuf))


== sizeof (dirbuf)) {
if (dirbuf.d_ino == 0) / * slot tidak digunakan * /
terus;
d.ino = dirbuf.d_ino;
strncpy (d.name, dirbuf.d_name, DIRSIZ);
d.name [DIRSIZ] = '\ 0'; / * memastikan penghentian * /
kembali & d;
}
mengembalikan NULL;
}
Meskipun program fsize agak terspesialisasi, ia menggambarkan beberapa hal penting
ide ide. Pertama, banyak program bukan `` program sistem ''; mereka hanya menggunakan informasi saja
dikelola oleh sistem operasi. Untuk program-program seperti itu, sangat penting untuk mewakili
informasi hanya muncul di header standar, dan program-program itu menyertakan header itu
alih-alih menanamkan deklarasi dalam diri mereka sendiri. Pengamatan kedua adalah bahwa dengan hati-hati
adalah mungkin untuk membuat antarmuka ke objek yang bergantung pada sistem yang dengan sendirinya relatif
independen. Fungsi perpustakaan standar adalah contoh yang baik.
Latihan 8-5. Ubah program fsize untuk mencetak informasi lain yang terkandung dalam inode
masuk.

8.7 Contoh - Alokasi Penyimpanan


Dalam Bab 5 , kami menyajikan pengalokasi penyimpanan beragam berorientasi stack yang terbatas. Versi yang kami
sekarang akan menulis tidak dibatasi. Panggilan ke malloc dan gratis dapat terjadi dalam urutan apa pun; panggilan malloc
pada sistem operasi untuk mendapatkan lebih banyak memori yang diperlukan. Rutinitas ini menggambarkan beberapa
pertimbangan yang terlibat dalam penulisan kode mesin-dependen dalam mesin yang relatif
cara independen, dan juga menunjukkan aplikasi kehidupan nyata dari struktur, serikat pekerja dan typedef .
Daripada mengalokasikan dari array ukuran tetap yang dikompilasi, malloc akan meminta ruang dari
sistem operasi sesuai kebutuhan. Karena kegiatan lain dalam program ini juga dapat meminta ruang
tanpa memanggil pengalokasi ini, ruang yang dikelola malloc mungkin tidak bersebelahan. Jadi itu
penyimpanan gratis disimpan sebagai daftar blok gratis. Setiap blok berisi ukuran, pointer ke yang berikutnya
blok, dan ruang itu sendiri. Blok disimpan dalam rangka meningkatkan alamat penyimpanan, dan
blok terakhir (alamat tertinggi) menunjuk ke yang pertama.

Halaman 149

150

Ketika permintaan dibuat, daftar gratis dipindai sampai blok yang cukup besar ditemukan. Ini
Algoritma ini disebut `` first fit, '' berbeda dengan `` best fit, '' yang mencari blok terkecil
itu akan memenuhi permintaan. Jika ukuran blok persis seperti yang diminta, maka tautan itu tidak terhubung dari daftar
dan kembali ke pengguna. Jika blok terlalu besar, itu dibagi, dan jumlah yang tepat dikembalikan ke
pengguna sementara residu tetap pada daftar gratis. Jika tidak ada blok yang cukup besar ditemukan, yang lain
bongkahan besar diperoleh oleh sistem operasi dan dihubungkan ke daftar gratis.

Membebaskan juga menyebabkan pencarian daftar gratis, untuk menemukan tempat yang tepat untuk memasukkan blok
dibebaskan. Jika blok yang dibebaskan berbatasan dengan blok bebas di kedua sisi, itu digabung dengan itu
menjadi satu blok yang lebih besar, sehingga penyimpanan tidak menjadi terlalu terfragmentasi. Menentukan
adjacency mudah karena daftar gratis dikelola dalam rangka mengurangi alamat.

Satu masalah, yang kami singgung di Bab 5 , adalah untuk memastikan bahwa penyimpanan dikembalikan oleh
malloc disejajarkan dengan benar untuk objek yang akan disimpan di dalamnya. Meskipun mesin bervariasi, untuk
setiap mesin ada jenis yang paling ketat: jika jenis yang paling ketat bisa disimpan di
alamat tertentu, semua jenis lainnya mungkin juga. Pada beberapa mesin, tipe yang paling membatasi adalah a
ganda ; pada orang lain, int atau panjang sudah cukup.

Blok gratis berisi pointer ke blok berikutnya dalam rantai, catatan ukuran blok,
dan kemudian ruang bebas itu sendiri; informasi kontrol di awal disebut sebagai `` header. ''
Untuk menyederhanakan perataan, semua blok adalah kelipatan dari ukuran tajuk, dan tajuk disejajarkan
tepat. Ini dicapai oleh gabungan yang berisi struktur header yang diinginkan dan sebuah instance
jenis keselarasan yang paling ketat, yang kami telah sewenang-wenang membuat panjang :

typedef long Align; / * untuk penyelarasan ke batas panjang * /

tajuk serikat { / * blokir judul * /


struct {
tajuk union * ptr; / * blok selanjutnya jika ada dalam daftar gratis * /
ukuran tidak ditandatangani; / * ukuran blok ini * /
} s;
Sejajarkan x; / * memaksa penyelarasan blok * /
};

header header typedef union;


Bidang Align tidak pernah digunakan; itu hanya memaksa setiap header untuk disejajarkan pada kasus terburuk
batas.
Di malloc , ukuran yang diminta dalam karakter dibulatkan ke jumlah header yang tepat
unit; blok yang akan dialokasikan berisi satu unit lagi, untuk header itu sendiri, dan ini
nilai yang terekam dalam bidang ukuran tajuk. Pointer dikembalikan oleh titik malloc di
ruang kosong, bukan di tajuk itu sendiri. Pengguna dapat melakukan apa saja dengan ruang yang diminta, tetapi
jika ada yang tertulis di luar ruang yang dialokasikan daftar kemungkinan akan diacak.

Halaman 150

151
Bidang ukuran diperlukan karena blok yang dikendalikan oleh malloc tidak perlu berdekatan - itu
tidak dimungkinkan untuk menghitung ukuran dengan pointer aritmatika.

Variabel dasar yang digunakan untuk memulai. Jika Freep adalah NULL , karena pada panggilan pertama dari malloc ,
kemudian daftar bebas yang merosot dibuat; itu berisi satu blok ukuran nol, dan menunjuk ke dirinya sendiri. Di
Bagaimanapun, daftar gratis kemudian dicari. Pencarian untuk blok gratis dengan ukuran yang memadai dimulai pada
titik ( freep ) tempat blok terakhir ditemukan; strategi ini membantu menjaga daftar
homogen. Jika blok terlalu besar ditemukan, ujung ekor dikembalikan ke pengguna; dengan cara ini
sundulan dokumen asli hanya perlu disesuaikan ukurannya. Dalam semua kasus, pointer kembali ke
pengguna menunjuk ke ruang kosong di dalam blok, yang dimulai satu unit di luar header.

Header statis; / * daftar kosong untuk memulai * /


Header statis * freep = NULL; / * mulai dari daftar gratis * /

/ * malloc: pengalokasi penyimpanan serba guna * /


void * malloc (nbytes yang tidak ditandatangani)
{
Header * p, * prevp;
Tajuk * moreroce (tidak ditandatangani);
biarawati yang tidak ditandatangani;

nunits = (nbytes + sizeof (Header) -1) / sizeof (header) + 1;


if ((prevp = freep) == NULL) {/ * belum ada daftar gratis * /
base.s.ptr = freeptr = prevptr = & base;
base.s.size = 0;
}
untuk (p = prevp-> s.ptr;; prevp = p, p = p-> s.ptr) {
if (p-> s.size> = nunits) {/ * cukup besar * /
if (p-> s.size == nunits) / * tepat * /
prevp-> s.ptr = p-> s.ptr;
lain { / * mengalokasikan ujung ekor * /
p-> s.size - = nunits;
p + = p-> s.ukuran;
p-> s.ukuran = nunits;
}
freep = prevp;
return (void *) (p + 1);
}
if (p == freep) / * melilit daftar gratis * /
if ((p = morecore (nunits)) == NULL)
mengembalikan NULL; /* tidak ada yang tersisa */
}
}
Fungsi morecore mendapatkan penyimpanan dari sistem operasi. Rincian cara kerjanya
ini bervariasi dari satu sistem ke sistem lainnya. Karena meminta sistem untuk memori adalah relatif
operasi yang mahal. kita tidak ingin melakukan itu pada setiap panggilan ke malloc , sehingga morecore permintaan
setidaknya unit NALLOC ; blok yang lebih besar ini akan dipotong sesuai kebutuhan. Setelah mengatur ukuran
bidang, morecore memasukkan memori tambahan ke arena dengan menelepon gratis .

Halaman 151

152
Panggilan sistem UNIX sbrk (n) mengembalikan pointer ke n byte penyimpanan lebih banyak. sbrk mengembalikan -1
jika tidak ada ruang, meskipun NULL bisa menjadi desain yang lebih baik. The -1 harus cor
untuk char * sehingga dapat dibandingkan dengan nilai pengembalian. Sekali lagi, gips membuat fungsi
relatif kebal terhadap detail representasi pointer pada mesin yang berbeda. Masih ada
satu asumsi, bagaimanapun, bahwa pointer ke blok yang berbeda dikembalikan oleh sbrk dapat
dibandingkan secara bermakna. Ini tidak dijamin oleh standar, yang memungkinkan pointer
perbandingan hanya di dalam array. Dengan demikian versi malloc ini portabel hanya untuk kalangan
mesin yang perbandingan pointer umumnya bermakna.

#define NALLOC 1024 / * minimum #unit untuk meminta * /

/ * morecore: minta sistem untuk lebih banyak memori * /


Header statis * morecore (unsigned nu)
{
char * cp, * sbrk (int);
Tajuk * ke atas;
if (nu <NALLOC)
nu = NALLOC;
cp = sbrk (nu * sizeof (Header));
if (cp == (char *) -1) / * tidak ada ruang sama sekali * /
mengembalikan NULL;
up = (Header *) cp;
up-> s.ukuran = nu;
gratis ((batal *) (naik +1));
freep kembali;
}
membebaskan diri adalah hal terakhir. Itu memindai daftar gratis, mulai dari freep , mencari tempat untuk
masukkan blok gratis. Ini antara dua blok yang ada atau di akhir daftar. Dalam apapun
kasus, jika blok yang dibebaskan berbatasan dengan tetangga, blok yang berdekatan digabungkan.
Satu-satunya masalah adalah menjaga pointer menunjuk ke hal yang benar dan ukurannya benar.
/ * gratis: letakkan ap blokir dalam daftar gratis * /
void gratis (batal * ap)
{
Header * bp, * p;

bp = (Header *) ap - 1; / * arahkan ke blok header * /


untuk (p = freep;! (bp> p && bp <p-> s.ptr); p = p-> s.ptr)
if (p> = p-> s.ptr && (bp> p || bp <p-> s.ptr))
istirahat; / * blok dibebaskan pada awal atau akhir arena * /

if (bp + bp-> size == p-> s.ptr) {/ * gabung ke nbr atas * /


bp-> s.size + = p-> s.ptr-> s.size;
bp-> s.ptr = p-> s.ptr-> s.ptr;
} lain
bp-> s.ptr = p-> s.ptr;
if (p + p-> size == bp) { / * gabung ke bawah nbr * /
p-> s.size + = bp-> s.size;
p-> s.ptr = bp-> s.ptr;
} lain
p-> s.ptr = bp;
freep = p;
}
Meskipun alokasi penyimpanan secara intrinsik bergantung pada mesin, kode di atas menggambarkan caranya
ketergantungan mesin dapat dikontrol dan terbatas pada bagian yang sangat kecil dari program.
Penggunaan typedef dan union menangani penyelarasan (mengingat bahwa sbrk memasok yang sesuai
pointer). Pemain mengatur bahwa konversi penunjuk dibuat eksplisit, dan bahkan mengatasi yang buruk-
antarmuka sistem yang dirancang. Meskipun detail di sini terkait dengan alokasi penyimpanan, namun
pendekatan umum juga berlaku untuk situasi lain.

Halaman 152

153
Latihan 8-6. Calloc fungsi perpustakaan standar (n, ukuran) mengembalikan sebuah pointer ke objek n
ukuran ukuran , dengan penyimpanan diinisialisasi ke nol. Tulis calloc , dengan menelepon malloc atau dengan
memodifikasinya.

Latihan 8-7. malloc menerima permintaan ukuran tanpa memeriksa kemungkinannya; percaya gratis
bahwa blok itu diminta untuk bebas berisi bidang ukuran yang valid. Tingkatkan rutinitas ini sehingga membuatnya
lebih banyak rasa sakit dengan pemeriksaan kesalahan.

Latihan 8-8. Tulis bfree rutin (p, n) yang akan membebaskan blok p sembarang dari n karakter
ke dalam daftar gratis yang dikelola oleh malloc dan gratis . Dengan menggunakan bfree , pengguna dapat menambahkan statis atau
array eksternal ke daftar gratis kapan saja.
Halaman 153

154

Lampiran A - Manual Referensi


A.1 Pendahuluan
Manual ini menjelaskan bahasa C yang ditentukan oleh draft yang diserahkan ke ANSI pada 31 Oktober,
1988, untuk persetujuan sebagai "Standar Amerika untuk Sistem Informasi - Bahasa pemrograman
C, X3.159-1989. '' Manual adalah interpretasi dari standar yang diusulkan, bukan standar
itu sendiri, meskipun telah dilakukan perawatan untuk membuatnya menjadi panduan yang dapat diandalkan untuk bahasa.
Sebagian besar, dokumen ini mengikuti garis besar standar, yang pada gilirannya
mengikuti edisi pertama buku ini, meskipun organisasi berbeda dalam detail. Kecuali
untuk mengganti nama beberapa produksi, dan tidak memformalkan definisi token leksikal atau
preprocessor, tata bahasa yang diberikan di sini untuk bahasa yang tepat setara dengan bahasa
standar.
Di sepanjang manual ini, bahan komentar diindentasi dan ditulis dalam tipe yang lebih kecil, seperti ini. Paling
sering komentar ini menyoroti cara-cara ANSI Standard C berbeda dari bahasa yang didefinisikan oleh
edisi pertama buku ini, atau dari penyempurnaan yang kemudian diperkenalkan dalam berbagai kompiler.

A.2 Konvensi Leksikal


Suatu program terdiri dari satu atau lebih unit terjemahan yang disimpan dalam file. Itu diterjemahkan dalam beberapa
fase, yang dijelaskan dalam Par.A.12 . Fase pertama melakukan transformasi leksikal tingkat rendah,
melaksanakan arahan yang diperkenalkan oleh garis yang dimulai dengan karakter #, dan melakukan makro
definisi dan perluasan. Ketika preprocessing Par.A.12 selesai, program telah
telah direduksi menjadi urutan token.
A.2.1 Token
Ada enam kelas token: pengidentifikasi, kata kunci, konstanta, string literal, operator, dan
pemisah lainnya. Tab kosong, horizontal dan vertikal, baris baru, umpan form dan komentar sebagai
dijelaskan di bawah (secara kolektif, `` ruang putih '') diabaikan kecuali saat mereka memisahkan token.
Diperlukan beberapa ruang putih untuk memisahkan pengidentifikasi, kata kunci, dan
konstanta.
Jika aliran input telah dipisahkan menjadi token hingga karakter yang diberikan, token berikutnya adalah
string karakter terpanjang yang bisa menjadi token.

A.2.2 Komentar
Karakter / * memperkenalkan komentar, yang berakhir dengan karakter * / . Komentar
jangan bersarang, dan mereka tidak muncul dalam string atau karakter literal.
A.2.3 Pengidentifikasi
Identifier adalah urutan huruf dan angka. Karakter pertama harus berupa surat; itu
garis bawah _ dihitung sebagai huruf. Huruf besar dan kecil berbeda. Mungkin pengidentifikasi
berapa pun panjangnya, dan untuk pengidentifikasi internal, setidaknya 31 karakter pertama adalah signifikan; beberapa
implementasi dapat mengambil lebih banyak karakter yang signifikan. Pengidentifikasi internal termasuk preprosesor
nama makro dan semua nama lain yang tidak memiliki tautan eksternal ( Par.A.11.2 ). Pengidentifikasi
dengan tautan eksternal lebih terbatas: implementasi dapat membuat sedikitnya enam yang pertama
karakter signifikan, dan dapat mengabaikan perbedaan huruf.
A.2.4 Kata kunci
Pengidentifikasi berikut dicadangkan untuk penggunaan sebagai kata kunci, dan tidak boleh digunakan sebaliknya:

mobil int ganda struct


istirahat lain panjang beralih

Halaman 154

155
kasus enum daftar typedef
arang extern return union
const mengapung pendek tidak ditandatangani
lanjutkan untuk batal ditandatangani
kebagian standar ukuran volatile
melakukan jika sementara statis
Beberapa implementasi juga mencadangkan kata fortran dan asm .
Kata kunci const , signed , dan volatile adalah baru dengan standar ANSI; enum dan batal
baru sejak edisi pertama, tetapi digunakan secara umum; entri , yang sebelumnya disediakan tetapi tidak pernah digunakan, tidak ada
lebih lama dipesan.

A.2.5 Konstanta
Ada beberapa macam konstanta. Masing-masing memiliki tipe data; Par.A.4.2 membahas jenis-jenis dasar:
konstan:
integer-konstan
karakter-konstan
mengambang-konstan
enumerasi-konstan

A.2.5.1 Konstanta Integer


Konstanta integer yang terdiri dari urutan digit dianggap oktal jika dimulai dengan 0
(digit nol), desimal sebaliknya. Konstanta oktal tidak mengandung angka 8 atau 9 . Urutan
digit yang didahului dengan 0x atau 0X (digit nol) dianggap sebagai bilangan bulat heksadesimal. Heksadesimal
digit termasuk a atau A sampai f atau F dengan nilai 10 hingga 15 .
Konstanta integer dapat diakhiri dengan huruf u atau U , untuk menentukan bahwa itu tidak ditandatangani. Mungkin
juga akan diakhiri dengan huruf l atau L untuk menentukan bahwa ia panjang.

Jenis konstanta integer tergantung pada bentuk, nilai, dan sufiksnya. (Lihat Par.A.4 untuk a
diskusi jenis). Jika tidak disatukan dan desimal, ia memiliki yang pertama dari jenis ini di mana itu
nilai dapat direpresentasikan: int , long int , unsigned long int . Jika tidak disambungkan, oktal atau
heksadesimal, ia memiliki kemungkinan pertama dari jenis ini: int , int unsigned , int panjang , unsigned
int panjang . Jika suffixed oleh u atau U , maka unsigned int , unsigned long int . Jika memang
diakhiri dengan l atau L , lalu int panjang , unsigned long int . Jika konstanta integer diakhiri dengan
UL , ini tidak ditandai lama .
Penjabaran dari tipe konstanta bilangan bulat jauh melampaui edisi pertama, yang
hanya menyebabkan konstanta bilangan bulat besar menjadi panjang . The U sufiks baru.

A.2.5.2 Konstanta Karakter


Konstanta karakter adalah urutan satu atau lebih karakter yang diapit dengan tanda kutip tunggal seperti pada
'x' . Nilai konstanta karakter dengan hanya satu karakter adalah nilai numerik dari
karakter dalam karakter mesin yang ditetapkan pada waktu eksekusi. Nilai multi-karakter
konstanta didefinisikan oleh implementasi.
Konstanta karakter tidak mengandung ' karakter atau baris baru; untuk mewakili mereka, dan
karakter lain tertentu, urutan pelarian berikut dapat digunakan:
garis baru NL (LF) \ n backslash \ \\
tab horisontal HT \ t tanda tanya ? \

tab vertikal VT \ v kutipan tunggal ' \ '


menghapus BS \ b kutipan ganda " \ "

carriage return CR \ r angka oktal ooo \ ooo


formfeed FF \ f nomor hex hh \ xhh
peringatan terdengar BEL \Sebuah

Halaman 155
156
Escape \ ooo terdiri dari garis miring terbalik diikuti oleh 1, 2, atau 3 digit oktal, yang diambil
untuk menentukan nilai karakter yang diinginkan. Contoh umum dari konstruksi ini adalah \ 0 (tidak
diikuti oleh digit), yang menentukan karakter NUL. Pelarian \ xhh terdiri dari
backslash, diikuti oleh x , diikuti oleh digit heksadesimal, yang diambil untuk menentukan nilai
dari karakter yang diinginkan. Tidak ada batasan jumlah digit, tetapi perilaku tidak terdefinisi
jika nilai karakter yang dihasilkan melebihi dari karakter terbesar. Untuk oktal atau
karakter pelarian heksadesimal, jika implementasi memperlakukan tipe char sebagai ditandatangani, nilainya
diperpanjang tanda seolah-olah dilemparkan ke tipe char . Jika karakter mengikuti \ bukan salah satunya
ditentukan, perilaku tidak terdefinisi.

Dalam beberapa implementasi, ada serangkaian karakter yang tidak dapat diwakili dalam
yang arang jenis. Sebuah konstanta dalam set yang diperluas ini ditulis dengan L sebelumnya , misalnya L'x ' ,
dan disebut konstanta karakter lebar. Konstanta semacam itu memiliki tipe wchar_t , tipe integral
didefinisikan dalam tajuk standar <stddef.h> . Seperti halnya konstanta karakter biasa, heksadesimal
lolos dapat digunakan; efeknya tidak terdefinisi jika nilai yang ditentukan melebihi yang diwakili
dengan wchar_t .
Beberapa dari sekuens pelarian ini adalah baru, khususnya representasi karakter heksadesimal.
Karakter yang diperluas juga baru. Set karakter yang biasa digunakan di Amerika dan barat
Eropa dapat dikodekan agar sesuai dengan tipe char ; maksud utama dalam menambahkan wchar_t adalah untuk
mengakomodasi bahasa-bahasa Asia.

A.2.5.3 Konstanta Apung


Konstanta mengambang terdiri dari bagian integer, bagian desimal, bagian fraksi, e atau E , an
opsional integer ditandatangani eksponen dan jenis akhiran opsional, salah satu f , F , l , atau L . Bilangan bulat
dan bagian pecahan keduanya terdiri dari urutan angka. Baik bagian bilangan bulat, atau fraksi
bagian (tidak keduanya) mungkin hilang; baik titik desimal atau e dan eksponen (tidak keduanya)
mungkin hilang. Jenisnya ditentukan oleh akhiran; F atau f membuatnya mengambang , L atau l membuatnya
panjang ganda , kalau tidak ganda .
A2.5.4 Konstanta Pencacahan
Identifier yang dinyatakan sebagai enumerator (lihat Par.A.8.4 ) adalah konstanta dari tipe int .
A.2.6 String Literals
String literal, juga disebut konstanta string, adalah urutan karakter yang dikelilingi oleh double
kutipan seperti dalam "..." . Sebuah string memiliki tipe `` array karakter '' dan kelas penyimpanan statis (lihat
Par.A.3 di bawah) dan diinisialisasi dengan karakter yang diberikan. Apakah string literal identik atau tidak
berbeda adalah implementasi-didefinisikan, dan perilaku suatu program yang mencoba untuk mengubah string
literal tidak terdefinisi.
Literal string yang berdekatan disatukan menjadi string tunggal. Setelah penggabungan, null
byte \ 0 ditambahkan ke string sehingga program yang memindai string dapat menemukan akhirnya. Tali
literal tidak mengandung karakter baris baru atau kutipan ganda; untuk mewakili mereka, sama saja
melarikan diri urutan untuk konstanta karakter tersedia.

Seperti halnya konstanta karakter, string literal dalam set karakter diperluas ditulis dengan a
sebelumnya L , seperti pada L "..." . Literal string karakter lebar memiliki jenis `` larik wchar_t . ''
Penggabungan literal string biasa dan lebar tidak terdefinisi.
Spesifikasi string literal tidak perlu berbeda, dan larangan memodifikasi mereka,
baru dalam standar ANSI, seperti juga gabungan string literal yang berdekatan. String karakter lebar
literal baru.

A.3 Notasi Sintaks


Dalam notasi sintaksis yang digunakan dalam manual ini, kategori sintaksis ditunjukkan oleh tipe italic , dan
kata dan karakter literal dengan gaya mesin tik . Kategori alternatif biasanya tercantum pada
garis terpisah; dalam beberapa kasus, serangkaian panjang alternatif sempit disajikan pada satu baris, ditandai

Halaman 156

157
dengan frasa `` salah satu. '' Terminal opsional atau simbol nonterminal membawa subskrip `` opt , ''
jadi, misalnya,
{ Ekspresi opt }

berarti ekspresi opsional, terlampir dalam kurung kurawal. Sintaksis diringkas dalam Par.A.13 .
Berbeda dengan tata bahasa yang diberikan dalam edisi pertama buku ini, yang diberikan di sini lebih diutamakan dan
asosiasi operator ekspresi eksplisit.

A.4 Arti Pengidentifikasi


Pengidentifikasi, atau nama, merujuk pada berbagai hal: fungsi; tag struktur, serikat pekerja, dan
pencacahan; anggota struktur atau serikat pekerja; konstanta enumerasi; nama typedef; dan
benda. Objek, kadang-kadang disebut variabel, adalah lokasi dalam penyimpanan, dan interpretasinya
tergantung pada dua atribut utama: kelas penyimpanan dan jenisnya . Kelas penyimpanan menentukan
masa penyimpanan terkait dengan objek yang diidentifikasi; tipe menentukan arti
nilai-nilai yang ditemukan di objek yang diidentifikasi. Nama juga memiliki cakupan, yang merupakan wilayah dari
program di mana diketahui, dan hubungan, yang menentukan apakah nama yang sama di
ruang lingkup lain mengacu pada objek atau fungsi yang sama. Lingkup dan keterkaitan dibahas dalam
Par.A.11 .
A.4.1 Kelas Penyimpanan
Ada dua kelas penyimpanan: otomatis dan statis. Beberapa kata kunci, bersama dengan
konteks deklarasi objek, tentukan kelas penyimpanannya. Objek otomatis adalah lokal ke a
blok ( Par.9.3 ), dan dibuang saat keluar dari blok. Deklarasi di dalam blok buat
objek otomatis jika tidak ada spesifikasi kelas penyimpanan yang disebutkan, atau jika specifier otomatis digunakan.
Objek yang dideklarasikan mendaftar otomatis, dan (jika mungkin) disimpan dalam register cepat
mesin.
Objek statis mungkin lokal ke blok atau eksternal untuk semua blok, tetapi dalam kedua kasus mempertahankannya
nilai lintas keluar dari dan masuk kembali ke fungsi dan blok. Dalam satu blok, termasuk satu blok
yang menyediakan kode untuk suatu fungsi, objek statis dideklarasikan dengan kata kunci statis . Itu
objek yang dideklarasikan di luar semua blok, pada level yang sama dengan definisi fungsi, selalu statis.
Mereka dapat dibuat lokal ke unit terjemahan tertentu dengan menggunakan kata kunci statis ; ini
memberi mereka hubungan internal . Mereka menjadi global ke seluruh program dengan menghilangkan eksplisit
kelas penyimpanan, atau dengan menggunakan kata kunci extern ; ini memberi mereka hubungan eksternal .

A.4.2 Jenis Dasar


Ada beberapa tipe mendasar. Header standar <Limit.h> dijelaskan dalam Lampiran
B mendefinisikan nilai-nilai terbesar dan terkecil dari setiap jenis dalam implementasi lokal. Angka-angka
diberikan dalam Lampiran B menunjukkan besaran terkecil yang dapat diterima.
Objek yang dideklarasikan sebagai karakter ( char ) cukup besar untuk menyimpan anggota eksekusi
set karakter. Jika karakter asli dari set itu disimpan dalam objek char , nilainya
setara dengan kode integer untuk karakter, dan tidak negatif. Jumlah lain mungkin
disimpan ke dalam variabel char , tetapi rentang nilai yang tersedia, dan terutama apakah nilainya
ditandatangani, tergantung pada implementasi.

Karakter yang tidak ditandai menyatakan karakter yang tidak ditandatangani mengkonsumsi ruang yang sama dengan polos
karakter, tetapi selalu tampil non-negatif; karakter yang ditandatangani secara eksplisit menyatakan karakter yang ditandatangani
juga mengambil ruang yang sama dengan karakter biasa.
tipe char unsigned tidak muncul dalam edisi pertama buku ini, tetapi umum digunakan. tertanda
char baru.

Selain tipe char , hingga tiga ukuran integer, menyatakan int pendek , int , dan panjang int ,
tersedia. Objek int polos memiliki ukuran alami yang disarankan oleh mesin host

Halaman 157

158
Arsitektur; ukuran lain disediakan untuk memenuhi kebutuhan khusus. Bilangan bulat yang lebih panjang tersedia di
paling tidak sebanyak penyimpanan yang lebih pendek, tetapi implementasi dapat membuat bilangan bulat polos
setara dengan bilangan bulat pendek, atau bilangan bulat panjang. Semua tipe int mewakili nilai yang ditandatangani
kecuali ditentukan sebaliknya.

Bilangan bulat tak bertanda , dinyatakan menggunakan kata kunci tidak bertanda , mematuhi hukum modulo aritmatika
2 n di mana n adalah jumlah bit dalam representasi, dan dengan demikian aritmatika pada unsigned
jumlah tidak pernah bisa meluap. Himpunan nilai-nilai non-negatif yang dapat disimpan dalam ditandatangani
objek adalah subset dari nilai-nilai yang dapat disimpan dalam objek unsigned yang sesuai, dan
representasi untuk nilai yang tumpang tindih adalah sama.

Setiap floating point presisi tunggal ( float ), floating point presisi ganda ( ganda ), dan
floating point presisi ekstra ( panjang ganda ) mungkin identik, tetapi yang kemudian dalam daftar
setidaknya setepat yang sebelumnya.
dobel panjang adalah baru. Edisi pertama dibuat float panjang setara dengan ganda ; lokasinya memiliki
telah ditarik.

Enumerasi adalah tipe unik yang memiliki nilai integral; terkait dengan setiap enumerasi adalah a
set konstanta bernama ( Par.A.8.4 ). Enumerasi berperilaku seperti bilangan bulat, tetapi umum untuk a
kompiler untuk mengeluarkan peringatan ketika objek enumerasi tertentu diberikan sesuatu
selain dari salah satu konstanta, atau ekspresi dari tipenya.

Karena objek jenis ini dapat diartikan sebagai angka, mereka akan disebut sebagai
jenis aritmatika . Jenis char , dan int dari semua ukuran, masing-masing dengan atau tanpa tanda, dan juga
jenis pencacahan, secara kolektif akan disebut jenis integral . Jenis melayang , ganda , dan
double double akan disebut tipe mengambang .

The kekosongan jenis menentukan himpunan kosong dari nilai-nilai. Ini digunakan sebagai tipe yang dikembalikan oleh fungsi-fungsi itu
tidak menghasilkan nilai.
A.4.3 Jenis turunan
Di samping tipe dasar, ada kelas konseptual tipe turunan tak terbatas yang dibangun dari
tipe dasar dengan cara berikut:
array objek dari tipe yang diberikan;
fungsi mengembalikan objek dari tipe yang diberikan;
pointer ke objek dari tipe yang diberikan;
struktur yang mengandung urutan objek dari berbagai jenis;
serikat pekerja mampu mengandung salah satu dari beberapa objek dari berbagai jenis.

Secara umum metode ini membangun objek dapat diterapkan secara rekursif.

A.4.4 Jenis Kualifikasi


Jenis objek mungkin memiliki kualifikasi tambahan. Mendeklarasikan sebuah objek const mengumumkan bahwa objeknya
nilai tidak akan berubah; mendeklarasikannya volatile mengumumkan bahwa ia memiliki properti khusus
relevan dengan optimasi. Kualifikasi tidak memengaruhi rentang nilai atau properti aritmatika
objek. Kualifikasi dibahas dalam Par.A.8.2 .

A.5 Obyek dan Nilai


Sebuah Object merupakan wilayah bernama penyimpanan; sebuah nilai adalah ekspresi yang mengacu pada objek. Sebuah
contoh nyata dari ekspresi nilai adalah pengidentifikasi dengan tipe dan kelas penyimpanan yang sesuai.
Ada operator yang menghasilkan nilai, jika E adalah ekspresi dari tipe pointer, maka * E adalah nilai
Ekspresi mengacu pada objek yang ditunjuk E. Nama `` lvalue '' berasal dari
ekspresi penugasan E1 = E2 di mana operan kiri E1 harus berupa ekspresi nilai. Itu

Halaman 158

159
diskusi masing-masing operator menentukan apakah mereka mengharapkan nilai operan dan apakah itu menghasilkan
sebuah nilai.

A.6 Konversi
Beberapa operator dapat, tergantung pada operan mereka, menyebabkan konversi nilai suatu
operan dari satu jenis ke jenis lainnya. Bagian ini menjelaskan hasil yang diharapkan dari hal tersebut
konversi. Par.6.5 merangkum konversi yang diminta oleh sebagian besar operator biasa; itu akan
ditambah sebagaimana disyaratkan oleh diskusi masing-masing operator.
A.6.1 Promosi Integral
Karakter, integer pendek, atau bidang bit integer, semuanya bertanda tangan atau tidak, atau objek
tipe enumerasi, dapat digunakan dalam ekspresi dimanapun integer dapat digunakan. Jika suatu int
dapat mewakili semua nilai dari tipe aslinya, kemudian nilainya dikonversi ke int ; jika tidak
nilainya dikonversikan menjadi int yang tidak ditandatangani . Proses ini disebut promosi integral .
A.6.2 Konversi Integral
Setiap bilangan bulat dikonversi ke tipe unsigned yang diberikan dengan menemukan nilai non-negatif terkecil
yang kongruen dengan bilangan bulat itu, modulo satu lebih dari nilai terbesar yang bisa
terwakili dalam tipe yang tidak ditandatangani. Dalam representasi komplemen dua, ini setara dengan
pemotongan kiri jika pola bit dari tipe unsigned lebih sempit, dan untuk zero-filling unsigned
nilai dan perluasan tanda yang ditandatangani nilai jika tipe yang tidak ditandatangani lebih luas.
Ketika bilangan bulat mana pun dikonversi menjadi tipe yang ditandatangani, nilainya tidak berubah jika dapat diwakili
dalam tipe baru dan implementasi-ditentukan sebaliknya.

A.6.3 Integer dan Floating


Ketika nilai tipe mengambang dikonversi ke tipe integral, bagian fraksinya dibuang; jika
nilai yang dihasilkan tidak dapat direpresentasikan dalam tipe integral, perilaku tidak terdefinisi. Di
khususnya, hasil dari konversi nilai mengambang negatif ke tipe integral yang tidak ditandatangani tidak
ditentukan.
Ketika nilai tipe integral dikonversi menjadi mengambang, dan nilai tersebut di representable
rentang tetapi tidak dapat direpresentasikan dengan tepat, maka hasilnya mungkin lebih tinggi berikutnya atau berikutnya
nilai keterwakilan yang lebih rendah. Jika hasilnya di luar jangkauan, perilaku tidak terdefinisi.

A.6.4 Jenis Apung


Ketika nilai mengambang yang kurang tepat dikonversi ke jenis mengambang yang sama atau lebih tepat, the
nilai tidak berubah. Ketika nilai mengambang yang lebih tepat dikonversi ke mengambang yang kurang tepat
ketik, dan nilainya berada dalam kisaran yang dapat direpresentasikan, hasilnya mungkin lebih tinggi berikutnya atau
selanjutnya nilai keterwakilan yang lebih rendah. Jika hasilnya di luar jangkauan, perilaku tidak terdefinisi.
A.6.5 Konversi Aritmatika
Banyak operator menyebabkan konversi dan menghasilkan tipe hasil dengan cara yang sama. Efeknya adalah membawa
operan menjadi tipe umum, yang juga merupakan jenis hasilnya. Pola ini disebut
konversi aritmatika biasa .
â € ¢ Pertama, jika salah satu operan panjang ganda , yang lain dikonversi menjadi panjang ganda .

â € ¢ Kalau tidak, jika salah satu operan ganda , yang lain dikonversi menjadi ganda .
â € ¢ Kalau tidak, jika salah satu operan float , yang lain dikonversi menjadi float .
â € ¢ Kalautidak, promosi integral dilakukan pada kedua operan; lalu, jika salah satunya
operan unsigned long int , yang lain dikonversi menjadi long int unsigned .

Halaman 159

160
â € ¢ Kalau tidak, jika satu operan adalah int panjang dan yang lainnya adalah int unsigned , efeknya
tergantung pada apakah sebuah int panjang dapat mewakili semua nilai dari int unsigned ; jika begitu,
yang unsigned int operan dikonversikan ke int panjang ; jika tidak, keduanya dikonversi menjadi
int panjang unsigned .
â € ¢ Kalau tidak, jika satu operan adalah int panjang , yang lain dikonversi menjadi int panjang .
â € ¢ Jika tidak, jika salah satu operan tidak ditandatangani , yang lain akan dikonversi menjadi tidak ditandatangani
int .
â € ¢ Jika tidak, kedua operan memiliki tipe int .
Ada dua perubahan di sini. Pertama, aritmatika pada operan float dapat dilakukan dalam presisi tunggal,
bukannya ganda; edisi pertama menetapkan bahwa semua aritmatika mengambang adalah presisi ganda. Kedua,
tipe unsign yang lebih pendek, bila dikombinasikan dengan tipe yang lebih besar yang ditandatangani, jangan menyebarkan yang tidak ditandatangani
properti ke tipe hasil; di edisi pertama, yang bertanda tangan selalu mendominasi. Aturan baru adalah
sedikit lebih rumit, tetapi kurangi kejutan yang mungkin terjadi ketika orang yang tidak ditandatangani
kuantitas bertemu ditandatangani. Hasil yang tidak terduga masih dapat terjadi ketika ekspresi yang tidak ditandatangani dibandingkan dengan
ekspresi yang ditandatangani dengan ukuran yang sama.

A.6.6 Pointer dan Integer


Ekspresi tipe integral dapat ditambahkan atau dikurangkan dari sebuah pointer; dalam kasus seperti itu
ekspresi integral dikonversi seperti ditentukan dalam diskusi operator tambahan
( Par.7.7 ).
Dua pointer ke objek dari tipe yang sama, dalam array yang sama, dapat dikurangi; hasilnya adalah
dikonversi menjadi bilangan bulat seperti yang ditentukan dalam diskusi operator pengurangan ( Par.A.7.7 ).

Ekspresi konstanta integral dengan nilai 0, atau ekspresi yang dilemparkan untuk mengetik void * , mungkin
dikonversikan, oleh pemain, dengan tugas, atau dengan perbandingan, ke pointer dari jenis apa pun. Ini
menghasilkan pointer nol yang sama dengan pointer nol lain dari jenis yang sama, tetapi tidak sama dengan
pointer apa pun ke fungsi atau objek.

Konversi tertentu lainnya yang melibatkan pointer diizinkan, tetapi memiliki implementasi yang ditentukan
aspek Mereka harus ditentukan oleh operator konversi tipe eksplisit, atau pemain (Pars. A.7.5
dan A.8.8 ).

Pointer dapat dikonversi ke tipe integral yang cukup besar untuk menahannya; ukuran yang dibutuhkan adalah
tergantung pada implementasi. Fungsi pemetaan juga tergantung pada implementasi.

Pointer ke satu tipe dapat dikonversi ke pointer ke tipe lain. Pointer yang dihasilkan mungkin
menyebabkan pengecualian pengalamatan jika pointer subjek tidak merujuk ke objek yang selaras dengannya
penyimpanan. Dijamin bahwa pointer ke objek dapat dikonversi ke pointer ke objek
yang jenisnya membutuhkan perataan penyimpanan yang kurang atau sama ketatnya dan kembali lagi tanpa perubahan; itu
Gagasan `` alignment '' bergantung pada implementasi, tetapi objek dari tipe char memiliki paling sedikit
persyaratan pelurusan yang ketat. Seperti dijelaskan dalam Par.A.6.8 , sebuah pointer juga dapat dikonversi ke
ketik void * dan kembali lagi tanpa perubahan.

Pointer dapat dikonversi ke pointer lain yang tipenya sama kecuali untuk penambahan
atau penghapusan kualifikasi (Pars. A.4.4 , A.8.2 ) dari tipe objek yang dirujuk oleh pointer. Jika
kualifikasi ditambahkan, pointer baru setara dengan yang lama kecuali untuk pembatasan yang tersirat oleh
kualifikasi baru. Jika kualifikasi dihapus, operasi pada objek yang mendasarinya tetap tunduk
untuk kualifikasi dalam deklarasi aktualnya.

Akhirnya, pointer ke suatu fungsi dapat dikonversi ke pointer ke tipe fungsi lain. Panggilan
fungsi yang ditentukan oleh pointer yang dikonversi bergantung pada implementasi; Namun, jika
pointer dikonversi dikonversi ke tipe aslinya, hasilnya identik dengan aslinya
penunjuk.
Halaman 160
161

A.6.7 Batal
Nilai (tidak ada) dari objek batal tidak dapat digunakan dengan cara apa pun, dan tidak eksplisit maupun
konversi implisit ke jenis apa pun yang tidak berlaku dapat diterapkan. Karena ekspresi kosong menunjukkan a
nilai tidak ada, ungkapan seperti itu hanya dapat digunakan jika nilainya tidak diperlukan, untuk
contoh sebagai pernyataan ekspresi ( Par.A.9.2 ) atau sebagai operan kiri dari operator koma
( Par.7.78 ).
Ekspresi dapat dikonversi untuk mengetikkan void oleh pemain. Misalnya, void membuang dokumen
membuang nilai pemanggilan fungsi yang digunakan sebagai pernyataan ekspresi.
kekosongan tidak muncul dalam edisi pertama buku ini, tetapi sudah menjadi hal umum sejak saat itu.

A.6.8 Petunjuk untuk Membatalkan


Setiap pointer ke objek dapat dikonversi untuk mengetikkan void * tanpa kehilangan informasi. Jika
hasil dikonversi kembali ke tipe pointer asli, pointer asli dipulihkan. Tidak seperti itu
konversi pointer-to-pointer yang dibahas dalam Par.A.6.6 , yang umumnya memerlukan eksplisit
cast, pointer dapat ditugaskan ke dan dari pointer tipe batal * , dan dapat dibandingkan
dengan mereka.
Interpretasi pointer kosong * ini baru; sebelumnya, pointer char * berperan
pointer umum. Standar ANSI secara khusus memberkati pertemuan pointer kosong dengan objek
pointer dalam penugasan dan relasi, sementara membutuhkan gips eksplisit untuk campuran pointer lainnya.

A.7 Ekspresi
Diutamakan operator ekspresi adalah sama dengan urutan subbagian utama dari
bagian ini, diutamakan tertinggi terlebih dahulu. Jadi, misalnya, ekspresi yang disebut sebagai
operan + ( Par.A.7.7 ) adalah ekspresi yang didefinisikan dalam Pars. A.7.1 - A.7.6 . Di dalam masing-masing
ayat, operator memiliki prioritas yang sama. Asosiatif kiri atau kanan ditentukan dalam
setiap ayat untuk operator yang dibahas di sini. Tata bahasa yang diberikan dalam Par.13 menggabungkan
diutamakan dan asosiasi dari operator.
Diutamakan dan asosiasi operator sepenuhnya ditentukan, tetapi urutan evaluasi
ekspresi adalah, dengan pengecualian tertentu, tidak terdefinisi, bahkan jika subekspresi melibatkan sisi
efek. Yaitu, kecuali definisi operator menjamin bahwa operannya dievaluasi
dalam urutan tertentu, implementasinya bebas untuk mengevaluasi operan dalam urutan apa pun, atau bahkan untuk
interleave evaluasi mereka. Namun, setiap operator menggabungkan nilai yang dihasilkan olehnya
operan dengan cara yang kompatibel dengan parsing ekspresi yang muncul.
Aturan ini mencabut kebebasan sebelumnya untuk menyusun ulang ekspresi dengan operator yang secara matematis
komutatif dan asosiatif, tetapi bisa gagal menjadi asosiatif komputasi. Perubahan hanya mempengaruhi
perhitungan floating-point dekat batas akurasinya, dan situasi di mana meluap
bisa jadi.

Penanganan overflow, cek pembagian, dan pengecualian lain dalam evaluasi ekspresi tidak
didefinisikan oleh bahasa. Sebagian besar implementasi C mengabaikan overflow dalam evaluasi
menandatangani ekspresi dan penugasan integral, tetapi perilaku ini tidak dijamin. Pengobatan
pembagian dengan 0, dan semua pengecualian floating-point, bervariasi di antara implementasi; terkadang itu
dapat disesuaikan dengan fungsi perpustakaan non-standar.

A.7.1 Konversi Pointer


Jika jenis ekspresi atau subekspresi adalah array ` T , '' untuk beberapa tipe T , maka nilai
ekspresi adalah penunjuk ke objek pertama dalam array, dan jenis ekspresinya adalah
diubah menjadi `` penunjuk ke T. '' Konversi ini tidak terjadi jika ekspresi dalam
operan unary & operator, atau ++ , - , sizeof , atau sebagai operan kiri dari penugasan
operator atau . operator. Demikian pula, ekspresi tipe `` berfungsi mengembalikan T , '' kecuali
ketika digunakan sebagai operan & operator, dikonversi menjadi `` penunjuk ke fungsi mengembalikan T. ''
A.7.2 Ekspresi Primer

Halaman 161

162
Ekspresi primer adalah pengidentifikasi, konstanta, string, atau ekspresi dalam tanda kurung.
ekspresi primer
pengidentifikasi
konstan
tali
(ekspresi)

Identifier adalah ekspresi utama, asalkan telah dinyatakan sesuai sebagaimana dibahas di bawah ini.
Jenisnya ditentukan oleh deklarasi. Identifier adalah nilai jika mengacu pada objek
( Par.A.5 ) dan jika tipenya adalah aritmatika, struktur, gabungan, atau penunjuk.

Konstanta adalah ekspresi utama. Jenisnya tergantung pada bentuknya seperti yang dibahas dalam Par.A.2.5 .
String literal adalah ekspresi utama. Jenisnya awalnya `` array char '' (untuk wide-char
string, `` array of wchar_t ''), tetapi mengikuti aturan yang diberikan pada Par.A.7.1 , ini biasanya dimodifikasi
ke `` pointer to char '' ( wchar_t ) dan hasilnya adalah pointer ke karakter pertama dalam string.
Konversi juga tidak terjadi pada inisialisasi tertentu; lihat Par.A.8.7 .

Ekspresi kurung adalah ekspresi primer yang jenis dan nilainya identik dengan mereka
dari ekspresi tanpa hiasan. Diutamakan tanda kurung tidak mempengaruhi apakah
ekspresi adalah nilai.

A.7.3 Ekspresi Postfix


Operator dalam grup ekspresi postfix dari kiri ke kanan.
ekspresi postfix:
ekspresi primer
postfix-expression [ekspresi]
postfix-expression (argumen-ekspresi-daftar opt )
postfix-expression.identifier
postfix-expression -> identifier
postfix-expression ++
ekspresi postfix -

argumen-ekspresi-daftar:
penugasan-ekspresi
penugasan-ekspresi-daftar , penugasan-ekspresi

A.7.3.1 Referensi Array


Ekspresi postfix diikuti oleh ekspresi dalam tanda kurung adalah ekspresi postfix
menunjukkan referensi array yang disubkripsikan. Salah satu dari dua ekspresi harus bertipe `` pointer ke
T '', di mana T adalah beberapa tipe, dan yang lainnya harus memiliki tipe integral; jenis subskrip
ekspresi T . Ekspresi E1 [E2] identik (menurut definisi) ke * ((E1) + (E2)) . Lihat
Par.A.8.6.2 untuk diskusi lebih lanjut.
A.7.3.2 Panggilan Fungsi
Panggilan fungsi adalah ekspresi postfix, yang disebut penunjuk fungsi, diikuti oleh tanda kurung
mengandung daftar ekspresi penugasan yang mungkin kosong dan dipisahkan koma ( Par.A7.17 ),
yang merupakan argumen untuk fungsi tersebut. Jika ekspresi postfix terdiri dari
pengidentifikasi yang tidak ada pernyataan dalam lingkup saat ini, pengidentifikasi secara implisit
dinyatakan seolah-olah deklarasi
extern int identifier ();

Halaman 162

163
telah diberikan di blok paling dalam yang berisi pemanggilan fungsi. Ekspresi postfix
(setelah kemungkinan deklarasi dan pembuatan pointer secara eksplisit, Par.A7.1 ) harus bertipe `` pointer
untuk fungsi yang mengembalikan T , '' untuk beberapa jenis T , dan nilai dari panggilan fungsi memiliki tipe T .
Dalam edisi pertama, jenisnya dibatasi untuk fungsi ``, '' dan operator * eksplisit diperlukan untuk
memanggil pointer ke fungsi. Standar ANSI memberkati praktik beberapa kompiler yang ada
dengan mengizinkan sintaks yang sama untuk panggilan ke fungsi dan ke fungsi yang ditentukan oleh pointer. Yang lebih tua
sintaks masih dapat digunakan.

Argumen istilah digunakan untuk ekspresi yang dilewati oleh panggilan fungsi; parameter istilahnya adalah
digunakan untuk objek input (atau pengenalnya) yang diterima oleh definisi fungsi, atau dijelaskan dalam a
deklarasi fungsi. Istilah `argumen aktual (parameter) '' dan` `argumen formal
(parameter) '' masing-masing terkadang digunakan untuk perbedaan yang sama.

Dalam mempersiapkan panggilan ke suatu fungsi, salinan dibuat dari setiap argumen; semua lewat argumen
secara ketat berdasarkan nilai. Suatu fungsi dapat mengubah nilai objek parameternya, yaitu salinan
dari ekspresi argumen, tetapi perubahan ini tidak dapat memengaruhi nilai argumen.
Namun, dimungkinkan untuk melewatkan pointer pada pemahaman bahwa fungsi tersebut dapat mengubah
nilai objek yang ditunjuk oleh pointer.

Ada dua gaya di mana fungsi dapat dideklarasikan. Dalam gaya baru, jenis
parameter bersifat eksplisit dan merupakan bagian dari jenis fungsi; deklarasi seperti itu juga os
disebut prototipe fungsi. Dalam gaya lama, tipe parameter tidak ditentukan. Fungsi
deklarasi dikeluarkan dalam Pars.A.8.6.3 dan A.10.1 .

Jika deklarasi fungsi dalam lingkup panggilan adalah gaya lama, maka promosi argumen default adalah
diterapkan pada setiap argumen sebagai berikut: promosi integral ( Par.A.6.1 ) dilakukan pada masing- masing argumen
argumen tipe integral, dan setiap argumen float dikonversi menjadi dua kali lipat . Efek dari
panggilan tidak terdefinisi jika jumlah argumen tidak setuju dengan jumlah parameter dalam
definisi fungsi,
parameter yang atau jikaJenis
sesuai. jenisperjanjian
argumen setelah promosi
tergantung pada tidak setuju
apakah dengan
definisi definisi
fungsi itu
gaya baru atau gaya lama. Jika gaya lama, maka perbandingannya adalah antara jenis yang dipromosikan
argumen panggilan, dan tipe parameter yang dipromosikan, jika definisinya baru-
style, tipe argumen yang dipromosikan harus berupa parameter itu sendiri, tanpa
promosi.

Jika pernyataan fungsi dalam ruang lingkup untuk panggilan adalah gaya baru, maka argumen dikonversi, sebagai
jika dengan penugasan, untuk jenis parameter yang sesuai dari prototipe fungsi. Itu
jumlah argumen harus sama dengan jumlah parameter yang dijelaskan secara eksplisit,
kecuali daftar parameter deklarasi berakhir dengan notasi ellipsis (, ...) . Dalam hal itu,
jumlah argumen harus sama atau melebihi jumlah parameter; argumen tertinggal
di luar parameter yang diketik secara eksplisit mengalami promosi argumen default seperti yang dijelaskan dalam
paragraf sebelumnya. Jika definisi fungsi adalah gaya lama, maka jenisnya masing-masing
parameter dalam definisi, setelah tipe parameter definisi telah menjalani argumen
promosi.
Aturan-aturan ini sangat rumit karena harus memenuhi campuran gaya lama dan gaya baru
fungsi. Campuran harus dihindari jika memungkinkan.

Urutan evaluasi argumen tidak ditentukan; perhatikan bahwa berbagai kompiler berbeda.
Namun, argumen dan penunjuk fungsi sepenuhnya dievaluasi, termasuk semua
efek samping, sebelum fungsi dimasukkan. Panggilan rekursif ke fungsi apa pun diizinkan.

A.7.3.3 Referensi Struktur


Ekspresi postfix diikuti oleh titik diikuti oleh pengenal adalah ekspresi postfix. Itu
ekspresi operan pertama harus berupa struktur atau gabungan, dan pengidentifikasi harus memberi nama anggota
dari struktur atau persatuan. Nilainya adalah nama anggota dari struktur atau persatuan, dan nilainya

Halaman 163

164
tipe adalah tipe anggota. Ekspresi adalah nilai jika ekspresi pertama adalah nilai,
dan jika jenis ekspresi kedua bukan tipe array.
Ekspresi postfix diikuti oleh panah (dibangun dari - dan > ) diikuti oleh pengidentifikasi adalah a
ekspresi postfix. Ekspresi operan pertama harus menjadi penunjuk ke struktur atau gabungan, dan
pengidentifikasi harus memberi nama anggota struktur atau gabungan. Hasilnya mengacu pada nama
anggota dari struktur atau gabungan yang menunjuk titik penunjuk, dan tipenya adalah
jenis anggota; hasilnya adalah nilai jika tipe bukan tipe array.

Dengan demikian ungkapan E1-> MOS sama dengan (* E1) .MOS . Struktur dan serikat dibahas dalam
Par.A.8.3 .
Dalam edisi pertama buku ini, sudah menjadi aturan bahwa nama anggota dalam ekspresi seperti itu
menjadi bagian dari struktur atau gabungan yang disebutkan dalam ekspresi postfix; Namun, sebuah catatan mengakui hal itu
aturan ini tidak ditegakkan dengan tegas. Kompiler baru-baru ini, dan ANSI, menegakkannya.

A.7.3.4 Penambahan Postfix


Ekspresi postfix diikuti oleh ++ atau - operator adalah ekspresi postfix. Nilai dari
Ekspresi adalah nilai operan. Setelah nilainya dicatat, operan bertambah ++
atau dikurangi - dengan 1. Operand harus merupakan nilai tambah; lihat pembahasan aditif
operator ( Par.A.7.7 ) dan penugasan ( Par.A.7.17 ) untuk kendala lebih lanjut tentang operan dan
rincian operasi. Hasilnya bukan nilai.
A.7.4 Operator Unary
Ekspresi dengan grup operator unary kanan-ke-kiri.
ekspresi unary :
ekspresi postfix
++ ekspresi unary
- Ekspresi unary
ekspresi pemain unary-operator
ukuran ekspresi unary
sizeof ( tipe-nama )

operator unary: salah satu dari


& * + - ~!

A.7.4.1 Operator Penambahan Awalan


Ekspresi unary diikuti oleh operator ++ atau - adalah ekspresi unary. Operan adalah
incremented ++ atau decremented - by 1. Nilai dari ekspresi adalah nilai setelah
incrementation (decrementation). Operan harus merupakan nilai; lihat pembahasan aditif
operator ( Par.A.7.7 ) dan penugasan ( Par.A.7.17 ) untuk kendala lebih lanjut pada operan dan
rincian operasi. Hasilnya bukan nilai.
A.7.4.2 Alamat Operator
Operator unary & mengambil alamat operandnya. Operand harus merupakan lvalue yang merujuk
baik untuk bidang bit maupun objek yang dideklarasikan sebagai register , atau harus bertipe fungsi. Itu
result adalah pointer ke objek atau fungsi yang disebut oleh lvalue. Jika jenis operan
adalah T , tipe hasilnya adalah `` pointer to T. ''
A.7.4.3 Operator Tidak Langsung
Operator unary * menunjukkan tipuan, dan mengembalikan objek atau fungsi yang padanya
poin operan. Ini adalah nilai jika operan adalah pointer ke objek aritmatika, struktur,
union, atau tipe pointer. Jika jenis ekspresi adalah `` pointer ke T , '' jenis hasilnya adalah T .
A.7.4.4 Operator Unary Plus

Halaman 164

165
Operand operator unary + harus memiliki tipe aritmatika, dan hasilnya adalah nilai
operan. Operan integral mengalami promosi integral. Jenis hasilnya adalah
jenis operan yang dipromosikan.
Unary + baru dengan standar ANSI. Itu ditambahkan untuk simetri dengan unary - .

A.7.4.5 Operator Minus Unary


Operan dari unary - operator harus memiliki jenis aritmatika, dan hasilnya adalah negatif
operan nya. Operan integral mengalami promosi integral. Negatif dari yang tidak ditandatangani
kuantitas dihitung dengan mengurangi nilai yang dipromosikan dari nilai terbesar yang dipromosikan
ketik dan tambahkan satu; tetapi nol negatif adalah nol. Jenis hasilnya adalah jenis
operan yang dipromosikan.
A.7.4.6 Operator Pelengkap Seseorang
Operand ~ operator harus memiliki tipe integral, dan hasilnya adalah komplemen satu
operan nya. Promosi integral dilakukan. Jika operan tidak ditandatangani, hasilnya adalah
dihitung dengan mengurangi nilai dari nilai terbesar dari jenis yang dipromosikan. Jika operan
ditandatangani, hasilnya dihitung dengan mengonversi operan yang dipromosikan ke yang sesuai
tipe yang tidak ditandatangani, menerapkan ~ , dan mengonversi kembali ke tipe yang ditandatangani. Jenis hasilnya adalah
jenis operan yang dipromosikan.
A.7.4.7 Operator Negasi Logika
Operan ! operator harus memiliki tipe aritmatika atau menjadi penunjuk, dan hasilnya adalah 1 jika
nilai operandnya membandingkan sama dengan 0, dan 0 sebaliknya. Jenis hasilnya adalah int .
A.7.4.8 Ukuran Operator
The sizeof hasil operator jumlah byte yang diperlukan untuk menyimpan objek dari jenis yang
operan. Operand adalah ekspresi, yang tidak dievaluasi, atau tipe kurung
nama. Ketika sizeof diterapkan ke char , hasilnya adalah 1; ketika diterapkan ke sebuah array, hasilnya adalah
jumlah total byte dalam array. Ketika diterapkan pada struktur atau gabungan, hasilnya adalah
jumlah byte dalam objek, termasuk padding yang diperlukan untuk membuat ubin objek array:
ukuran array n elemen adalah n kali ukuran satu elemen. Operator mungkin tidak
diterapkan pada operan tipe fungsi, atau tipe tidak lengkap, atau ke bidang bit. Hasilnya adalah
konstanta integral tak bertanda; tipe tertentu ditentukan oleh implementasi. Header standar
<stddef.h> (Lihat lampiran B ) mendefinisikan jenis ini sebagai size_t .

A.7.5 Pemain
Ekspresi unary yang diawali dengan nama yang dipatenkan menyebabkan konversi
nilai ekspresi ke tipe bernama.
cast-expression:
ekspresi unary
(ketik-nama) ekspresi-pemeran

Konstruksi ini disebut gips . Nama-nama tersebut dijelaskan dalam Par.A.8.8 . Efek dari
konversi dijelaskan pada Par.A.6 . Ekspresi dengan pemain bukanlah nilai.

A.7.6 Operator Multiplikatif


Operator multiplikatif * , / , dan % grup kiri-ke-kanan.
ekspresi multiplikatif :
multiplicative-expression * cast-expression
multi-ekspresi / pemeran-ekspresi
multiplicative-expression % cast-expression
Halaman 165

166
Operand * dan / harus memiliki tipe aritmatika; operan % harus memiliki tipe integral.
Konversi aritmatika yang biasa dilakukan pada operan, dan memprediksi tipe
hasil.

Operator biner * menunjukkan penggandaan.

Biner / operator menghasilkan hasil bagi, dan operator % sisanya, dari pembagian
operan pertama oleh kedua; jika operan kedua adalah 0, hasilnya tidak ditentukan. Kalau tidak, itu
selalu benar bahwa (a / b) * b + a% b sama dengan a . Jika kedua operan non-negatif, maka
sisanya adalah non-negatif dan lebih kecil dari pembagi, jika tidak, hanya dijamin bahwa
nilai absolut sisanya lebih kecil dari nilai absolut pembagi.

A.7.7 Operator Aditif


Operator aditif + dan - grup kiri-ke-kanan. Jika operan memiliki tipe aritmatika, maka
konversi aritmatika biasa dilakukan. Ada beberapa kemungkinan tipe tambahan untuk
setiap operator.
ekspresi-aditif :
ekspresi multiplikatif
additive-expression + multiplicative-expression
aditif-ekspresi - ekspresi multiplikatif

Hasil dari operator + adalah jumlah dari operan. Pointer ke objek dalam array dan a
nilai dari setiap tipe integral dapat ditambahkan. Yang terakhir dikonversi ke alamat diimbangi oleh
mengalikannya dengan ukuran objek yang ditunjuk oleh pointer. Jumlahnya adalah pointer dari
jenis yang sama dengan pointer asli, dan menunjuk ke objek lain dalam array yang sama, secara tepat
mengimbangi dari objek aslinya. Jadi, jika P adalah pointer ke objek dalam array, ekspresi
P + 1 adalah pointer ke objek berikutnya dalam array. Jika jumlah penunjuk menunjuk di luar batas
array, kecuali pada lokasi pertama di luar high-end, hasilnya tidak terdefinisi.
Ketentuan untuk pointer tepat di luar akhir array adalah baru. Ini melegitimasi idiom umum untuk
pengulangan elemen array.

Hasil dari - operator adalah perbedaan dari operan. Nilai dari semua tipe integral mungkin
dikurangkan dari sebuah pointer, dan kemudian konversi dan ketentuan yang sama seperti untuk penambahan
menerapkan.

Jika dua pointer ke objek dari jenis yang sama dikurangi, hasilnya adalah nilai integral yang ditandatangani
mewakili perpindahan antara objek yang diarahkan ke; pointer ke objek berturut-turut
berbeda dengan 1. Jenis hasil didefinisikan sebagai ptrdiff_t di header standar <stddef.h> .
Nilai tidak ditentukan kecuali pointer menunjuk ke objek dalam array yang sama; Namun, jika P
menunjuk ke anggota terakhir array, lalu (P + 1) -P memiliki nilai 1.

A.7.8 Operator Pergeseran


Operator shift << dan >> grup kiri-ke-kanan. Untuk kedua operator, setiap operan harus
integral, dan tunduk pada integral promosi. Jenis hasilnya adalah bahwa dari
operan kiri yang dipromosikan. Hasilnya tidak terdefinisi jika operan kanan negatif, atau lebih besar dari
atau sama dengan jumlah bit dalam tipe ekspresi kiri.
pergeseran-ekspresi :
ekspresi aditif
shift-expression << aditif-ekspresi
shift-expression >> additive-expression

Nilai E1 << E2 adalah E1 (ditafsirkan sebagai pola bit) bit E2 bergeser kiri ; tanpa adanya
overflow, ini setara dengan perkalian dengan 2 E2 . Nilai E1 >> E2 adalah E1 bergeser ke kanan E2

Halaman 166

167
posisi bit. Pergeseran kanan setara dengan pembagian dengan 2 E2 jika E1 tidak ditandatangani atau memiliki
nilai negatif; jika tidak hasilnya adalah implementasi yang ditentukan.

A.7.9 Operator Relasional


Grup operator relasional dari kiri ke kanan, tetapi fakta ini tidak berguna; a <b <c diuraikan sebagai
(a <b) <c , dan mengevaluasi antara 0 atau 1.
ekspresi relasional :
pergeseran-ekspresi
ekspresi-relasional < ekspresi-bergeser
ekspresi-relasional > ekspresi-pergeseran
ekspresi relasional <= pergeseran-ekspresi
relational-expression > = shift-ekspresi

Operator < (kurang), > (lebih besar), <= (kurang atau sama) dan > = (lebih besar atau sama) semua menghasilkan 0 jika
hubungan yang ditentukan salah dan 1 jika itu benar. Jenis hasilnya adalah int . Aritmatika yang biasa
konversi dilakukan pada operan aritmatika. Pointer ke objek dengan tipe yang sama
(mengabaikan kualifikasi apa pun) dapat dibandingkan; hasilnya tergantung pada lokasi relatif di
ruang alamat dari objek yang diarahkan ke. Perbandingan pointer didefinisikan hanya untuk bagian
objek yang sama; jika dua petunjuk menunjuk ke objek sederhana yang sama, mereka membandingkan sama; jika
pointer adalah untuk anggota dari struktur yang sama, pointer ke objek yang dinyatakan kemudian dalam
struktur membandingkan lebih tinggi; jika pointer merujuk ke anggota array, perbandingannya adalah
setara dengan perbandingan dari subskrip yang sesuai. Jika P menunjuk ke anggota terakhir dari
sebuah array, kemudian P + 1 membandingkan lebih tinggi dari P , meskipun P + 1 menunjukkan di luar array.
Jika tidak, perbandingan pointer tidak ditentukan.
Aturan-aturan ini sedikit meliberalisasi pembatasan yang dinyatakan dalam edisi pertama, dengan mengizinkan perbandingan
menunjuk ke anggota yang berbeda dari suatu struktur atau persatuan. Mereka juga melegalkan perbandingan dengan pointer saja
dari akhir array.

A.7.10 Operator Kesetaraan


persamaan ekspresi :
ekspresi relasional
persamaan persamaan == ekspresi relasional
persamaan-persamaan ! = ekspresi relasional
Operator == (sama dengan) dan ! = (Tidak sama dengan) adalah analog dengan operator relasional
kecuali untuk prioritas mereka yang lebih rendah. (Jadi a <b == c <d adalah 1 setiap kali a <b dan c <d memiliki
nilai kebenaran yang sama.)

Operator kesetaraan mengikuti aturan yang sama dengan operator relasional, tetapi mengizinkan tambahan
kemungkinan: pointer dapat dibandingkan dengan ekspresi integral konstan dengan nilai 0, atau ke a
arahkan ke batal . Lihat Par.A.6.6 .

A.7.11 Bitwise DAN Operator


DAN-ekspresi :
persamaan-ekspresi
DAN-ekspresi & persamaan-ekspresi
Konversi aritmatika biasa dilakukan; hasilnya adalah fungsi bitwise DAN dari
operan. Operator hanya berlaku untuk operan integral.

A.7.12 Bitwise Exclusive ATAU Operator


eksklusif-ATAU-ekspresi :
DAN-ekspresi
eksklusif-ATAU-ekspresi ^ DAN-ekspresi

Halaman 167

168
Konversi aritmatika biasa dilakukan; hasilnya adalah fungsi ATAU bitwise eksklusif
dari operan. Operator hanya berlaku untuk operan integral.

A.7.13 Bitwise Inclusive ATAU Operator


inklusif-ATAU-ekspresi :
eksklusif-ATAU-ekspresi
inklusif-ATAU-ekspresi | eksklusif-ATAU-ekspresi
Konversi aritmatika biasa dilakukan; hasilnya adalah fungsi ATAU bitwise inklusif
dari operan. Operator hanya berlaku untuk operan integral.

A.7.14 Logis DAN Operator


logis-DAN-ekspresi :
inklusif-ATAU-ekspresi
logis-DAN-ekspresi && inklusif-ATAU-ekspresi
Grup && operator kiri-ke-kanan. Ini mengembalikan 1 jika kedua operannya membandingkan tidak sama dengan nol,
0 sebaliknya. Tidak seperti & , && menjamin evaluasi dari kiri ke kanan: operan pertama dievaluasi,
termasuk semua efek samping; jika sama dengan 0, nilai ekspresi adalah 0. Jika tidak, hak
operan dievaluasi, dan jika sama dengan 0, nilai ekspresi adalah 0, jika tidak 1.

Operan tidak harus memiliki tipe yang sama, tetapi masing-masing harus memiliki tipe aritmatika atau menjadi pointer.
Hasilnya adalah int .

A.7.15 Logis ATAU Operator


logis-ATAU-ekspresi :
logis-DAN-ekspresi
logical-OR-expression || logis-DAN-ekspresi
The || grup operator kiri-ke-kanan. Ini mengembalikan 1 jika salah satu operannya membandingkan tidak sama dengan
nol, dan 0 sebaliknya. Berbeda dengan | , || menjamin evaluasi kiri-ke-kanan: operan pertama adalah
dievaluasi, termasuk semua efek samping; jika tidak sama dengan 0, nilai ekspresi adalah 1.
Jika tidak, operan yang tepat dievaluasi, dan jika tidak sama dengan 0, nilai ekspresi adalah 1,
jika tidak 0.

Operan tidak harus memiliki tipe yang sama, tetapi masing-masing harus memiliki tipe aritmatika atau menjadi pointer.
Hasilnya adalah int .

A.7.16 Operator Bersyarat


ekspresi bersyarat :
logis-ATAU-ekspresi
logis-ATAU-ekspresi ? Ekspresi : ekspresi-kondisional
Ekspresi pertama dievaluasi, termasuk semua efek samping; jika dibandingkan tidak sama dengan 0, maka
hasilnya adalah nilai dari ekspresi kedua, jika tidak dari ekspresi ketiga. Hanya satu
operan kedua dan ketiga dievaluasi. Jika operan kedua dan ketiga adalah aritmatika, maka
konversi aritmatika biasa dilakukan untuk membawa mereka ke jenis umum, dan tipe itu
jenis hasilnya. Jika keduanya batal , atau struktur atau serikat dari jenis yang sama, atau petunjuk untuk
objek dengan tipe yang sama, hasilnya memiliki tipe yang sama. Jika satu adalah pointer dan yang lainnya adalah
konstan 0, 0 dikonversi ke tipe pointer, dan hasilnya memiliki tipe itu. Jika ada a
pointer ke void dan yang lainnya adalah pointer lain, pointer lain dikonversi menjadi pointer ke
batal , dan itu adalah jenis hasilnya.

Halaman 168

169
Dalam perbandingan jenis untuk pointer, kualifikasi jenis apa pun ( Par.A.8.2 ) dalam jenis yang
poin pointer tidak signifikan, tetapi tipe hasil mewarisi kualifikasi dari kedua lengan
bersyarat.

A.7.17 Ekspresi Penugasan


Ada beberapa operator penugasan; semua grup kanan-ke-kiri.
penugasan-ekspresi :
ekspresi bersyarat
penugasan-ekspresi tugas-operator-ekspresi unary

operator penugasan : salah satu


= * = / =% = + = - = << = >> = & = ^ = | =

Semua membutuhkan nilai sebagai operan kiri, dan nilai harus dapat dimodifikasi: itu harus bukan array,
dan tidak boleh memiliki tipe yang tidak lengkap, atau menjadi fungsi. Juga, jenisnya tidak boleh memenuhi syarat
dengan const ; jika itu adalah struktur atau persatuan, ia tidak boleh memiliki anggota atau, secara rekursif,
submember memenuhi syarat dengan const . Jenis ekspresi penugasan adalah yang kiri
operan, dan nilainya adalah nilai yang disimpan di operan kiri setelah penugasan diambil
tempat.

Dalam penugasan sederhana dengan = , nilai ekspresi menggantikan nilai dari objek yang dirujuk
oleh nilai. Salah satu dari yang berikut ini harus benar: kedua operan memiliki tipe aritmatika, dalam
kasus mana operan kanan dikonversi ke jenis kiri oleh penugasan; atau keduanya
operan adalah struktur atau serikat dengan tipe yang sama; atau satu operan adalah pointer dan yang lainnya adalah
pointer ke void , atau operan kiri adalah pointer dan operan kanan adalah konstan
ekspresi dengan nilai 0; atau kedua operan adalah pointer ke fungsi atau objek yang tipenya
sama kecuali untuk kemungkinan tidak adanya const atau volatile di operan kanan.

Ekspresi bentuk E1 op = E2 setara dengan E1 = E1 op (E2) kecuali bahwa E1 adalah


dievaluasi hanya sekali.

A.7.18 Operator Koma


ekspresi :
penugasan-ekspresi
ekspresi , penugasan-ekspresi
Sepasang ekspresi yang dipisahkan oleh koma dievaluasi dari kiri ke kanan, dan nilai dari kiri
Ekspresi dibuang. Jenis dan nilai hasilnya adalah jenis dan nilai yang tepat
operan. Semua efek samping dari evaluasi operan kiri diselesaikan sebelumnya
memulai evaluasi operan yang tepat. Dalam konteks di mana koma diberikan khusus
artinya, misalnya dalam daftar argumen fungsi ( Par.A.7.3.2 ) dan daftar inisialisasi
( Par.A.8.7 ), unit sintaksis yang diperlukan adalah ekspresi penugasan, jadi operator koma
hanya muncul dalam pengelompokan tanda kurung, misalnya,

f (a, (t = 3, t + 2), c)
memiliki tiga argumen, yang kedua memiliki nilai 5.
A.7.19 Ekspresi Konstan
Secara sintaksis, ekspresi konstan adalah ekspresi yang dibatasi untuk subset operator:
ekspresi konstan :
ekspresi bersyarat

Halaman 169

170
Ekspresi yang mengevaluasi ke konstanta diperlukan dalam beberapa konteks: setelah kasus , sebagai array
batas dan panjang bidang bit, sebagai nilai konstanta enumerasi, pada inisialisasi, dan dalam
ekspresi preprocessor tertentu.

Ekspresi konstan mungkin tidak mengandung penugasan, kenaikan atau penurunan operator, fungsi
panggilan, atau operator koma; kecuali dalam operan sizeof . Jika ekspresi konstannya adalah
harus integral, operannya harus terdiri dari integer, enumerasi, karakter, dan
konstanta mengambang; gips harus menentukan tipe integral, dan konstanta mengambang apa pun harus dilemparkan
ke integer. Ini tentu mengesampingkan susunan, tipuan, alamat, dan struktur anggota
operasi. (Namun, setiap operan diizinkan untuk sizeof .)

Lebih banyak garis lintang diizinkan untuk ekspresi konstan inisialisasi; operan bisa berupa apa saja
jenis konstanta, dan operator unary & dapat diterapkan ke objek eksternal atau statis, dan untuk
array eksternal dan statis disubkripsikan dengan ekspresi konstan. Unary & operator bisa
juga diterapkan secara tersirat oleh penampilan array dan fungsi yang tidak berlangganan. Inisialisasi harus
mengevaluasi baik ke konstanta atau ke alamat objek eksternal atau statis yang dinyatakan sebelumnya
plus atau minus konstanta.

Garis lintang yang lebih sedikit diperbolehkan untuk ekspresi konstanta integral setelah # jika ; ukuran ekspresi,
konstanta enumerasi, dan gips tidak diizinkan. Lihat Par.A.12.5 .

A.8 Deklarasi
Deklarasi menentukan interpretasi yang diberikan kepada masing-masing pengidentifikasi; mereka tidak perlu memesan
penyimpanan yang terkait dengan pengidentifikasi. Deklarasi bahwa penyimpanan cadangan disebut definisi .
Deklarasi memiliki formulir
deklarasi :
penentu deklarasi memilih init-declarator-list opt ;

Deklarator dalam daftar init-deklarator berisi pengidentifikasi yang dideklarasikan; deklarasi-


specifier terdiri dari urutan tipe dan specifier kelas penyimpanan.

penentu deklarasi :
storage-class-specifier deklarasi-specifier memilih
type-specifier declaration-specifiers opt
tipe-kualifikasi deklarasi-specifier memilih

init-declarator-list :
init-declarator
init-declarator-list , init-declarator

init-declarator :
deklarator
deklarator = penginisialisasi

Deklarator akan dibahas kemudian ( Par.A.8.5 ); mereka berisi nama-nama yang dideklarasikan. SEBUAH
deklarasi harus memiliki setidaknya satu deklarator, atau specifier tipenya harus mendeklarasikan tag struktur,
tag serikat, atau anggota enumerasi; deklarasi kosong tidak diizinkan.

A.8.1 Penentu Kelas Penyimpanan


Penentu kelas penyimpanan adalah:
specifier kelas penyimpanan :
mobil
daftar
Halaman 170

171
statis
luar
mengetik

Arti dari kelas penyimpanan dibahas dalam Par.A.4.4 .

The auto dan mendaftar specifier memberikan objek dinyatakan kelas penyimpanan otomatis, dan mungkin
hanya digunakan dalam fungsi. Deklarasi semacam itu juga berfungsi sebagai definisi dan menyebabkan penyimpanan
dipesan. Sebuah daftar deklarasi setara dengan auto deklarasi, tetapi petunjuk bahwa
objek yang dideklarasikan akan sering diakses. Hanya beberapa objek yang ditempatkan
register, dan hanya tipe tertentu yang memenuhi syarat; pembatasan tergantung pada implementasi.
Namun, jika suatu objek dinyatakan register , unary & operator mungkin tidak berlaku untuk itu,
secara eksplisit atau implisit.
Aturan bahwa adalah ilegal untuk menghitung alamat objek yang didaftarkan , tetapi sebenarnya diambil
menjadi otomatis , baru.

The statis specifier memberikan benda dinyatakan statis kelas penyimpanan, dan dapat digunakan baik
fungsi di dalam atau di luar. Di dalam suatu fungsi, specifier ini menyebabkan penyimpanan dialokasikan, dan
berfungsi sebagai definisi; untuk efeknya di luar fungsi, lihat Par.A.11.2 .

Deklarasi dengan extern , digunakan di dalam fungsi, menentukan penyimpanan untuk yang dinyatakan
objek didefinisikan di tempat lain; untuk efeknya di luar fungsi, lihat Par.A.11.2 .

The typedef specifier tidak penyimpanan cadangan dan disebut kelas penyimpanan specifier hanya untuk
kenyamanan sintaksis; itu dibahas dalam Par.A.8.9 .

Paling banyak satu specifier kelas penyimpanan dapat diberikan dalam deklarasi. Jika tidak ada yang diberikan, aturan ini
digunakan: objek yang dideklarasikan di dalam suatu fungsi dianggap otomatis ; fungsi yang dideklarasikan dalam a
fungsi dianggap eksternal ; objek dan fungsi yang dideklarasikan di luar fungsi dibawa ke
menjadi statis , dengan tautan eksternal. Lihat Pars. A.10 - A.11 .

A.8.2 Jenis Penentu


Jenis-specifier adalah
jenis specifier :
kosong
arang
pendek
int
panjang
mengapung
dua kali lipat
tertanda
tidak ditandatangani
struct-or-union-specifier
enum-specifier
ketik nama

Paling banyak satu kata panjang atau pendek dapat ditentukan bersama dengan int ; artinya adalah
sama jika int tidak disebutkan. Kata panjang dapat ditentukan bersama dengan ganda . Di
sebagian besar yang ditandatangani atau tidak ditandatangani dapat ditentukan bersama dengan int atau salah satu dari yang pendek atau
varietas panjang , atau dengan arang . Entah dapat muncul sendiri dalam hal int dipahami. Itu
specifier yang ditandatangani berguna untuk memaksa objek char membawa tanda; itu diizinkan tetapi
berlebihan dengan tipe integral lainnya.

Halaman 171

172
Kalau tidak, paling banyak satu tipe-specifier dapat diberikan dalam deklarasi. Jika tipe-specifier adalah
hilang dari deklarasi, itu diambil untuk menjadi int .

Tipe mungkin juga memenuhi syarat, untuk menunjukkan properti khusus dari objek yang dideklarasikan.

jenis-kualifikasi :
const
lincah
Kualifikasi
setelah tipe dapat muncul
itu ditugaskan dengan
untuk. Tidak adaspecifier
semantik tipe apabergantung
yang pun. Sebuah const
pada objek dapatuntuk
implementasi diinisialisasi,
volatiletetapi tidak
benda.
Properti const dan volatile baru dengan standar ANSI. Tujuan dari const adalah untuk
mengumumkan objek yang dapat ditempatkan dalam memori hanya baca, dan mungkin untuk meningkatkan peluang
optimasi. Tujuan volatile adalah untuk memaksa implementasi untuk menekan optimasi itu
kalau tidak bisa terjadi. Misalnya, untuk mesin dengan input / output yang dipetakan memori, pointer ke a
register perangkat mungkin dinyatakan sebagai pointer ke volatile , untuk mencegah compiler
menghapus referensi yang tampaknya berlebihan melalui pointer. Kecuali bahwa itu harus didiagnosis secara eksplisit
mencoba untuk mengubah objek const , kompilator dapat mengabaikan kualifikasi ini.

A.8.3 Struktur dan Deklarasi Serikat Pekerja


Struktur adalah objek yang terdiri dari urutan anggota bernama dari berbagai jenis. Serikat pekerja
adalah objek yang berisi, pada waktu yang berbeda, salah satu dari beberapa anggota dari berbagai jenis. Struktur
dan penentu serikat memiliki bentuk yang sama.
struct-or-union-specifier :
opt -atau-union identifier opt { struct-declaration-list }

pengidentifikasi struct-or-union

struct-or-union :
struct
Persatuan

Struct-declaration-list adalah urutan deklarasi untuk anggota struktur atau


Persatuan:

struct-declaration-list :
deklarasi struct
struct-declaration-list deklarasi struct

struct-declaration : specifier-qualifier-list struct-declarator-list ;

specifier-kualifikasi-daftar :
type-specifier specifier-qualifier-list opt
jenis-kualifikasi-daftar-kualifikasi-daftar opt

struct-declarator-list :
struct-declarator
struct-declarator-list , struct-declarator

Biasanya, struct-declarator hanyalah deklarator untuk anggota struktur atau gabungan. SEBUAH
anggota struktur juga dapat terdiri dari jumlah bit yang ditentukan. Anggota seperti itu juga disebut
a bit-bidang ; panjangnya berangkat dari deklarator untuk nama bidang dengan titik dua.

struct-declarator :
deklarator declarator opt : ekspresi konstan

Halaman 172

173
Jenis specifier formulir

pengidentifikasi struct-or-union { struct-declaration-list }

mendeklarasikan pengidentifikasi sebagai tag struktur atau gabungan yang ditentukan oleh daftar. Selanjutnya
deklarasi dalam lingkup yang sama atau dalam dapat merujuk pada jenis yang sama dengan menggunakan tag di a
specifier tanpa daftar:

pengidentifikasi struct-or-union

Jika specifier dengan tag tetapi tanpa daftar muncul ketika tag tidak dinyatakan, tidak lengkap
jenis ditentukan. Objek dengan struktur atau tipe gabungan yang tidak lengkap dapat disebutkan dalam
konteks di mana ukurannya tidak diperlukan, misalnya dalam deklarasi (bukan definisi), untuk
menentukan pointer, atau untuk membuat typedef , tetapi tidak sebaliknya. Jenisnya menjadi lengkap
pada terjadinya specifier berikutnya dengan tag itu, dan berisi daftar deklarasi. Bahkan di
penentu dengan daftar, struktur atau jenis serikat yang dideklarasikan tidak lengkap dalam daftar,
dan menjadi lengkap hanya pada } mengakhiri specifier.

Struktur mungkin tidak mengandung anggota dengan tipe tidak lengkap. Karena itu, tidak mungkin
mendeklarasikan struktur atau kesatuan yang mengandung instance dari dirinya sendiri. Namun, selain memberi nama kepada
struktur atau jenis gabungan, tanda memungkinkan definisi struktur referensi-diri; suatu struktur atau
union dapat berisi pointer ke instance itu sendiri, karena pointer ke tipe yang tidak lengkap mungkin
diumumkan.

Aturan yang sangat khusus berlaku untuk deklarasi formulir


pengidentifikasi struct-or-union ;

yang menyatakan struktur atau gabungan, tetapi tidak memiliki daftar deklarasi dan tidak memiliki deklarator. Bahkan jika
pengidentifikasi adalah struktur atau tag gabungan yang sudah dinyatakan dalam lingkup luar ( Par.A.11.1 ), ini
deklarasi membuat pengidentifikasi tag dari struktur atau serikat baru, tidak lengkap mengetik di
lingkup saat ini.
Rekondisi ini baru dengan ANSI. Hal ini dimaksudkan untuk menangani struktur saling rekursif yang dinyatakan dalam
ruang lingkup dalam, tetapi yang tagnya mungkin sudah dinyatakan dalam ruang lingkup luar.

Penentu struktur atau gabungan dengan daftar tetapi tidak ada tag yang membuat jenis unik; itu bisa disebut
langsung hanya dalam deklarasi yang merupakan bagiannya.

Nama-nama anggota dan tag tidak saling bertentangan atau dengan variabel biasa. SEBUAH
nama anggota mungkin tidak muncul dua kali dalam struktur atau kesatuan yang sama, tetapi nama anggota yang sama
dapat digunakan dalam struktur atau serikat yang berbeda.
Dalam edisi pertama buku ini, nama-nama struktur dan anggota serikat tidak terkait dengan
orang tua mereka. Namun, asosiasi ini menjadi umum di kompiler jauh sebelum standar ANSI.

Anggota non-bidang dari suatu struktur atau gabungan dapat memiliki jenis objek apa pun. Seorang anggota lapangan (yang
tidak perlu memiliki deklarator dan karenanya mungkin tidak disebutkan namanya) memiliki tipe int , unsigned int , atau ditandatangani
int , dan ditafsirkan sebagai objek tipe integral dari panjang yang ditentukan dalam bit; Apakah An
bidang int diperlakukan sebagai ditandatangani tergantung pada implementasi. Anggota bidang yang berdekatan dari
struktur dikemas ke dalam unit penyimpanan yang tergantung pada implementasi dalam implementasi-
arah tergantung. Ketika bidang yang mengikuti bidang lain tidak akan masuk ke sebagian terisi
unit penyimpanan, dapat dibagi antara unit, atau unit dapat diisi. Bidang tanpa nama dengan
lebar 0 memaksa lapisan ini, sehingga bidang berikutnya akan dimulai di tepi alokasi berikutnya
satuan.
Standar ANSI membuat bidang lebih tergantung pada implementasi daripada edisi pertama. ini
disarankan untuk membaca aturan bahasa untuk menyimpan bit-field sebagai `` tergantung pada implementasi '' tanpa
kualifikasi. Struktur dengan bit-bidang dapat digunakan sebagai cara portabel untuk mengurangi
penyimpanan diperlukan untuk struktur (dengan kemungkinan biaya meningkatkan ruang instruksi, dan waktu,

Halaman 173

174
diperlukan untuk mengakses bidang), atau sebagai cara non-portabel untuk menggambarkan tata letak penyimpanan yang dikenal di bit-
tingkat. Dalam kasus kedua, perlu untuk memahami aturan pelaksanaan lokal.

Anggota struktur memiliki alamat yang meningkat sesuai urutan deklarasi mereka. Sebuah non-
anggota bidang struktur disejajarkan pada batas pengalamatan tergantung pada jenisnya;
oleh karena itu, mungkin ada lubang yang tidak disebutkan namanya dalam suatu struktur. Jika pointer ke struktur dilemparkan ke
jenis pointer ke anggota pertamanya, hasilnya merujuk ke anggota pertama.

Serikat pekerja dapat dianggap sebagai struktur yang semua anggotanya mulai dengan offset 0 dan yang
ukurannya cukup untuk memuat salah satu anggotanya. Paling banyak salah satu anggota dapat disimpan di a
serikat kapan saja. Jika sebuah pointr ke sebuah union dilemparkan ke jenis pointer ke anggota, hasilnya
merujuk pada anggota itu.

Contoh sederhana deklarasi struktur adalah

struct tnode {
char tword [20];
int count;
struct tnode * kiri;
struct tnode * right;
}
yang berisi array 20 karakter, integer, dan dua pointer ke struktur yang sama.
Setelah deklarasi ini mendapat manfaat, deklarasi
struct tnode s, * sp;
menyatakan s sebagai struktur dari jenis yang diberikan, dan sp untuk menjadi pointer ke struktur yang diberikan
menyortir. Dengan deklarasi ini, ekspresi
sp-> hitung
mengacu pada bidang hitung struktur ke mana titik sp ;

s. kiri
merujuk ke pointer subtree kiri struktur s , dan

s.right-> tword [0]


mengacu pada karakter pertama anggota tword dari subtree kanan s .
Secara umum, seorang anggota serikat tidak boleh diperiksa kecuali nilai serikat telah
ditugaskan menggunakan anggota yang sama. Namun, satu jaminan khusus menyederhanakan penggunaan serikat:
jika sebuah serikat berisi beberapa struktur yang memiliki urutan awal yang sama, dan serikat tersebut
saat ini mengandung salah satu dari struktur ini, diizinkan untuk merujuk ke bagian awal umum dari
salah satu struktur yang terkandung. Misalnya, berikut ini adalah fragmen hukum:
Persatuan
struct{ {
tipe int;
} n;
struct {
tipe int;
int intode;
} ni;
struct {
tipe int;
float floatnode;
} nf;
} u;
...
u.nf.type = FLOAT;
u.nf.floatnode = 3.14;
...
if (untype == FLOAT)

Halaman 174

175
... sin (u.nf.floatnode) ...

A.8.4 Pencacahan
Enumerasi adalah tipe unik dengan nilai berkisar dari sekumpulan konstanta bernama bernama
enumerator. Bentuk specifier enumerasi meminjam dari struktur dan serikat pekerja.
enum-specifier :
enum identifier opt { enumerator-list }
pengidentifikasi enum
daftar enumerator :
pencacah
daftar enumerator , enumerator

pencacah :
pengidentifikasi
identifier = ekspresi konstan

Pengidentifikasi dalam daftar enumerator dinyatakan sebagai konstanta bertipe int , dan dapat muncul
dimanapun konstanta dibutuhkan. Jika tidak ada enumerasi dengan = muncul, maka nilai dari
konstanta yang sesuai mulai dari 0 dan meningkat sebesar 1 saat deklarasi dibaca dari kiri ke
Baik. Pencacah dengan = memberikan pengidentifikasi terkait nilai yang ditentukan; selanjutnya
pengidentifikasi melanjutkan perkembangan dari nilai yang diberikan.

Nama enumerator dalam cakupan yang sama harus berbeda satu sama lain dan dari yang biasa
nama variabel, tetapi nilainya tidak harus berbeda.

Peran pengidentifikasi dalam enum-specifier analog dengan struktur tag di a


struct-specifier; itu menyebutkan enumerasi tertentu. Aturan untuk enum-specifier dengan dan
tanpa tag dan daftar adalah sama dengan yang untuk penentu struktur atau gabungan, kecuali itu
jenis pencacahan tidak lengkap tidak ada; tag enum-specifier tanpa enumerator
daftar harus merujuk ke specifier dalam lingkup dengan daftar.
Enumerasi adalah hal baru sejak edisi pertama buku ini, tetapi telah menjadi bagian dari bahasa untuk beberapa orang
tahun.

A.8.5 Deklarator
Deklarator memiliki sintaks:
deklarator :
pointer memilih deklarator langsung

deklarator langsung :
pengidentifikasi
( deklarator )
direct-declarator [ opt -ekspresi konstan ]
direct-declarator ( parameter-type-list )
direct-declarator ( opt -list identifier )

penunjuk :
* jenis-kualifikasi-daftar memilih
* penunjuk optis jenis-kualifikasi-daftar

jenis-kualifikasi-daftar :
jenis-kualifikasi
tipe-kualifikasi-daftar tipe-kualifikasi
Halaman 175

176
Struktur deklarator mirip dengan tipuan, fungsi, dan ekspresi array; itu
pengelompokan adalah sama.

A.8.6 Arti Deklarator


Daftar deklarator muncul setelah urutan jenis dan kelas penyimpanan penentu. Setiap
deklarator menyatakan pengidentifikasi utama yang unik, yang muncul sebagai alternatif pertama dari
produksi untuk deklarator langsung . Penentu kelas penyimpanan berlaku langsung ke pengidentifikasi ini,
tetapi tipenya tergantung pada bentuk deklaratornya. Deklarator dibaca sebagai pernyataan bahwa
ketika pengenalnya muncul dalam ekspresi dengan bentuk yang sama dengan deklarator, itu menghasilkan
objek dari tipe yang ditentukan.
Mempertimbangkan hanya tipe bagian dari penentu deklarasi ( Par. A.8.2 ) dan khusus
deklarator, deklarasi memiliki bentuk `` TD , '' di mana T adalah tipe dan D adalah deklarator. Tipe
dikaitkan dengan pengidentifikasi dalam berbagai bentuk deklarator dijelaskan secara induktif menggunakan ini
notasi.

Dalam deklarasi TD di mana D adalah identifier unadored, jenis identifier adalah T .

Dalam deklarasi TD di mana D memiliki formulir

(D1)
maka jenis identifier di D1 adalah sama dengan D . Tanda kurung tidak mengubah
ketik, tetapi dapat mengubah pengikatan deklarator kompleks.
A.8.6.1 Deklarator Pointer
Dalam deklarasi TD di mana D memiliki formulir
* ketik-kualifikasi-daftar memilih D1

dan tipe pengidentifikasi dalam deklarasi T D1 adalah `` type-modifier T , '' tipe dari
pengidentifikasi D adalah `` type-modifier type-qualifier-list pointer ke T. '' Kualifikasi berikut * berlaku untuk
pointer itu sendiri, bukan ke objek yang menunjuk pointer.

Misalnya, pertimbangkan deklarasi

int * ap [];
Di sini, ap [] memainkan peran D1 ; deklarasi ` int ap [] '' (di bawah) akan memberikan ap tipe
`` array int, '' daftar tipe-kualifikasi kosong, dan tipe-modifier adalah `` array of. '' Oleh karena itu
deklarasi aktual memberikan ap tipe `` array to pointer ke int . ''
Sebagai contoh lain, deklarasi

int i, * pi, * const cpi = & i;


const int ci = 3, * pci;
mendeklarasikan integer i dan pointer ke pi integer . Nilai dari pointer konstan cpi mungkin
tidak diubah; itu akan selalu menunjuk ke lokasi yang sama, meskipun nilai yang dirujuk
dapat diubah. Integer ci adalah konstan, dan mungkin tidak dapat diubah (meskipun mungkin demikian
diinisialisasi, seperti di sini.) Jenis pci adalah `` pointer ke const int , '' dan pci itu sendiri dapat diubah
untuk menunjuk ke tempat lain, tetapi nilai yang ditunjuknya tidak dapat diubah dengan menetapkan
melalui pci .
A.8.6.2 Deklarator Array
Dalam deklarasi TD di mana D memiliki formulir
D1 [ opt -ekspresi konstan ]

Halaman 176

177
dan tipe pengidentifikasi dalam deklarasi T D1 adalah `` type-modifier T , '' tipe dari
pengidentifikasi D adalah `` type-modifier array of T. '' Jika ekspresi konstan ada, ia harus memiliki
tipe integral, dan nilainya lebih besar dari 0. Jika ekspresi konstan menentukan batasnya
hilang, array memiliki tipe yang tidak lengkap.

Array dapat dibangun dari tipe aritmatika, dari pointer, dari struktur atau
union, atau dari array lain (untuk menghasilkan array multi-dimensi). Jenis apa pun dari mana
array yang dibangun harus lengkap; tidak boleh berupa susunan struktur tipe tidak lengkap.
Ini menyiratkan bahwa untuk array multi-dimensi, hanya dimensi pertama yang mungkin hilang. Itu
tipe objek tipe aray tidak lengkap diselesaikan oleh deklarasi untuk, lengkap, lainnya
objek ( Par.A.10.2 ), atau dengan menginisialisasi ( Par.A.8.7 ). Sebagai contoh,

float fa [17], * afp [17];


mendeklarasikan array angka float dan array pointer ke angka float . Juga,

static int x3d [3] [5] [7];


mendeklarasikan array tiga dimensi statis bilangan bulat, dengan pangkat 3 X 5 X 7. Rincian lengkap,
x3d adalah array dari tiga item: setiap item adalah array dari lima array; setiap array yang terakhir adalah
array tujuh bilangan bulat. Ekspresi x3d , x3d [i] , x3d [i] [j] , x3d [i] [j] [k] dapat
cukup muncul dalam ekspresi. Tiga yang pertama bertipe `` array, '', yang terakhir bertipe int .
Lebih khusus, x3d [i] [j] adalah array dari 7 integer, dan x3d [i] adalah array dari 5 array dengan 7
bilangan bulat.
Operasi langganan array didefinisikan sehingga E1 [E2] identik dengan * (E1 + E2) . Karena itu,
meskipun tampilannya asimetris, subskrip adalah operasi komutatif. Karena
aturan konversi yang berlaku untuk + dan untuk array (Pars. A6.6 , A.7.1 , A.7.7 ), jika E1 adalah array dan
E2 bilangan bulat, maka E1 [E2] mengacu pada anggota E2 - E1 .

Dalam contoh, x3d [i] [j] [k] setara dengan * (x3d [i] [j] + k) . Subekspresi pertama
x3d [i] [j] dikonversi oleh Par.A.7.1 untuk mengetikkan `` pointer ke array of integer, '' oleh Par.A.7.7 , the
Selain itu melibatkan multiplikasi dengan ukuran bilangan bulat. Ini mengikuti dari aturan yang mengatur
disimpan oleh baris (subskrip terakhir bervariasi tercepat) dan subskrip pertama dalam deklarasi
membantu menentukan jumlah penyimpanan yang dikonsumsi oleh array, tetapi tidak memainkan bagian lain di dalamnya
perhitungan subskrip.

A.8.6.3 Deklarator Fungsi


Dalam TD deklarasi fungsi gaya baru di mana D memiliki formulir
D1 ( daftar tipe-parameter )

dan tipe pengidentifikasi dalam deklarasi T D1 adalah `` type-modifier T , '' tipe dari
identifier D adalah `` type-modifier function dengan argumen parameter-type-list mengembalikan T. ''

Sintaks dari parameter adalah

parameter-type-list :
daftar parameter
daftar parameter , ...

daftar parameter :
deklarasi parameter
daftar parameter , deklarasi parameter

deklarasi parameter :
declarator-specifier deklarator
declaration-specifier abstrak-declarator opt

Halaman 177

178
Dalam deklarasi gaya baru, daftar parameter menentukan jenis parameter. Sebagai
kasus khusus, deklarator untuk fungsi gaya baru tanpa parameter memiliki daftar parameter
terdiri dari kekosongan kata kunci . Jika daftar parameter berakhir dengan elipsis `` , ... '', maka
fungsi tersebut dapat menerima lebih banyak argumen daripada jumlah parameter yang dijelaskan secara eksplisit,
lihat Par.A.7.3.2 .

Jenis-jenis parameter yang merupakan array atau fungsi diubah ke pointer, sesuai dengan
aturan untuk konversi parameter; lihat Par.A.10.1 . Satu-satunya specifier kelas penyimpanan diizinkan
dalam deklarasi parameter adalah register , dan specifier ini diabaikan kecuali fungsinya
deklarator mengepalai definisi fungsi. Demikian pula jika deklarator dalam parameter deklarasi
mengandung pengidentifikasi dan deklarator fungsi tidak memiliki definisi fungsi, the
pengidentifikasi segera keluar dari ruang lingkup. Deklarator abstrak, yang tidak menyebutkan
pengidentifikasi, dibahas dalam Par.A.8.8 .

Dalam deklarasi fungsi gaya lama TD di mana D memiliki formulir

D1 ( opt -list identifier )

dan tipe pengidentifikasi dalam deklarasi T D1 adalah `` type-modifier T , '' tipe dari
identifier D adalah `` type-modifier function dari argumen tidak spesifik yang mengembalikan T. '' The
parameter (jika ada) memiliki formulir

daftar pengidentifikasi :
pengidentifikasi
daftar pengidentifikasi , pengidentifikasi

Dalam deklarator gaya lama, daftar pengidentifikasi harus tidak ada kecuali deklarator digunakan dalam
kepala definisi fungsi ( Par.A.10.1 ). Tidak ada informasi tentang jenis parameternya
disediakan oleh deklarasi.

Misalnya, deklarasi

int f (), * fpi (), (* pfi) ();


mendeklarasikan fungsi f mengembalikan integer, fungsi fpi mengembalikan pointer ke integer, dan
sebuah pointer pfi ke fungsi yang mengembalikan integer. Tidak satupun dari ini adalah tipe parameter
ditentukan; mereka gaya lama.
Dalam deklarasi gaya baru

int strcpy (char * dest, const char * source), rand (void);


strcpy adalah fungsi yang mengembalikan int , dengan dua argumen, yang pertama adalah penunjuk karakter, dan
kedua penunjuk ke karakter konstan. Nama parameter adalah komentar yang efektif. Itu
fungsi kedua rand tidak mengambil argumen dan mengembalikan int .
Deklarator fungsi dengan prototipe parameter, sejauh ini, merupakan perubahan bahasa yang paling penting
diperkenalkan oleh standar ANSI. Mereka menawarkan keunggulan dibandingkan deklarator `` gaya lama` yang pertama
edisi dengan menyediakan deteksi kesalahan dan paksaan argumen di seluruh panggilan fungsi, tetapi dengan biaya:
kekacauan dan kebingungan selama pengenalan mereka, dan perlunya mengakomodasi kedua bentuk. Beberapa
keburukan sintaksis diperlukan demi kompatibilitas, yaitu batal sebagai penanda eksplisit
fungsi gaya baru tanpa parameter.
Notasi ellipsis `` , ... '' untuk fungsi variadic juga baru, dan, bersama dengan makro di
header standar <stdarg.h> , memformalkan suatu mekanisme yang secara resmi dilarang tetapi tidak resmi
dimaafkan dalam edisi pertama.

Notasi ini diadaptasi dari bahasa C ++.

A.8.7 Inisialisasi

Halaman 178

179
Ketika suatu objek dideklarasikan, init-declarator-nya dapat menentukan nilai awal untuk pengidentifikasi
dideklarasikan. Inisialisasi diawali dengan = , dan merupakan ekspresi, atau daftar
initializers bersarang di kawat gigi. Daftar dapat diakhiri dengan koma, kerapian untuk pemformatan yang rapi.
penginisialisasi :
penugasan-ekspresi
{ daftar penginisialisasi }
{ daftar penginisialisasi ,}

daftar penginisialisasi :
penginisialisasi
daftar initializer , initializer

Semua ekspresi dalam initializer untuk objek atau array statis harus berupa ekspresi konstan
dijelaskan dalam Par.A.7.19 . Ekspresi di penginisialisasi untuk objek otomatis atau mendaftar atau
array juga harus berupa ekspresi konstan jika inisialisasi adalah daftar kurung kurawal. Namun,
jika initializer untuk objek otomatis adalah ekspresi tunggal, itu tidak harus berupa konstanta
ekspresi, tetapi hanya harus memiliki jenis yang sesuai untuk penugasan ke objek.
Edisi pertama tidak menyetujui inisialisasi struktur otomatis, serikat pekerja, atau array. Itu
Standar ANSI memungkinkannya, tetapi hanya dengan konstruksi konstan kecuali initializer dapat dinyatakan oleh a
ekspresi sederhana.

Objek statis tidak secara eksplisit diinisialisasi diinisialisasi seolah-olah (atau anggotanya) ditugaskan
constant 0. Nilai awal dari objek otomatis yang tidak secara eksplisit diinternisasi tidak terdefinisi.

Inisialisasi untuk pointer atau objek tipe aritmatika adalah ekspresi tunggal, mungkin dalam
kawat gigi. Ekspresi ditugaskan ke objek.

Penginisialisasi untuk suatu struktur adalah ekspresi dari tipe yang sama, atau daftar kurung kurawal
inisialisasi untuk anggotanya secara berurutan. Anggota bit-field yang tidak disebutkan namanya diabaikan, dan tidak
diinisialisasi. Jika ada lebih sedikit inisialisasi dalam daftar daripada anggota struktur, yang tertinggal
anggota diinisialisasi dengan 0. Mungkin tidak ada lebih banyak inisialisasi daripada anggota. Bit tidak bernama
anggota lapangan diabaikan, dan tidak diinisialisasi.

Penginisialisasi untuk array adalah daftar inisialisasi brace-tertutup untuk anggotanya. Jika array memiliki
ukuran tidak diketahui, jumlah inisialisasi menentukan ukuran array, dan jenisnya menjadi
lengkap. Jika array memiliki ukuran tetap, jumlah inisialisasi mungkin tidak melebihi jumlah
anggota array; jika jumlahnya lebih sedikit, anggota tambahan akan diinisialisasi dengan 0.

Sebagai kasus khusus, array karakter dapat diinisialisasi dengan string literal; karakter berturut-turut
dari string menginisialisasi anggota berturut-turut dari array. Begitu pula dengan karakter hurufnya yang luas
( Par.A.2.6 ) dapat menginisialisasi array bertipe wchar_t . Jika array memiliki ukuran yang tidak diketahui, angkanya
karakter dalam string, termasuk karakter null terminating, menentukan ukurannya; jika itu
ukuran tetap, jumlah karakter dalam string, tidak termasuk penghentian karakter nol,
tidak boleh melebihi ukuran array.

Penginisialisasi untuk gabungan adalah ekspresi tunggal dari jenis yang sama, atau kurung kurawal
inisialisasi untuk anggota pertama serikat.
Edisi pertama tidak memungkinkan inisialisasi serikat pekerja. Aturan `` anggota-pertama '' canggung, tetapi sulit
untuk menggeneralisasi tanpa sintaks baru. Selain memungkinkan serikat pekerja untuk secara eksplisit diinisialisasi dalam setidaknya a
cara primitif, aturan ANSI ini membuat semantik serikat statis tidak diinisialisasi secara eksplisit.

Sebuah agregat adalah struktur atau array. Jika suatu agregat mengandung anggota tipe agregat, the
aturan inisialisasi berlaku secara rekursif. Kawat gigi dapat dicabut dalam inisialisasi sebagai berikut: jika
initializer untuk anggota agregat yang itu sendiri adalah agregat dimulai dengan kurung kurawal, kemudian
daftar inisialisasi yang dipisahkan koma memisahkan para anggota subagregat; ini
keliru karena ada lebih banyak inisialisasi dari anggota. Namun, jika initializer untuk a

Halaman 179

180
subagregat tidak dimulai dengan penjepit kiri, maka hanya cukup elemen dari daftar
diperhitungkan untuk anggota subagregat; anggota yang tersisa dibiarkan
inisialisasi anggota agregat berikutnya dimana subagregat menjadi bagiannya.

Sebagai contoh,

int x [] = {1, 3, 5};


menyatakan dan menginisialisasi x sebagai array 1 dimensi dengan tiga anggota, karena tidak ada ukuran
ditentukan dan ada tiga inisialisasi.

float y [4] [3] = {


{1, 3, 5},
{2, 4, 6},
{3, 5, 7},
};
adalah inisialisasi yang dikurung sepenuhnya: 1, 3 dan 5 menginisialisasi baris pertama array y [0] ,
yaitu y [0] [0] , y [0] [1] , dan y [0] [2] . Demikian juga dua baris berikutnya menginisialisasi y [1] dan y [2] .
Penginisialisasi berakhir lebih awal, dan karena itu elemen-elemen y [3] diinisialisasi dengan 0. Tepatnya
efek yang sama bisa dicapai oleh
float y [4] [3] = {
1, 3, 5, 2, 4, 6, 3, 5, 7
};
Inisialisasi untuk y dimulai dengan penjepit kiri, tetapi untuk y [0] tidak; oleh karena itu tiga
elemen dari daftar digunakan. Demikian juga tiga berikutnya diambil berturut-turut untuk y [1] dan untuk
y [2] . Juga,
float y [4] [3] = {
{1}, {2}, {3}, {4}
};
menginisialisasi kolom pertama y (dianggap sebagai array dua dimensi) dan meninggalkan sisanya 0.
Akhirnya,

char msg [] = "Kesalahan sintaksis pada baris% s \ n";


memperlihatkan susunan karakter yang anggotanya diinisialisasi dengan string; ukurannya termasuk
mengakhiri karakter nol.
A.8.8 Ketikkan nama
Dalam beberapa konteks (untuk menentukan jenis konversi secara eksplisit dengan pemain, untuk mendeklarasikan parameter
ketik deklarator fungsi, dan sebagai argumen sizeof ) perlu untuk memberikan nama
tipe data. Ini dilakukan dengan menggunakan nama tipe , yang secara sintaksis merupakan deklarasi untuk
objek jenis itu menghilangkan nama objek.
jenis-nama :
opt -declarator opt -qualifier-list abstrak

abstrak-deklarator :
penunjuk
pointer memilih direct-abstract-declarator

direct-abstract-declarator :
( abstrak-deklarator )
langsung abstrak-declarator opt [ konstan ekspresi opt ]
opt direct-abstract-declarator ( opt -type-list opt )
Halaman 180

181
Dimungkinkan untuk mengidentifikasi secara unik lokasi di abstrak-deklarator tempat pengidentifikasi
akan muncul jika konstruksi adalah deklarator dalam suatu deklarasi. Jenis bernama ini kemudian
sama dengan jenis pengidentifikasi hipotetis. Sebagai contoh,

int
int *
int * [3]
int (*) []
int * ()
int (* []) (batal)
nama masing-masing tipe `` integer, '' `` pointer to integer, '' `` array 3 pointer ke integer, ''
`` penunjuk ke jumlah bilangan bulat yang tidak ditentukan, '' 'fungsi dari parameter yang tidak ditentukan kembali
pointer ke integer, '' dan `` array, dengan ukuran yang tidak ditentukan, dari pointer ke fungsi tanpa parameter
masing-masing mengembalikan bilangan bulat. ''
A.8.9 Typedef
Deklarasi yang specifier kelas penyimpanannya diketik tidak mendeklarasikan objek; sebaliknya mereka
mendefinisikan pengidentifikasi yang mengetikkan nama. Pengidentifikasi ini disebut nama typedef.
ketik nama :
pengidentifikasi

Sebuah typedef deklarasi atribut jenis untuk setiap nama di antara declarators dalam cara yang biasa
(lihat Par.A.8.6 ). Setelah itu, masing-masing nama typedef tersebut secara sintaksis setara dengan suatu tipe
kata kunci penentu untuk tipe terkait.

Misalnya setelah

ketikkan Blockno panjang, * Blockptr;


typedef struct {double r, theta; } Kompleks;
konstruksi

Blockno b;
extern Blockptr bp;
Kompleks z, * zp;
adalah deklarasi hukum. Jenis b adalah panjang , bahwa bp adalah `` pointer to long , '' dan z adalah
struktur yang ditentukan; zp adalah pointer ke struktur seperti itu.
typedef tidak memperkenalkan tipe baru, hanya sinonim untuk tipe yang dapat ditentukan dalam
cara lain. Dalam contoh, b memiliki tipe yang sama dengan objek panjang .

Nama typedef dapat dideklarasikan ulang dalam lingkup bagian dalam, tetapi kumpulan penentu tipe yang tidak kosong
harus diberikan. Sebagai contoh,

extern Blockno;
tidak mendeklarasikan ulang Blockno , tetapi
extern int Blockno;
tidak.
A.8.10 Jenis Kesetaraan
Dua daftar specifier tipe sama jika mengandung set specifier yang sama, pengambilan
mempertimbangkan bahwa beberapa specifier dapat tersirat oleh yang lain (misalnya, sudah lama menyiratkan
panjang int ). Struktur, serikat, dan enumerasi dengan tag berbeda berbeda, dan tanpa tag
gabungan, struktur, atau enumerasi menentukan tipe unik.

Halaman 181

182
Dua jenis adalah sama jika deklarator abstraknya ( Par.A.8.8 ), setelah memperluas semua typedef
jenis, dan menghapus setiap penentu parameter fungsi, adalah sama hingga kesetaraan
ketik daftar specifier. Ukuran array dan tipe parameter fungsi sangat signifikan.

A.9 Pernyataan
Kecuali seperti yang dijelaskan, pernyataan dieksekusi secara berurutan. Pernyataan dieksekusi untuk mereka
efek, dan tidak memiliki nilai. Mereka terbagi dalam beberapa kelompok.
pernyataan :
pernyataan-berlabel
ekspresi-pernyataan
pernyataan majemuk
seleksi-pernyataan
iterasi-pernyataan
pernyataan singkat

A.9.1 Pernyataan Berlabel


Pernyataan dapat membawa awalan label.
pernyataan berlabel :
pengidentifikasi : pernyataan
case constant-expression : statement
default: pernyataan

Label yang terdiri dari pengidentifikasi menyatakan pengidentifikasi. Satu-satunya penggunaan label pengidentifikasi adalah sebagai
target goto . Cakupan pengidentifikasi adalah fungsi saat ini. Karena label punya
ruang nama sendiri, mereka tidak mengganggu pengidentifikasi lain dan tidak dapat dideklarasikan ulang. Lihat
Par.A.11.1 .

Label kasus dan label default digunakan dengan pernyataan sakelar ( Par.A.9.4 ). Konstan
ekspresi case harus memiliki tipe integral.

Label itu sendiri tidak mengubah aliran kontrol.

A.9.2 Pernyataan Ekspresi


Kebanyakan pernyataan adalah pernyataan ekspresi, yang memiliki bentuk
ekspresi-pernyataan :
ekspresi opt ;

Sebagian besar pernyataan ekspresi adalah penugasan atau panggilan fungsi. Semua efek samping dari
ekspresi selesai sebelum pernyataan berikutnya dieksekusi. Jika ekspresinya hilang,
konstruksi disebut pernyataan nol; sering digunakan untuk memasok tubuh kosong ke sebuah
pernyataan iterasi untuk menempatkan label.

A.9.3 Pernyataan Gabungan


Sehingga beberapa pernyataan dapat digunakan di mana seseorang diharapkan, pernyataan majemuk (juga
disebut `` blok '') disediakan. Badan definisi fungsi adalah pernyataan majemuk.
pernyataan majemuk :
{ daftar pernyataan memilih daftar pernyataan memilih }

daftar deklarasi :
pernyataan
daftar pernyataan deklarasi

Halaman 182

183
daftar pernyataan :
pernyataan
pernyataan-pernyataan pernyataan

Jika pengidentifikasi dalam daftar-deklarasi berada dalam lingkup di luar blok, deklarasi luar adalah
ditangguhkan dalam blok (lihat Par.A.11.1 ), setelah itu melanjutkan kekuatannya. Pengidentifikasi bisa
dideklarasikan hanya sekali dalam blok yang sama. Aturan ini berlaku untuk pengidentifikasi dengan nama yang sama
ruang ( Par.A.11 ); pengidentifikasi dalam ruang nama yang berbeda diperlakukan sebagai berbeda.

Inisialisasi objek otomatis dilakukan setiap kali blok dimasukkan di atas, dan
hasil dalam urutan deklarator. Jika lompatan ke blok dijalankan, ini
inisialisasi tidak dilakukan. Inisialisasi objek statis dilakukan hanya sekali,
sebelum program dimulai eksekusi.

A.9.4 Pernyataan Seleksi


Pernyataan pemilihan memilih satu dari beberapa aliran kontrol.
seleksi-pernyataan :
jika ( ekspresi ) pernyataan
jika ( ekspresi ) pernyataan lain pernyataan
beralih ( ekspresi ) pernyataan
Dalam kedua bentuk pernyataan if , ekspresi, yang harus memiliki tipe aritmatika atau pointer,
dievaluasi, termasuk semua efek samping, dan jika membandingkan tidak sama dengan 0, substatement pertama adalah
dieksekusi. Dalam bentuk kedua, substatement kedua dijalankan jika ekspresi adalah 0. The
lain ambiguitas diselesaikan dengan menghubungkan yang lain dengan yang lain ditemui lain- kecuali jika di
tingkat sarang blok yang sama.

The beralih penyebab pernyataan mengontrol akan ditransfer ke salah satu dari beberapa pernyataan tergantung
pada nilai ekspresi, yang harus memiliki tipe integral. Subtitle dikendalikan oleh a
saklar biasanya majemuk. Pernyataan apa pun dalam subtitle dapat dilabeli dengan satu
atau lebih banyak label kasus ( Par.A.9.1 ). Ekspresi mengendalikan mengalami promosi integral
( Par.A.6.1 ), dan konstanta kasing dikonversi ke tipe yang dipromosikan. Tidak ada dua kasus ini
konstanta yang terkait dengan sakelar yang sama mungkin memiliki nilai yang sama setelah konversi. Sana
mungkin juga paling banyak satu label default yang terkait dengan sakelar. Switch mungkin bersarang; Sebuah
case atau label default dikaitkan dengan sakelar terkecil yang mengandungnya.

Ketika pernyataan switch dieksekusi, ekspresinya dievaluasi, termasuk semua efek samping,
dan dibandingkan dengan setiap kasus konstan. Jika salah satu dari konstanta kasus sama dengan nilai
ekspresi, kontrol lolos ke pernyataan label kasus yang cocok . Jika tidak ada kasus yang konstan
cocok dengan ekspresi, dan jika ada label default , kontrol beralih ke pernyataan berlabel.
Jika tidak ada case yang cocok, dan jika tidak ada default , maka tidak ada substatemen dari swtich
dieksekusi.
Dalam edisi pertama buku ini, ekspresi pengendali saklar , dan konstanta kasus, adalah
wajib memiliki tipe int .

A.9.5 Pernyataan Iterasi


Pernyataan iterasi menentukan perulangan.
iterasi-pernyataan :
sementara ( ekspresi ) pernyataan
lakukan statement while ( ekspresi ) ;
untuk ( ekspresi opt ; ekspresi opt ; ekspresi opt ) pernyataan

Halaman 183

184
Dalam pernyataan while and do , substatement dieksekusi berulang kali selama nilainya
dari ekspresi tetap tidak sama dengan 0; ekspresi harus memiliki tipe aritmatika atau penunjuk.
Dengan sementara , tes, termasuk semua efek samping dari ekspresi, terjadi sebelum masing-masing
pelaksanaan pernyataan; dengan do , tes mengikuti setiap iterasi.

Dalam pernyataan for , ekspresi pertama dievaluasi sekali, dan dengan demikian menentukan inisialisasi untuk
putaran. Tidak ada batasan pada jenisnya. Ekspresi kedua harus memiliki aritmatika atau
jenis pointer; itu dievaluasi sebelum setiap iterasi, dan jika itu menjadi sama dengan 0, untuk ini
dihentikan. Ekspresi ketiga dievaluasi setelah setiap iterasi, dan dengan demikian menentukan suatu
inisialisasi untuk loop. Tidak ada batasan pada jenisnya. Efek samping dari setiap ekspresi
diselesaikan segera setelah evaluasi. Jika subtitle tidak mengandung melanjutkan ,
sebuah pernyataan

untuk ( expression1 ; expression2 ; expression3 ) pernyataan

setara dengan

ekspresi1 ;
while ( expression2 ) {
pernyataan
ekspresi3 ;
}
Salah satu dari tiga ekspresi ini dapat dihapus. Ekspresi kedua yang hilang membuat tersirat
uji setara dengan menguji elemen yang tidak nol.
A.9.6 Pernyataan lompat
Kontrol transfer pernyataan langsung tanpa syarat.
pernyataan singkat :
pengidentifikasi goto ;
terus;
istirahat;
opt kembali ekspresi ;
Dalam pernyataan goto , pengidentifikasi harus berupa label ( Par.A.9.1 ) yang terletak di fungsi saat ini.
Kontrol transfer ke pernyataan berlabel.

Sebuah terus Pernyataan mungkin muncul hanya dalam sebuah pernyataan iterasi. Itu menyebabkan kontrol lewat
ke bagian loop-kelanjutan dari pernyataan terkecil terlampir tersebut. Lebih tepatnya,
dalam masing-masing pernyataan

while (...) {do { untuk (...) {


... ... ...
terus:; terus:; terus:;
} } while (...); }
sebuah lanjutan yang tidak terkandung dalam pernyataan iterasi yang lebih kecil sama dengan goto contin .
Sebuah istirahat Pernyataan mungkin muncul hanya dalam sebuah pernyataan iterasi atau saklar pernyataan, dan
mengakhiri eksekusi dari pernyataan terlampir yang terkecil; kontrol lolos ke pernyataan
mengikuti pernyataan yang dihentikan.

Suatu fungsi kembali ke pemanggilnya dengan pernyataan kembali . Ketika pengembalian diikuti oleh
ekspresi, nilai dikembalikan ke pemanggil fungsi. Ekspresi dikonversi, sebagai
dengan penugasan, ke jenis yang dikembalikan oleh fungsi yang muncul.

Mengalir dari ujung fungsi setara dengan pengembalian tanpa ekspresi. Dalam kedua kasus tersebut,
nilai yang dikembalikan tidak terdefinisi.

Halaman 184

185

A.10 Deklarasi Eksternal


Unit input yang disediakan untuk kompiler C disebut unit terjemahan; itu terdiri dari a
urutan deklarasi eksternal, yang merupakan deklarasi atau definisi fungsi.
unit terjemahan :
deklarasi eksternal
unit terjemahan-deklarasi eksternal

deklarasi eksternal :
fungsi-definisi
pernyataan

Ruang lingkup deklarasi eksternal tetap ada sampai akhir unit terjemahan di mana mereka berada
dideklarasikan, seperti efek deklarasi di dalam blok yang bertahan hingga akhir blok.
Sintaks deklarasi eksternal sama dengan semua deklarasi, kecuali hanya di
level ini semoga kode untuk fungsi diberikan.

A.10.1 Definisi Fungsi


Definisi fungsi memiliki formulir
fungsi-definisi :
deklarasi-penentu memilih deklarator daftar deklarasi memilih gabungan-pernyataan

Satu-satunya penentu kelas penyimpanan yang diizinkan di antara penentu deklarasi adalah eksternal atau
statis ; lihat Par.A.11.2 untuk perbedaan di antara mereka.

Fungsi dapat mengembalikan tipe aritmatika, struktur, gabungan, penunjuk, atau batal , tetapi bukan a
fungsi atau array. Deklarator dalam deklarasi fungsi harus menentukan secara eksplisit bahwa
pengidentifikasi yang dideklarasikan memiliki tipe fungsi; yaitu, ia harus mengandung salah satu formulir (lihat Par.A.8.6.3 ).

direct-declarator ( parameter-type-list )
direct-declarator ( opt -list identifier )

di mana deklarator langsung adalah pengidentifikasi atau pengenal yang diurung. Khususnya, itu harus
tidak mencapai tipe fungsi dengan menggunakan typedef .

Dalam bentuk pertama, definisi adalah fungsi gaya baru, dan parameternya, bersama dengan mereka
tipe, dideklarasikan dalam daftar tipe parameternya; daftar deklarasi mengikuti fungsi
deklarator harus tidak ada. Kecuali daftar tipe parameter hanya terdiri dari kekosongan , menunjukkan itu
fungsi tidak memerlukan parameter, setiap deklarator dalam daftar tipe parameter harus berisi
pengidentifikasi. Jika daftar jenis parameter berakhir dengan `` , ... '' maka fungsi tersebut dapat dipanggil dengan
lebih banyak argumen daripada parameter; yang va_arg mekanisme makro didefinisikan dalam header standar
<stdarg.h> dan dijelaskan dalam Lampiran B harus digunakan untuk merujuk pada argumen tambahan.
Fungsi variadik harus memiliki setidaknya satu parameter bernama.

Dalam bentuk kedua, definisi adalah gaya lama: daftar pengidentifikasi menamai parameter, sedangkan
daftar deklarasi atribut tipe padanya. Jika tidak ada pernyataan yang diberikan untuk parameter, tipenya adalah
dianggap sebagai int . Daftar deklarasi harus mendeklarasikan hanya parameter yang disebutkan dalam daftar,
inisialisasi tidak diizinkan, dan satu-satunya specifier kelas penyimpanan yang mungkin adalah register .

Dalam kedua gaya definisi fungsi, parameter dipahami untuk dideklarasikan tepat setelah
awal dari pernyataan majemuk yang membentuk tubuh fungsi, dan dengan demikian sama
pengidentifikasi tidak boleh dicek ulang di sana (meskipun mereka, seperti pengidentifikasi lainnya, boleh dicek ulang
di blok dalam). Jika suatu parameter dinyatakan memiliki tipe `` array of type , '' deklarasi tersebut adalah
disesuaikan untuk membaca `` pointer ke mengetik ; '' sama, jika suatu parameter dinyatakan memiliki fungsi `` jenis

Halaman 185

186
tipe pengembalian , '' deklarasi disesuaikan untuk membaca `` pointer ke fungsi tipe pengembalian . '' Selama
panggilan ke suatu fungsi, argumen dikonversi seperlunya dan ditugaskan untuk
parameter; lihat Par.A.7.3.2 .
Definisi fungsi gaya baru adalah baru dengan standar ANSI. Ada juga perubahan kecil di
rincian promosi; edisi pertama menentukan bahwa deklarasi parameter float adalah
disesuaikan untuk membaca ganda . Perbedaannya menjadi terlihat ketika pointer ke parameter
dihasilkan dalam suatu fungsi.

Contoh lengkap dari definisi fungsi gaya baru adalah

int max (int a, int b, int c)


{
int m;

m = (a> b)? a: b;
kembali (m> c)? m: c;
}
Di sini int adalah penentu deklarasi; maks (int a, int b, int c) adalah deklarator fungsi,
dan {...} adalah blok yang memberi kode untuk fungsi. Gaya lama yang sesuai
definisi akan menjadi
int max (a, b, c)
int a, b, c;
{
/ * ... * /
}
di mana sekarang int max (a, b, c) adalah deklarator, dan int a, b, c; adalah daftar deklarasi untuk
parameter.
A.10.2 Deklarasi Eksternal
Deklarasi eksternal menentukan karakteristik objek, fungsi, dan pengidentifikasi lainnya. Itu
istilah `` eksternal '' merujuk ke lokasi mereka di luar fungsi, dan tidak terhubung langsung dengan
kata kunci eksternal ; kelas penyimpanan untuk objek yang dideklarasikan secara eksternal dapat dibiarkan kosong, atau itu
dapat ditentukan sebagai eksternal atau statis .
Beberapa deklarasi eksternal untuk pengidentifikasi yang sama mungkin ada di dalam unit terjemahan yang sama jika
mereka setuju dalam jenis dan tautan, dan jika ada paling banyak satu definisi untuk pengidentifikasi.

Dua deklarasi untuk objek atau fungsi dianggap setuju dalam tipe di bawah aturan
dibahas dalam Par.A.8.10 . Selain itu, jika deklarasi berbeda karena satu jenis tidak lengkap
struktur, gabungan, atau tipe enumerasi ( Par.A.8.3 ) dan yang lainnya sudah selesai
ketik dengan tag yang sama, jenis diambil untuk setuju. Apalagi jika satu jenis tidak lengkap
tipe array ( Par.A.8.6.2 ) dan yang lainnya adalah tipe array yang lengkap, tipe, jika sebaliknya
identik, juga dianggap setuju. Akhirnya, jika satu jenis menentukan fungsi gaya lama, dan
lainnya merupakan fungsi gaya baru yang identik, dengan deklarasi parameter, tipenya
diambil untuk setuju.

Jika deklarator eksternal pertama untuk suatu fungsi atau objek menyertakan penentu statis , the
pengidentifikasi memiliki hubungan internal ; selain itu memiliki hubungan eksternal . Tautan dibahas di
Par.11.2 .

Deklarasi eksternal untuk suatu objek adalah definisi jika memiliki penginisialisasi. Objek eksternal
deklarasi yang tidak memiliki initializer, dan tidak mengandung specifier extern , adalah a
definisi tentatif . Jika definisi untuk suatu objek muncul di unit terjemahan, tentatif apa pun
definisi diperlakukan hanya sebagai deklarasi yang berlebihan. Jika tidak ada definisi untuk objek muncul
di unit terjemahan, semua definisi tentatif menjadi definisi tunggal dengan initializer 0.

Halaman 186

187
Setiap objek harus memiliki tepat satu definisi. Untuk objek dengan tautan internal, aturan ini berlaku
secara terpisah untuk setiap unit terjemahan, karena objek yang terhubung secara internal adalah unik untuk terjemahan
satuan. Untuk objek dengan tautan eksternal, ini berlaku untuk seluruh program.
Meskipun aturandengan
berlaku identik satu-definisi dirumuskan
yang dinyatakan di agak berbeda dalam
sini. Beberapa edisi pertama
implementasi buku ini, peraturan
mengendurkannya denganitumenggeneralisasikan gagasan tersebut
definisi tentatif. Dalam formulasi alternatif, yang biasa dalam sistem UNIX dan dikenal sebagai
ekstensi umum oleh Standar, semua definisi tentatif untuk objek yang terhubung secara eksternal,
di seluruh unit terjemahan program, dianggap bersama, bukan di masing-masing
unit terjemahan secara terpisah. Jika definisi muncul di suatu tempat dalam program, maka tentatif
definisi menjadi sekadar deklarasi, tetapi jika tidak ada definisi yang muncul, maka semua definisi tentatif
menjadi definisi dengan initializer 0.

A.11 Lingkup dan Keterkaitan


Suatu program tidak perlu dikompilasi sekaligus: teks sumber dapat disimpan dalam beberapa file
berisi unit terjemahan, dan rutin yang dikompilasi dapat diambil dari perpustakaan.
Komunikasi antara fungsi-fungsi suatu program dapat dilakukan melalui panggilan dan
melalui manipulasi data eksternal.
Oleh karena itu, ada dua jenis ruang untuk dipertimbangkan: pertama, ruang lingkup leksikal pengidentifikasi
yang merupakan wilayah teks program di mana karakteristik pengidentifikasi berada
dipahami; dan kedua, ruang lingkup yang terkait dengan objek dan fungsi dengan tautan eksternal,
yang menentukan koneksi antara pengidentifikasi dalam unit terjemahan yang dikompilasi secara terpisah.

A.11.1 Lingkup Leksikal


Pengidentifikasi jatuh ke dalam beberapa ruang nama yang tidak saling mengganggu; sama
pengidentifikasi dapat digunakan untuk tujuan yang berbeda, bahkan dalam cakupan yang sama, jika penggunaannya berbeda
ruang nama. Kelas-kelas ini adalah: objek, fungsi, nama typedef, dan konstanta enum ; label;
tag struktur atau serikat pekerja, dan enumerasi; dan anggota dari setiap struktur atau persatuan
secara individual.
Aturan-aturan ini berbeda dalam beberapa hal dari yang dijelaskan dalam edisi pertama manual ini. Label melakukannya
sebelumnya tidak memiliki ruang nama sendiri; tag struktur dan serikat masing-masing memiliki ruang terpisah,
dan dalam beberapa implementasi, tag enumerasi juga melakukannya; menempatkan berbagai jenis tag ke dalam
ruang yang sama adalah batasan baru. Penyimpangan terpenting dari edisi pertama adalah masing-masing
struktur atau gabungan menciptakan ruang nama terpisah untuk anggotanya, sehingga nama yang sama dapat muncul di
beberapa struktur berbeda. Aturan ini sudah menjadi praktik umum selama beberapa tahun.

Lingkup leksikal dari suatu objek atau fungsi pengidentifikasi dalam deklarasi eksternal dimulai di akhir
deklaratornya dan bertahan hingga akhir unit terjemahan di mana ia muncul. Ruang lingkup
parameter definisi fungsi dimulai pada awal blok yang mendefinisikan fungsi, dan
bertahan melalui fungsi; lingkup parameter dalam deklarasi fungsi berakhir di akhir
deklarator. Ruang lingkup pengidentifikasi yang dinyatakan di kepala blok dimulai pada akhir
deklaratornya, dan tetap ada di ujung blok. Lingkup label adalah keseluruhan dari
fungsi yang muncul. Lingkup struktur, gabungan, atau tag enumerasi, atau
konstanta enumerasi, dimulai pada penampilannya dalam specifier tipe, dan berlanjut hingga akhir a
unit terjemahan (untuk deklarasi di tingkat eksternal) atau ke akhir blok (untuk
deklarasi dalam suatu fungsi).

Jika pengidentifikasi secara eksplisit dinyatakan di kepala blok, termasuk blok yang merupakan a
fungsi, setiap pernyataan pengidentifikasi di luar blok ditangguhkan sampai akhir
blok.

A.11.2 Linkage
Dalam unit terjemahan, semua deklarasi objek atau fungsi pengidentifikasi yang sama dengan internal
linkage merujuk pada hal yang sama, dan objek atau fungsinya unik untuk unit terjemahan itu. Semua

Halaman 187

188
deklarasi untuk pengidentifikasi objek atau fungsi yang sama dengan tautan eksternal merujuk ke yang sama
hal, dan objek atau fungsi dibagikan oleh seluruh program.
Seperti dibahas dalam Par.A.10.2 , deklarasi eksternal pertama untuk pengidentifikasi memberikan pengidentifikasi
hubungan internal jika specifier statis digunakan, hubungan eksternal sebaliknya. Jika deklarasi untuk
pengidentifikasi dalam blok tidak menyertakan penentu eksternal , maka pengenal tidak
linkage dan unik untuk fungsinya. Jika itu termasuk eksternal , dan deklarasi eksternal untuk
aktif dalam lingkup di sekitar blok, maka pengidentifikasi memiliki hubungan yang sama dengan
deklarasi eksternal, dan mengacu pada objek atau fungsi yang sama; tetapi jika tidak ada deklarasi eksternal
terlihat, hubungannya adalah eksternal.

A.12 Pemrosesan awal


Seorang preprosesor melakukan substitusi makro, kompilasi bersyarat, dan memasukkan nama
file. Baris yang diawali dengan # , mungkin diawali dengan ruang putih, berkomunikasi dengan ini
preprosesor. Sintaks dari baris-baris ini tidak tergantung pada bagian bahasa lainnya; mereka mungkin
muncul di mana saja dan memiliki efek yang bertahan (tidak tergantung ruang lingkup) hingga akhir
unit terjemahan. Batas garis sangat penting; setiap baris dianalisis secara individual (lihat bus
Par.A.12.2 untuk cara menyatukan garis). Untuk preprosesor, token adalah token bahasa apa pun, atau a
urutan karakter yang memberikan nama file seperti dalam #include directive ( Par.A.12.4 ); tambahan,
setiap karakter yang tidak didefinisikan didefinisikan sebagai token. Namun, efek spasi putih
selain ruang dan tab horizontal tidak ditentukan dalam garis preprosesor.
Preprocessing sendiri berlangsung dalam beberapa fase logis yang mungkin, khususnya
implementasi, dikondensasikan.

1. Pertama, urutan trigraph seperti yang dijelaskan dalam Par.A.12.1 diganti dengan padanannya.
Jika lingkungan sistem operasi memerlukannya, karakter baris baru akan diperkenalkan
di antara baris file sumber.

2. Setiap kemunculan karakter backslash \ diikuti oleh baris baru dihapus, ini
garis penyambungan ( Par.A.12.2 ).

3. Program ini dibagi menjadi token yang dipisahkan oleh karakter spasi-putih; komentar adalah
digantikan oleh satu ruang. Kemudian arahan preprocessing dipatuhi, dan makro
(Pars. A.12.3 - A.12.10 ) diperluas.

4. Urutan melarikan diri dalam konstanta karakter dan string literal (Pars. A.2.5.2 , A.2.6 ) adalah
diganti dengan padanannya; kemudian string literal yang berdekatan digabungkan.

5. Hasilnya diterjemahkan, kemudian dihubungkan bersama dengan program dan perpustakaan lain, oleh
mengumpulkan program dan data yang diperlukan, dan menghubungkan fungsi eksternal dan
referensi objek untuk definisi mereka.

A.12.1 Urutan Trigraph


Set karakter program sumber C terkandung dalam ASCII tujuh-bit, tetapi merupakan superset
dari Set Kode Invariant ISO 646-1983. Agar program dapat diwakili dalam
set dikurangi, semua kemunculan dari urutan trigraph berikut digantikan oleh
karakter tunggal yang sesuai. Penggantian ini terjadi sebelum pemrosesan lainnya.

?? = # ?? ([ ?? <{
?? / \ ??)] ??>}
?? ^ ??! | ?? - ~
Tidak ada penggantian seperti itu yang terjadi.
Urutan trigraph baru dengan standar ANSI.

A.12.2 Penyambungan Garis

Halaman 188

189
Baris yang diakhiri dengan karakter backslash \ dilipat dengan menghapus backslash dan
mengikuti karakter baris baru. Ini terjadi sebelum pembagian menjadi token.
A.12.3 Definisi dan Perluasan Makro
Garis kontrol formulir
# define token-sequence identifier

menyebabkan preprocessor mengganti instance pengidentifikasi berikutnya dengan yang diberikan


urutan token; ruang putih memimpin dan tertinggal di sekitar urutan token dibuang. SEBUAH
#define kedua untuk pengidentifikasi yang sama adalah salah kecuali urutan token kedua adalah
identik dengan yang pertama, di mana semua pemisahan ruang putih dianggap setara.

Garis bentuk

# define identifier ( identifier-list ) token-sequence

di mana tidak ada ruang antara pengidentifikasi pertama dan (, adalah definisi makro dengan
parameter yang diberikan oleh daftar pengidentifikasi. Seperti halnya bentuk pertama, ruang putih terkemuka dan tertinggal
sekitar urutan token dibuang, dan makro dapat didefinisikan ulang hanya dengan definisi
di mana jumlah dan ejaan parameter, dan urutan token, identik.

Garis kontrol formulir

# undef identifier

menyebabkan definisi preprosesor pengidentifikasi dilupakan. Tidak salah untuk mendaftar


#undef ke pengidentifikasi yang tidak dikenal.

Ketika makro telah didefinisikan dalam bentuk kedua, instance tekstual makro berikutnya
pengidentifikasi diikuti oleh spasi putih opsional, dan kemudian oleh (, urutan token dipisahkan oleh
koma, dan a) merupakan panggilan makro. Argumen panggilan tersebut adalah
urutan token yang terpisah; koma yang dikutip atau dilindungi oleh kurung bersarang tidak
argumen terpisah. Selama pengumpulan, argumen tidak diperluas secara makro. Jumlah
argumen dalam panggilan harus sesuai dengan jumlah parameter dalam definisi. Setelah
argumen terisolasi, memimpin dan mengekor ruang putih dihapus dari mereka. Lalu tokennya
urutan yang dihasilkan dari setiap argumen diganti untuk setiap kemunculan tanda kutip dari
pengidentifikasi parameter yang sesuai dalam urutan token pengganti makro. Kecuali kalau
parameter dalam urutan penggantian didahului oleh # , atau didahului atau diikuti oleh ## ,
token argumen diperiksa untuk panggilan makro, dan diperluas seperlunya, sesaat sebelumnya
insersi.

Dua operator khusus memengaruhi proses penggantian. Pertama, jika suatu parameter terjadi
dalam urutan token pengganti segera didahului dengan # , tanda kutip ( " ) ditempatkan
di sekitar parameter yang sesuai, dan kemudian # dan pengidentifikasi parameter
digantikan oleh argumen yang dikutip. A \ karakter dimasukkan sebelum setiap " atau \ karakter itu
muncul di sekeliling, atau di dalam, string literal atau karakter konstan dalam argumen.

Kedua, jika urutan token definisi untuk kedua jenis makro berisi operator ## , maka
tepat setelah penggantian parameter, setiap ## dihapus, bersama dengan spasi putih yang aktif
di kedua sisi, untuk menggabungkan token yang berdekatan dan membentuk token baru. Efeknya adalah
tidak terdefinisi apakah token yang tidak valid diproduksi, atau jika hasilnya tergantung pada urutan pemrosesan
yang ## operator. Juga, ## mungkin tidak muncul di awal atau akhir token pengganti
urutan.

Halaman 189

190
Dalam kedua jenis makro, urutan token pengganti berulang kali dipindai ulang untuk lebih
pengidentifikasi didefinisikan. Namun, begitu pengidentifikasi yang diberikan telah diganti dalam ekspansi yang diberikan, itu
tidak diganti jika muncul lagi selama rescanning; melainkan dibiarkan tidak berubah.

Bahkan jika nilai akhir dari ekspansi makro dimulai dengan # , itu tidak dianggap sebagai a
arahan preprocessing.
Rincian proses ekspansi makro dijelaskan lebih tepatnya dalam standar ANSI daripada di
edisi pertama. Perubahan terpenting adalah penambahan operator # dan ## , yang membuatnya
kutipan dan rangkuman diterima. Beberapa aturan baru, terutama yang melibatkan
penggabungan, aneh. (Lihat contoh di bawah.)

Misalnya, fasilitas ini dapat digunakan untuk `` konstanta manifes, '' seperti pada

#define TABSIZE 100


int table [TABSIZE];
Definisi

#define ABSDIFF (a, b) ((a)> (b)? (a) - (b): (b) - (a))


mendefinisikan makro untuk mengembalikan nilai absolut dari perbedaan antara argumennya. Tidak seperti a
berfungsi untuk melakukan hal yang sama, argumen dan nilai yang dikembalikan mungkin memiliki tipe aritmatika
atau bahkan menjadi petunjuk. Juga, argumen, yang mungkin memiliki efek samping, dievaluasi dua kali,
satu kali untuk ujian dan satu kali untuk menghasilkan nilai.
Diberi definisi

#define tempfile (dir) #dir "% s"


tempfile panggilan makro (/ usr / tmp) menghasilkan

"/ usr / tmp" "% s"


yang kemudian akan dikelompokkan menjadi satu string. Setelah

#define cat (x, y) x ## y


kucing panggilan (var, 123) menghasilkan var123 . Namun, kucing panggilan (kucing (1,2), 3) tidak ditentukan: the
Kehadiran ## mencegah argumen panggilan luar agar tidak diperluas. Jadi itu menghasilkan
string token

kucing (1, 2) 3
dan ) 3 (penghentian token terakhir dari argumen pertama dengan token pertama dari argumen kedua)
bukan token hukum. Jika tingkat kedua definisi makro diperkenalkan,

#define xcat (x, y) cat (x, y)


semuanya bekerja lebih lancar; xcat (xcat (1, 2), 3) memang menghasilkan 123 , karena ekspansi
dari xcat sendiri tidak melibatkan operator ## .
Demikian juga, ABSDIFF (ABSDIFF (a, b), c) menghasilkan hasil yang diharapkan, diperluas sepenuhnya.

A.12.4 Inklusi File


Garis kontrol formulir
# include < nama file >

menyebabkan penggantian baris itu dengan seluruh isi nama file file . Karakter dalam
nama file tidak boleh menyertakan > atau baris baru, dan efeknya tidak terdefinisi jika mengandung
dari " , ' , \ , atau / * . File bernama dicari dalam urutan implementasi yang ditentukan
tempat
Demikian pula, garis kontrol formulir

# include " nama file "

Halaman 190

191
mencari terlebih dahulu dalam kaitannya dengan file sumber asli (implementasi sengaja-
frase dependen), dan jika pencarian itu gagal, maka seperti pada form pertama. Efek menggunakan ' , \ , atau
/ * dalam nama file tetap tidak terdefinisi, tetapi > diizinkan.

Akhirnya, arahan formulir

# termasuk token-sequence

tidak cocok dengan salah satu formulir sebelumnya ditafsirkan dengan memperluas urutan token
teks normal; salah satu dari dua bentuk dengan <...> atau "..." harus dihasilkan, dan kemudian diperlakukan sebagai
dijelaskan sebelumnya.

#sertakan file yang dapat disarangkan.

A.12.5 Kompilasi Bersyarat


Bagian dari suatu program dapat dikompilasi secara kondisional, sesuai dengan skema berikut
sintaksis.
preprosesor-kondisional :
if-line text elif-parts else-bagian opt #endif

jika-line :
# jika ekspresi konstan
# pengidentifikasi ifdef
# ifndef pengidentifikasi

bagian elif :
teks baris-elif
elif-bagian opt

garis-elif :
# elif ekspresi konstan

bagian lain :
teks baris lain

else-line :
#lain

Setiap arahan (if-line, elif-line, else-line, dan #endif ) muncul sendiri pada sebuah baris. Itu
ekspresi konstan dalam # jika dan baris # elif berikutnya dievaluasi dalam urutan sampai
ekspresi dengan nilai bukan nol ditemukan; teks yang mengikuti garis dengan nilai nol dibuang.
Teks mengikuti garis arahan yang berhasil diperlakukan secara normal. `` Teks '' di sini merujuk pada apa saja
bahan, termasuk garis preprosesor, yang bukan bagian dari struktur kondisional; itu mungkin
kosong. Setelah baris #if atau #elif berhasil ditemukan dan teksnya diproses, berhasil
Baris #elif dan #else , beserta teksnya, dibuang. Jika semua ekspresi adalah nol,
dan ada #else , teks yang mengikuti #else diperlakukan secara normal. Teks dikontrol oleh
lengan kondisional yang tidak aktif diabaikan kecuali untuk memeriksa sarang yang bersyarat.

Ekspresi konstan dalam #jika dan #elif tunduk pada penggantian makro biasa.
Apalagi segala bentuk ekspresi

pengidentifikasi didefinisikan

atau

didefinisikan ( pengidentifikasi )

Halaman 191

192
diganti, sebelum memindai makro, dengan 1L jika pengidentifikasi didefinisikan dalam preprosesor,
dan dengan 0L jika tidak. Setiap pengidentifikasi yang tersisa setelah ekspansi makro diganti dengan 0L . Akhirnya,
setiap konstanta bilangan bulat dianggap sufiks dengan L , sehingga semua aritmatika dianggap
lama atau tidak ditandatangani lama.

Ekspresi konstan yang dihasilkan ( Par.7.79 ) dibatasi: itu harus integral, dan mungkin tidak
mengandung sizeof , gips, atau konstanta enumerasi.

Garis kontrol

pengidentifikasi #ifdef
pengidentifikasi #ifndef

setara dengan

# jika pengidentifikasi yang ditentukan


# jika! pengidentifikasi didefinisikan

masing-masing.
#elif baru sejak edisi pertama, meskipun telah tersedia adalah beberapa preprosesor. Itu
operator preprosesor yang ditentukan juga baru.

A.12.6 Kontrol Saluran


Untuk kepentingan preprosesor lain yang menghasilkan program C, sebaris di salah satu formulir
# baris konstan " nama file "
# baris konstan

menyebabkan kompiler untuk percaya, untuk keperluan diagnosa kesalahan, bahwa nomor baris
baris sumber berikutnya diberikan oleh konstanta bilangan bulat desimal dan file input saat ini dinamai oleh
pengidentifikasi. Jika nama file yang dikutip tidak ada, nama yang diingat tidak berubah. Makro
dalam baris diperluas sebelum ditafsirkan.

A.12.7 Pembuatan Kesalahan


Garis preprosesor dari formulir
# kesalahan token-sequence opt

menyebabkan preprocessor menulis pesan diagnostik yang menyertakan urutan token.

A.12.8 Pragma
Garis kontrol formulir
# pragma token-sequence opt

menyebabkan preprocessor untuk melakukan tindakan yang bergantung pada implementasi. Tidak dikenal
pragma diabaikan.

A.12.9 Arahan kosong


Garis kontrol formulir
#

tidak berpengaruh.

A.12.10 Nama yang ditentukan sebelumnya

Halaman 192

193
Beberapa pengidentifikasi sudah ditentukan sebelumnya, dan diperluas untuk menghasilkan informasi khusus. Mereka, dan juga
operator ekspansi preprosesor yang ditentukan , mungkin tidak terdefinisi atau didefinisikan ulang.
__LINE__ Konstanta desimal yang berisi nomor baris sumber saat ini.
__FILE__ String literal yang berisi nama file yang sedang dikompilasi.
__DATE__ String literal yang berisi tanggal kompilasi, dalam bentuk "Mmmm dd yyyy"
__TIME__ String literal yang berisi waktu kompilasi, dalam bentuk "hh: mm: ss"

__STDC__
Konstanta 1 . Dimaksudkan bahwa pengidentifikasi ini didefinisikan sebagai 1 hanya dalam standar-
menyesuaikan implementasi.
#error dan #pragma baru dengan standar ANSI; macro preprocessor yang telah ditentukan adalah
baru, tetapi beberapa di antaranya telah tersedia di beberapa implementasi.

A.13 Tata Bahasa


Di bawah ini adalah rekapitulasi tata bahasa yang diberikan sepanjang bagian awal ini
lampiran. Ini memiliki konten yang persis sama, tetapi dalam urutan yang berbeda.
Tata bahasanya memiliki simbol terminal yang tidak terdefinisi integer-konstan , karakter-konstan , mengambang-
konstan , identifier , string , dan enumerasi-konstan ; yang mesin tik gaya kata-kata dan
simbol adalah terminal yang diberikan secara harfiah. Tata bahasa ini dapat ditransformasikan secara mekanis menjadi input
dapat diterima untuk generator parser otomatis. Selain menambahkan tanda sintaksis apa pun
digunakan untuk menunjukkan alternatif dalam produksi, perlu untuk memperluas `` salah satu dari ''
konstruksi, dan (tergantung pada aturan parser-generator) untuk menggandakan masing-masing
produksi dengan opt simbol, sekali dengan simbol dan sekali tanpa. Dengan satu lagi
ubah, yaitu menghapus produksi typedef-name : identifier dan membuat typedef-name a
simbol terminal, tata bahasa ini dapat diterima oleh parser-generator YACC. Hanya ada satu
konflik, yang dihasilkan oleh ambiguitas if-else .

unit terjemahan :
deklarasi eksternal
unit terjemahan-deklarasi eksternal

deklarasi eksternal :
fungsi-definisi
pernyataan

fungsi-definisi :
deklarasi-penentu memilih deklarator daftar deklarasi memilih gabungan-pernyataan

deklarasi :
penentu deklarasi memilih init-declarator-list opt ;

daftar deklarasi :
pernyataan
daftar pernyataan deklarasi

penentu deklarasi :
storage-class-specifier deklarasi-specifier memilih
type-specifier declaration-specifiers opt
tipe-kualifikasi deklarasi-specifier memilih

specifier kelas penyimpanan : salah satunya


register otomatis typedef eksternal extern

tipe specifier : salah satunya


batal char pendek pendek mengapung ditandatangani
unsumed struct-or-union-specifier enum-specifier typedef-name

Halaman 193

194
jenis-kualifikasi : salah satu
const volatile

struct-or-union-specifier :
opt -atau-union identifier opt { struct-declaration-list }

pengidentifikasi struct-or-union

struct-or-union : salah satunya


serikat pekerja

struct-declaration-list :
deklarasi struct
struct-declaration-list deklarasi struct

init-declarator-list :
init-declarator
init-declarator-list , init-declarator

init-declarator :
deklarator
deklarator = penginisialisasi

struct-declaration :
daftar-specifier-kualifikasi-struct-declarator-list ;

specifier-kualifikasi-daftar :
type-specifier specifier-qualifier-list opt
jenis-kualifikasi-daftar-kualifikasi-daftar opt

struct-declarator-list :
struct-declarator
struct-declarator-list , struct-declarator

struct-declarator :
deklarator
declarator opt : ekspresi konstan

enum-specifier :
enum identifier opt { enumerator-list }
pengidentifikasi enum

daftar enumerator :
pencacah
daftar enumerator , enumerator

pencacah :
pengidentifikasi
identifier = ekspresi konstan

deklarator :
pointer memilih deklarator langsung

deklarator langsung :
pengidentifikasi
( deklarator )
direct-declarator [ opt -ekspresi konstan ]

Halaman 194

195
direct-declarator ( parameter-type-list )
direct-declarator ( opt -list identifier )

penunjuk :
* jenis-kualifikasi-daftar memilih
* penunjuk optis jenis-kualifikasi-daftar

jenis-kualifikasi-daftar :
jenis-kualifikasi
tipe-kualifikasi-daftar tipe-kualifikasi

parameter-type-list :
daftar parameter
daftar parameter , ...

daftar parameter :
deklarasi parameter
daftar parameter , deklarasi parameter

deklarasi parameter :
declarator-specifier deklarator
declaration-specifier abstrak-declarator opt

daftar pengidentifikasi :
pengidentifikasi
daftar pengidentifikasi , pengidentifikasi

penginisialisasi :
penugasan-ekspresi
{ daftar penginisialisasi }
{ daftar penginisialisasi ,}

daftar penginisialisasi :
penginisialisasi
daftar initializer , initializer

jenis-nama :
opt -declarator opt -qualifier-list abstrak

abstrak-deklarator :
penunjuk
pointer memilih direct-abstract-declarator

direct-abstract-declarator :
( abstrak-deklarator )
langsung abstrak-declarator opt [ konstan ekspresi opt ]
opt direct-abstract-declarator ( opt -type-list opt )

ketik nama :
pengidentifikasi
pernyataan :
pernyataan-berlabel
ekspresi-pernyataan
pernyataan majemuk
seleksi-pernyataan

Halaman 195

196
iterasi-pernyataan
pernyataan singkat

pernyataan berlabel :
pengidentifikasi : pernyataan
case constant-expression : statement
default: pernyataan

ekspresi-pernyataan :
ekspresi opt ;

pernyataan majemuk :
{ daftar pernyataan memilih daftar pernyataan memilih }

daftar pernyataan :
pernyataan
pernyataan-pernyataan pernyataan

seleksi-pernyataan :
jika ( ekspresi ) pernyataan
jika ( ekspresi ) pernyataan lain pernyataan
beralih ( ekspresi ) pernyataan

iterasi-pernyataan :
sementara ( ekspresi ) pernyataan
lakukan statement while ( ekspresi ) ;
untuk ( ekspresi opt ; ekspresi opt ; ekspresi opt ) pernyataan

pernyataan singkat :
pengidentifikasi goto ;
terus;
istirahat;
opt kembali ekspresi ;

ekspresi :
penugasan-ekspresi
ekspresi , penugasan-ekspresi

penugasan-ekspresi :
ekspresi bersyarat
penugasan-ekspresi tugas-operator-ekspresi unary

operator penugasan : salah satu


= * = / =% = + = - = << = >> = & = ^ = | =

ekspresi bersyarat :
logis-ATAU-ekspresi
logis-ATAU-ekspresi ? Ekspresi : ekspresi-kondisional

ekspresi konstan :
ekspresi bersyarat

logis-ATAU-ekspresi :
logis-DAN-ekspresi
logical-OR-expression || logis-DAN-ekspresi

Halaman 196

197
logis-DAN-ekspresi :
inklusif-ATAU-ekspresi
logis-DAN-ekspresi && inklusif-ATAU-ekspresi

inklusif-ATAU-ekspresi :
eksklusif-ATAU-ekspresi
inklusif-ATAU-ekspresi | eksklusif-ATAU-ekspresi

eksklusif-ATAU-ekspresi :
DAN-ekspresi
eksklusif-ATAU-ekspresi ^ DAN-ekspresi

DAN-ekspresi :
persamaan-ekspresi
DAN-ekspresi & persamaan-ekspresi

persamaan ekspresi :
ekspresi relasional
persamaan persamaan == ekspresi relasional
persamaan-persamaan ! = ekspresi relasional

ekspresi relasional :
pergeseran-ekspresi
ekspresi-relasional < ekspresi-bergeser
ekspresi-relasional > ekspresi-pergeseran
ekspresi relasional <= pergeseran-ekspresi
relational-expression > = shift-ekspresi

pergeseran-ekspresi :
ekspresi aditif
shift-expression << aditif-ekspresi
shift-expression >> additive-expression

ekspresi-aditif :
ekspresi multiplikatif
additive-expression + multiplicative-expression
aditif-ekspresi - ekspresi multiplikatif

ekspresi multiplikatif :
multiplicative-expression * cast-expression
multi-ekspresi / pemeran-ekspresi
multiplicative-expression % cast-expression

cast-expression :
ekspresi unary
( ketik-nama ) ekspresi-pemeran

ekspresi unary :
ekspresi postfix
++ ekspresi unary
- Ekspresi unary
ekspresi pemain unary-operator
ukuran ekspresi unary
sizeof ( tipe-nama )

Halaman 197

198
operator unary : salah satu dari
& * + - ~!

ekspresi postfix :
ekspresi primer
postfix-expression [ ekspresi ]
postfix-expression ( argumen-ekspresi-daftar opt )
ekspresi postfix . pengidentifikasi
postfix-expression -> + identifier
postfix-expression ++
ekspresi postfix -

ekspresi primer :
pengidentifikasi
konstan
tali
( ekspresi )
argumen-ekspresi-daftar :
penugasan-ekspresi
penugasan-ekspresi-daftar , penugasan-ekspresi

konstan :
integer-konstan
karakter-konstan
mengambang-konstan
enumerasi-konstan

Tata bahasa berikut untuk preprocessor merangkum struktur garis kontrol, tetapi
tidak cocok untuk parsing mekanis. Ini termasuk teks simbol , yang berarti program biasa
teks, garis kontrol preprosesor non-kondisional, atau kondisional preprosesor lengkap
instruksi.

garis kontrol :
# define token-sequence identifier
# define identifier ( identifier, ..., identifier ) token-sequence
# undef identifier
# include < nama file >
# include " nama file "
# baris konstan " nama file "
# baris konstan
# kesalahan token-sequence opt
# pragma token-sequence opt
#
preprocessor-conditional

preprosesor-kondisional :
if-line text elif-parts else-bagian opt #endif

jika-line :
# jika ekspresi konstan
# pengidentifikasi ifdef
# ifndef pengidentifikasi

Halaman 198

199
bagian elif :
teks baris-elif
elif-bagian opt

garis-elif :
# elif ekspresi konstan

bagian lain :
teks baris lain

else-line :
#lain
Halaman 199

200

Lampiran B - Perpustakaan Standar


Apendiks ini adalah ringkasan perpustakaan yang ditentukan oleh standar ANSI. Perpustakaan standar
bukan bagian dari bahasa C yang tepat, tetapi lingkungan yang mendukung standar C akan menyediakan
deklarasi fungsi dan tipe serta definisi makro dari perpustakaan ini. Kami telah menghilangkan beberapa
fungsi yang utilitasnya terbatas atau mudah disintesis dari yang lain; kami telah menghilangkan multi-
karakter byte; dan kami telah menghilangkan diskusi tentang masalah lokal; yaitu, properti yang bergantung
pada bahasa lokal, kebangsaan, atau budaya.
Fungsi, jenis, dan makro perpustakaan standar dinyatakan dalam header standar :

<assert.h> <float.h> <math.h> <stdarg.h> <stdlib.h>


<ctype.h> <limit.h> <setjmp.h> <stddef.h> <string.h>
<errno.h> <locale.h> <signal.h> <stdio.h> <time.h>
Header dapat diakses oleh
#termasuk < header >

Tajuk dapat dimasukkan dalam urutan apa pun dan berapa kali. Header harus disertakan
di luar deklarasi atau definisi eksternal dan sebelum penggunaan apa pun yang dinyatakannya. SEBUAH
header tidak perlu berupa file sumber.

Pengidentifikasi eksternal yang dimulai dengan garis bawah disediakan untuk digunakan oleh perpustakaan, seperti juga semua
pengidentifikasi lain yang dimulai dengan garis bawah dan huruf besar atau garis bawah lainnya.

B.1 Input dan Output: <stdio.h>


Fungsi, tipe, dan makro input dan output yang didefinisikan dalam <stdio.h> mewakili hampir satu
sepertiga dari perpustakaan.
Sebuah sungai merupakan sumber atau tujuan data yang mungkin terkait dengan disk atau lainnya
periferal. Perpustakaan mendukung aliran teks dan aliran biner, meskipun pada beberapa sistem,
terutama UNIX, ini identik. Aliran teks adalah urutan garis; setiap baris memiliki nol atau
lebih banyak karakter dan diakhiri oleh '\ n' . Lingkungan mungkin perlu mengubah aliran teks
ke atau dari beberapa representasi lain (seperti pemetaan '\ n' ke carriage return dan linefeed).
Aliran biner adalah urutan byte yang tidak diproses yang merekam data internal, dengan properti
bahwa jika itu ditulis, lalu membaca kembali pada sistem yang sama, itu akan membandingkan sama.

Aliran terhubung ke file atau perangkat dengan membukanya ; koneksi rusak oleh penutupan tersebut
aliran. Membuka file mengembalikan pointer ke objek tipe FILE , yang merekam apa pun
informasi diperlukan untuk mengontrol aliran. Kami akan menggunakan `` penunjuk file '' dan `` aliran ''
dipertukarkan ketika tidak ada ambiguitas.

Ketika sebuah program memulai eksekusi, ketiga stream stdin , stdout , dan stderr sudah ada
Buka.

B.1.1 Operasi File


Fungsi-fungsi
jenis berikut oleh
yang diproduksi berhubungan dengan operasi pada file. Tipe size_t adalah integral yang tidak ditandatangani
ukuran operator.
FILE * fopen (const char * nama file, const char * mode)
fopen membuka file bernama, dan mengembalikan aliran, atau NULL jika upaya gagal. Hukum
nilai untuk mode meliputi:
"r" buka file teks untuk dibaca
"w" membuat file teks untuk ditulis; buang konten sebelumnya jika ada
"a" tambahkan; buka atau buat file teks untuk ditulis di akhir file

Halaman 200

201

"r +" file teks terbuka untuk pembaruan (yaitu, membaca dan menulis)
"w +" buat file teks untuk pembaruan, buang konten sebelumnya jika ada
"a +" tambahkan; buka atau buat file teks untuk pembaruan, tulis di akhir
Mode pembaruan memungkinkan membaca dan menulis file yang sama; fflush atau penentuan posisi file
fungsi harus dipanggil antara baca dan tulis atau sebaliknya. Jika mode termasuk b
setelah huruf awal, seperti pada "rb" atau "w + b" , itu menunjukkan file biner. Nama file adalah
terbatas pada FILENAME_MAX karakter. Paling banyak, file FOPEN_MAX dapat dibuka sekaligus.
FILE * freopen (const char * nama file, mode const char *, FILE * stream)
freopen membuka file dengan mode yang ditentukan dan menghubungkan aliran dengan itu. Itu
mengembalikan aliran , atau NULL jika terjadi kesalahan. freopen biasanya digunakan untuk mengubah
file yang terkait dengan stdin , stdout , atau stderr .
int fflush (FILE * streaming)
Pada aliran keluaran, fflush menyebabkan data buffered tetapi tidak tertulis ditulis; di
input stream, efeknya tidak terdefinisi. Ini mengembalikan EOF untuk kesalahan tulis, dan nol
jika tidak. fflush (NULL) menyiram semua aliran keluaran.
int fclose (FILE * stream)
fclose mem- flush data yang tidak tertulis untuk streaming , membuang input buffer yang belum dibaca,
membebaskan penyangga yang dialokasikan secara otomatis, lalu menutup aliran. Ini mengembalikan EOF jika ada
kesalahan terjadi, dan nol sebaliknya.
int remove (const char * nama file)
hapusmenghapus file bernama, sehingga upaya selanjutnya untuk membukanya akan gagal. Itu
mengembalikan non-nol jika upaya gagal.
int rename (const char * oldname, const char * newname)
rename mengubah nama file; mengembalikan non-nol jika upaya gagal.
FILE * tmpfile (batal)
tmpfile membuat file sementara mode "wb +" yang akan dihapus secara otomatis
ketika ditutup atau ketika program berakhir secara normal. tmpfile mengembalikan aliran, atau
NULL jika tidak bisa membuat file.
char * tmpnam (char s [L_tmpnam])
tmpnam (NULL) membuat string yang bukan nama file yang ada, dan mengembalikan a
pointer ke array statis internal. tmpnam (s) menyimpan string di s serta kembali
sebagai nilai fungsi; s harus memiliki ruang untuk setidaknya karakter L_tmpnam . tmpnam
menghasilkan nama yang berbeda setiap kali dipanggil; paling banyak TMP_MAX nama berbeda
dijamin selama pelaksanaan program. Perhatikan bahwa tmpnam membuat nama, bukan a
mengajukan.
int setvbuf (FILE * stream, char * buf, mode int, size_t ukuran)
setvbuf mengontrol buffering untuk stream; itu harus dipanggil sebelum membaca, menulis atau
operasi lainnya. Sebuah modus dari _IOFBF menyebabkan buffer penuh, _IOLBF garis buffering
file teks, dan _IONBF tanpa buffering. Jika buf bukan NULL , itu akan digunakan sebagai buffer,
jika tidak, buffer akan dialokasikan. size menentukan ukuran buffer. pengembalian setvbuf
bukan nol untuk kesalahan apa pun.
batal setbuf (FILE * streaming, char * buf)
Jika buf adalah NULL , buffering dimatikan untuk sungai. Kalau tidak, setbuf adalah setara
untuk (membatalkan) setvbuf (streaming, buf, _IOFBF, BUFSIZ) .
B.1.2 Output Terformat
Fungsi printf menyediakan konversi output yang diformat.

int fprintf (FILE * stream, format const char *, ...)


fprintf mengonversi dan menulis keluaran untuk dialirkan di bawah kendali format . Nilai pengembalian
adalah jumlah karakter yang ditulis, atau negatif jika terjadi kesalahan.
String format berisi dua jenis objek: karakter biasa, yang disalin ke
aliran output, dan spesifikasi konversi, yang masing-masing menyebabkan konversi dan pencetakan

Halaman 201
202
argumen berturut-turut berikutnya ke fprintf . Setiap spesifikasi konversi dimulai dengan
karakter % dan diakhiri dengan karakter konversi. Antara % dan karakter konversi
mungkin ada, dalam urutan:
â € ¢ Bendera (dalam urutan apa pun), yang mengubah spesifikasi:

o - , yang menentukan penyesuaian kiri dari argumen yang dikonversi di bidangnya.

o + , yang menentukan bahwa nomor tersebut akan selalu dicetak dengan tanda.

o spasi : jika karakter pertama bukan tanda, spasi akan diawali.

o 0 : untuk konversi numerik, tentukan bantalan ke lebar bidang dengan penuntun


nol

o # , yang menentukan bentuk output alternatif. Untuk o , digit pertama akan menjadi
nol. Untuk x atau X , 0x atau 0X akan diawali dengan hasil yang bukan nol. Untuk e , E , f , g ,
dan G , output akan selalu memiliki titik desimal; untuk g dan G , tertinggal nol
tidak akan dihapus.
â € ¢ Angka yang menentukan lebar bidang minimum. Argumen yang dikonversi akan dicetak dalam
bidang setidaknya selebar ini, dan lebih luas jika perlu. Jika argumen yang dikonversi memiliki lebih sedikit
karakter daripada lebar bidang itu akan diisi di sebelah kiri (atau kanan, jika penyesuaian kiri
telah diminta) untuk membuat lebar bidang. Karakter padding biasanya
space, tetapi bernilai 0 jika flag zero padding hadir.
â € ¢ Periode, yang memisahkan lebar bidang dari presisi.

â € ¢ Angka, ketepatan, yang menentukan jumlah karakter maksimum yang akan dicetak
dari string, atau jumlah digit yang akan dicetak setelah titik desimal untuk e , E , atau f
konversi, atau jumlah digit signifikan untuk konversi g atau G , atau jumlah
digit yang akan dicetak untuk integer (angka 0 s akan ditambahkan untuk menggantikan yang diperlukan
lebar).

â € ¢ Sebuah panjang pengubah h , l (surat ela), atau L . `` h '' menunjukkan argumen yang sesuai
akan dicetak sebagai short atau unsigned short ; `` l '' menunjukkan bahwa argumennya adalah a
long atau unsigned long , `` L '' menunjukkan bahwa argumennya panjang ganda .

Lebar atau presisi atau keduanya dapat ditentukan sebagai * , dalam hal ini nilai dihitung oleh
mengonversi argumen berikutnya, yang harus int .

Karakter konversi dan artinya ditunjukkan pada Tabel B.1. Jika karakter setelah
% bukan karakter konversi, perilaku tidak terdefinisi.

Tabel B.1 Konversi Printf


Karakter Jenis argumen; Dicetak sebagai
d, i int ; notasi desimal bertanda tangan
Hai int ; notasi oktal yang tidak ditandatangani (tanpa nol di depan).
x, X int unsigned ; notasi heksadesimal unsigned (tanpa 0x atau 0X terkemuka ),
menggunakan abcdef untuk 0x atau ABCDEF untuk 0X .
kamu int ; notasi desimal tak bertanda tangan.
c int ; karakter tunggal, setelah konversi ke karakter yang tidak ditandatangani
s char * ; karakter dari string dicetak hingga '\ 0' tercapai atau hingga
jumlah karakter yang ditunjukkan oleh presisi telah dicetak.

Halaman 202

203

ganda ; notasi desimal dari bentuk [-] mmm.ddd , di mana jumlah d adalah
f diberikan dengan presisi. Presisi default adalah 6; ketelitian 0 menekan
titik desimal.
ganda ; notasi desimal dari bentuk [-] m.dddddd e +/- xx atau [-] m.dddddd E +/-
e, E xx , di mana jumlah d ditentukan oleh presisi. Presisi standarnya adalah
6; ketelitian 0 menekan titik desimal.
ganda ; % e atau % E digunakan jika eksponen kurang dari -4 atau lebih besar dari atau sama dengan
g, G presisi; jika tidak % f digunakan. Nol tertinggal dan titik desimal tertinggal adalah
tidak dicetak.
hal batal * ; cetak sebagai penunjuk (representasi yang bergantung pada implementasi).
n int * ; jumlah karakter yang ditulis sejauh ini oleh panggilan ke printf ini ditulis
dalam argumen. Tidak ada argumen yang dikonversi.
% tidak ada argumen yang dikonversi; cetak%
int printf (format const * char, ...)
printf (...) setara dengan fprintf (stdout, ...) .
int sprintf (format char * s, const char *, ...)
sprintf sama dengan printf kecuali bahwa output ditulis ke dalam string s ,
diakhiri dengan '\ 0' . s harus cukup besar untuk menampung hasilnya. Hitungan kembali tidak
tidak termasuk '\ 0' .
int vprintf (format const char *, va_list arg)
int vfprintf (FILE * stream, format const char *, va_list arg)
int vsprintf (format char * s, const char *, va_list arg)
Fungsi vprintf , vfprintf , dan vsprintf setara dengan yang sesuai
fungsi printf , kecuali bahwa daftar argumen variabel diganti oleh arg , yang memiliki
telah diinisialisasi oleh makro va_start dan mungkin va_arg panggilan. Lihat diskusi tentang
<stdarg.h> di Bagian B.7 .

B.1.3 Input Terformat


Fungsi scanf berkaitan dengan konversi input yang diformat.

int fscanf (FILE * stream, format const char *, ...)


fscanf membaca dari aliran di bawah kendali format , dan memberikan nilai yang dikonversi melalui
argumen selanjutnya, yang masing-masing harus berupa pointer . Ini kembali ketika format habis.
fscanf mengembalikan EOF jika akhir file atau kesalahan terjadi sebelum konversi apa pun; jika tidak kembali
jumlah item input yang dikonversi dan ditugaskan.
String format biasanya berisi spesifikasi konversi, yang digunakan untuk mengarahkan
interpretasi input. String format dapat berisi:

â € ¢ Kosong atau tab, yang tidak diabaikan.


â € ¢ Karakter biasa (bukan%), yang diharapkan cocok dengan spasi non-putih berikutnya
karakter aliran input.

â € ¢ Spesifikasi konversi, yang terdiri dari % , penindasan penugasan opsional


karakter * , angka opsional yang menentukan lebar bidang maksimum, opsional h , l , atau
L menunjukkan lebar target, dan karakter konversi.
Spesifikasi konversi menentukan konversi bidang input selanjutnya. Biasanya itu
hasil ditempatkan dalam variabel yang ditunjukkan oleh argumen yang sesuai. Jika penugasan
penindasan ditunjukkan oleh * , seperti dalam % * s , bagaimanapun, bidang input hanya dilewati; tidak
tugas dibuat. Bidang input didefinisikan sebagai string karakter spasi non-putih; Itu
meluas ke karakter spasi putih berikutnya atau sampai lebar bidang, jika ditentukan, adalah
habis. Ini menyiratkan bahwa scanf akan membaca lintas batas untuk menemukan inputnya, karena

Halaman 203

204
baris baru adalah ruang putih. (Karakter spasi putih kosong, tab, baris baru, carriage return,
tab vertikal, dan formfeed.)
Karakter konversi menunjukkan interpretasi bidang input. Yang sesuai
argumen harus berupa pointer. Karakter konversi hukum ditunjukkan pada Tabel B.2.

Karakter konversi d , i , n , o , u , dan x dapat didahului oleh h jika argumennya adalah a


pointer ke short daripada int, atau dengan l (letter ell) jika argumennya adalah pointer ke long . Itu
karakter konversi e , f , dan g dapat didahului oleh l jika penunjuk menjadi dua kali lipat daripada
float ada dalam daftar argumen, dan oleh L jika pointer ke double panjang .

Tabel B.2 Konversi Scanf

Karakter Memasukan data; Jenis argumen


d bilangan bulat desimal; int *

saya
bilangan bulat; int * . Bilangan bulat mungkin dalam oktal (awalan 0 ) atau heksadesimal (awalan 0x
atau 0X ).
Hai octal integer (dengan atau tanpa memimpin nol); int * .
kamu bilangan bulat desimal yang tidak ditandatangani; unsigned int * .
x bilangan bulat heksadesimal (dengan atau tanpa memimpin 0x atau 0X ); int * .
karakter; char * . Karakter input selanjutnya ditempatkan dalam larik yang ditunjukkan, ke atas
ke nomor yang diberikan oleh bidang lebar; standarnya adalah 1. Tidak ada '\ 0' ditambahkan. Itu
c
lompatan normal karakter spasi ditekan dalam kasus ini; untuk membaca
karakter spasi non-putih berikutnya, gunakan % 1s .
string karakter spasi non-putih (tidak dikutip); char * , menunjuk ke sebuah array
s karakter yang cukup besar untuk menampung string dan penghentian '\ 0' nantinya
ditambahkan.
angka titik-mengambang; mengapung * . Format input untuk float adalah tanda opsional,
e, f, g serangkaian angka yang mungkin mengandung titik desimal, dan eksponen opsional
bidang yang berisi E atau e diikuti oleh bilangan bulat yang mungkin ditandatangani.
hal nilai pointer seperti yang dicetak oleh printf ("% p"); , batal * .

n
menulis ke dalam argumen jumlah karakter yang dibaca sejauh ini oleh panggilan ini; int * .
Tidak ada input yang dibaca. Jumlah item yang dikonversi tidak bertambah.

[...]
cocok dengan string karakter input non-kosong terpanjang dari set di antara
kurung; char * . A '\ 0' ditambahkan. [] ...] termasuk ] dalam set.
[^ ...]
cocok dengan string karakter input non-kosong terpanjang yang tidak berasal dari set
antara kurung; char * . A '\ 0' ditambahkan. [^] ...] termasuk ] dalam set.
% % literal; tidak ada tugas yang dilakukan.
int scanf (format const char *, ...)
scanf (...) identik dengan fscanf (stdin, ...) .
int sscanf (format const char * s, const char *, ...)
sscanf (s, ...) setara dengan scanf (...) kecuali karakter inputnya adalah
diambil dari string s .
B.1.4 Fungsi Input dan Output Karakter
int fgetc (FILE * streaming)
fgetc mengembalikan karakter aliran berikutnya sebagai karakter yang tidak ditandai (dikonversi menjadi
int ), atau EOF jika akhir file atau kesalahan terjadi.
char * fgets (char * s, int n, FILE * stream)
fgets membaca paling berikutnya n-1 karakter ke dalam array s , berhenti jika baris baru adalah
ditemui; baris baru termasuk dalam larik, yang diakhiri oleh '\ 0' . uang
mengembalikan s , atau NULL jika akhir file atau kesalahan terjadi.

Halaman 204

205
int fputc (int c, FILE * stream)
fputc menulis karakter c (dikonversi ke char unsigend ) di aliran . Ia kembali
karakter yang ditulis, atau EOF untuk kesalahan.
int fputs (const char * s, FILE * stream)
fputs menulis string s (yang tidak perlu mengandung \ n ) saat streaming ; mengembalikan non-
negatif, atau EOF untuk kesalahan.
int getc (FILE * stream)
getc sama dengan fgetc kecuali jika itu adalah makro, ia dapat mengevaluasi aliran lebih banyak
dari sekali.
int getchar (batal)
getchar setara dengan getc (stdin) .
char * gets (char * s)
akan membaca baris input berikutnya ke dalam array s ; ini menggantikan baris baru dengan
'\ 0' . Mengembalikan s , atau NULL jika akhir file atau kesalahan terjadi.
int putc (int c, FILE * stream)
putc setara dengan fputc kecuali bahwa jika itu adalah makro, ia dapat mengevaluasi aliran lebih banyak
dari sekali.
int putchar (int c)
putchar (c) setara dengan putc (c, stdout) .
int puts (const char * s)
puts menulis string s dan baris baru ke stdout . Ini mengembalikan EOF jika terjadi kesalahan,
tidak negatif sebaliknya.
int ungetc (int c, FILE * stream)
ungetc mendorong c (dikonversi ke char yang tidak ditandatangani ) kembali ke stream , di mana ia akan berada
dikembalikan pada bacaan berikutnya. Hanya satu karakter pushback per stream yang dijamin.
EOF mungkin tidak didorong kembali. ungetc mengembalikan karakter yang didorong mundur, atau EOF untuk
kesalahan.
B.1.5 Fungsi Input dan Output Langsung
size_t fread (void * ptr, size_t size, size_t nobj, FILE * stream)
fread membaca dari aliran ke dalam array ptr paling nobj benda ukuran ukuran . ketakutan
mengembalikan jumlah objek yang dibaca; ini mungkin kurang dari jumlah yang diminta. feof
dan ferror harus digunakan untuk menentukan status.
size_t fwrite (tidak berlaku * ptr, ukuran size_t, size_t nobj, FILE * stream)
fwrite menulis, dari ptr array , objek nobj ukuran ukuran on stream . Ini mengembalikan
jumlah objek yang ditulis, yang kurang dari nobj pada kesalahan.
B.1.6 Fungsi Penentuan Posisi File
int fseek (FILE * streaming, offset panjang, asal int)
fseek mengatur posisi file untuk streaming ; baca atau tulis selanjutnya akan mengakses data
mulai dari posisi baru. Untuk file biner, posisi diatur ke karakter offset
dari asal , yang mungkin SEEK_SET (awal), SEEK_CUR (posisi saat ini), atau
SEEK_END (akhir file). Untuk aliran teks, offset harus nol, atau nilai yang dikembalikan oleh
ftell (dalam hal ini asal harus SEEK_SET ). fseek mengembalikan non-zero on error.
long ftell (FILE * stream)
ftell mengembalikan posisi file saat ini untuk streaming , atau -1 pada kesalahan.
membatalkan mundur (FILE * streaming)
rewind (fp) setara dengan fseek (fp, 0L, SEEK_SET); clearerr (fp) .
int fgetpos (FILE * streaming, fpos_t * ptr)
fgetpos mencatat posisi saat ini dalam aliran di * ptr , untuk penggunaan selanjutnya oleh
fsetpos . Tipe fpos_t cocok untuk merekam nilai-nilai tersebut. pengembalian fgetpos non-
nol pada kesalahan.
int fsetpos (FILE * streaming, const fpos_t * ptr)

Halaman 205

206

aliranposisi fsetpos pada posisi yang direkam oleh fgetpos di * ptr . fsetpos
mengembalikan bukan nol pada kesalahan.
B.1.7 Fungsi Kesalahan
Banyak fungsi di perpustakaan menetapkan indikator status ketika kesalahan atau akhir file terjadi. Ini
indikator dapat diatur dan diuji secara eksplisit. Selain itu, ekspresi integer errno (dideklarasikan
di <errno.h> ) dapat berisi nomor kesalahan yang memberikan informasi lebih lanjut tentang yang paling
kesalahan terbaru.
void clearerr (FILE * stream)
clearerr menghapus akhir file dan indikator kesalahan untuk streaming .
int feof (FILE * streaming)
feof mengembalikan non-nol jika akhir indikator file untuk aliran diatur.
int ferror (FILE * stream)
ferror mengembalikan bukan nol jika indikator kesalahan untuk aliran diatur.
void perror (const char * s)
perror mencetak s dan pesan kesalahan yang ditentukan implementasi terkait dengan
integer di errno , seolah-olah oleh
fprintf (stderr, "% s:% s \ n", s, " pesan kesalahan ");
Lihat strerror di Bagian B.3 .

B.2 Tes Kelas Karakter: <ctype.h>


Header <ctype.h> menyatakan fungsi untuk menguji karakter. Untuk setiap fungsi, tombol
daftar argumen adalah int , yang nilainya harus EOF atau diwakili sebagai char yang tidak ditandatangani , dan
nilai kembali adalah int . Fungsi mengembalikan non-nol (benar) jika argumen c memenuhi
kondisi yang dijelaskan, dan nol jika tidak.
isalnum (c) isalpha (c) atau isdigit (c) benar
isalpha (c) isupper (c) atau islower (c) benar
karakter kontrol iscntrl (c)
isdigit (c) digit desimal
isgraph (c) karakter pencetakan kecuali spasi
islower (c) huruf kecil
isprint (c) karakter pencetakan termasuk spasi
ispunct (c) karakter pencetakan kecuali spasi atau huruf atau angka
ruang isspace (c) , formfeed, baris baru, carriage return, tab, tab vertikal
isupper (c) huruf besar
isxdigit (c) digit heksadesimal

Dalam set karakter ASCII tujuh-bit, karakter pencetakan adalah 0x20 ('') hingga 0x7E ('-') ;
karakter kontrolnya adalah 0 NUL hingga 0x1F (AS), dan 0x7F (DEL).

Selain itu, ada dua fungsi yang mengubah huruf besar-kecil:


int tolower (c) mengonversi c ke huruf kecil
int toupper (c) mengonversi c ke huruf besar
Jika c adalah huruf besar, tolower (c) mengembalikan huruf kecil yang sesuai,
toupper (c) mengembalikan huruf besar yang sesuai; jika tidak kembali c .

B.3 Fungsi String: <string.h>


Ada dua kelompok fungsi string yang didefinisikan di header <string.h> . Yang pertama miliki
nama yang dimulai dengan str ; yang kedua memiliki nama yang dimulai dengan mem . Kecuali untuk memmove ,
perilaku tidak terdefinisi jika penyalinan terjadi antara objek yang tumpang tindih. Perbandingan
fungsi memperlakukan argumen sebagai array char yang tidak ditandatangani .

Halaman 206
207
Dalam tabel berikut, variabel s dan t adalah tipe char * ; cs dan ct adalah tipe const char
* ; n adalah tipe size_t ; dan c adalah int yang dikonversi menjadi char .
char * strcpy (s, ct) menyalin string ct ke string s , termasuk '\ 0' ; kembali s .
arang salin paling banyak n karakter string ct to s ; kembali s . Pad dengan '\ 0 ' s
* strncpy (s, ct, n) jika ct memiliki kurang dari n karakter.
char * strcat (s, ct) menggabungkan string ct ke akhir string s ; kembali s .
arang paling banyak n karakter string ct ke string s , terminate s
* strncat (s, ct, n) dengan '\ 0' ; kembali s .
bandingkan string cs dengan string ct , kembalikan <0 jika cs <ct , 0 jika cs == ct , atau> 0
int strcmp (cs, ct)
jika cs> ct .
int bandingkan paling banyak n karakter string cs dengan string ct ; return <0 if
strncmp (cs, ct, n)
cs <ct , 0 if cs == ct , atau> 0 if cs> ct .
char * strchr (cs, c) mengembalikan pointer ke kemunculan pertama dari c in cs atau NULL jika tidak ada.
char * strrchr (cs, c) mengembalikan pointer ke kejadian terakhir dari c in cs atau NULL jika tidak ada.
size_t
strspn (cs, ct) mengembalikan panjang awalan cs yang terdiri dari karakter dalam ct .
size_t
strcspn (cs, ct) mengembalikan panjang awalan cs yang terdiri dari karakter tidak dalam ct .
arang mengembalikan pointer ke kemunculan pertama dalam string cs dari string karakter apa pun
* strpbrk (cs, ct)
, atau NULL jika tidak ada.
ct
mengembalikan pointer ke kemunculan pertama string ct di cs , atau NULL jika tidak
char * strstr (cs, ct)
menyajikan.
size_t strlen (cs) mengembalikan panjang cs .
mengembalikan pointer ke string yang ditentukan implementasi yang terkait dengan
char * strerror (n)
kesalahan n .
char * strtok (s, ct) strtok mencari s untuk token yang dibatasi oleh karakter dari ct ; Lihat
di bawah.
Urutan panggilan strtok (s, ct) membelah s menjadi token, masing-masing dibatasi oleh karakter dari
ct . Panggilan pertama secara berurutan memiliki non NULL s , ia menemukan dalam tanda pertama s terdiri dari
karakter tidak dalam ct ; ini mengakhiri dengan menimpa karakter s berikutnya dengan '\ 0' dan
mengembalikan pointer ke token. Setiap panggilan selanjutnya, ditunjukkan dengan nilai NULL s , mengembalikan
selanjutnya token tersebut, mencari dari hanya melewati akhir yang sebelumnya. strtok mengembalikan NULL
ketika tidak ada token lebih lanjut ditemukan. String ct mungkin berbeda pada setiap panggilan.

Fungsi mem ... dimaksudkan untuk memanipulasi objek sebagai array karakter; maksudnya adalah
antarmuka ke rutinitas yang efisien. Dalam tabel berikut, s dan t bertipe void * ; cs dan ct adalah
dari tipe const batal * ; n adalah tipe size_t ; dan c adalah int yang dikonversi menjadi char yang tidak ditandatangani .
kosong
* memcpy (s, ct, n) salin n karakter dari ct ke s , dan kembali s .
kosong
* memmove (s, ct, n) sama seperti memcpy kecuali bahwa itu berfungsi bahkan jika objek tumpang tindih.
int memcmp (cs, ct, n) membandingkan karakter n pertama cs dengan ct ; kembali dengan strcmp .
kosong mengembalikan pointer ke kemunculan pertama karakter c dalam cs , atau NULL jika tidak
* memchr (cs, c, n) hadir di antara n karakter pertama .
void * memset (s, c, n) menempatkan karakter c ke dalam n karakter pertama dari s , mengembalikan s .

B.4 Fungsi Matematika: <math.h>


Header <math.h> menyatakan fungsi matematika dan makro.
Makro EDOM dan ERANGE (ditemukan di <errno.h> ) adalah konstanta integral nol
digunakan untuk sinyal domain dan berbagai kesalahan untuk fungsi; HUGE_VAL adalah nilai ganda positif .
Sebuah kesalahan domain terjadi jika argumen berada di luar domain di mana fungsi ini
didefinisikan. Pada kesalahan domain, errno diatur ke EDOM ; nilai kembali ditentukan oleh implementasi.
Sebuah kesalahan rentang terjadi jika hasil dari fungsi tersebut tidak dapat direpresentasikan sebagai ganda . Jika

Halaman 207

208
hasil meluap, fungsi mengembalikan HUGE_VAL dengan tanda yang benar, dan errno diatur ke
ERANGE . Jika hasilnya underflow, fungsi mengembalikan nol; apakah errno diatur ke ERANGE adalah
implementasi-didefinisikan.

Dalam tabel berikut, x dan y bertipe double , n adalah int , dan semua fungsi mengembalikan double .
Sudut untuk fungsi trigonometri diekspresikan dalam radian.
dosa (x) sinus x
cos (x) cosinus x
tan (x) garis singgung x
asin (x) sin -1 (x) dalam rentang [-pi / 2, pi / 2], x dalam [-1,1].
acos (x) cos -1 (x) dalam rentang [0, pi], x dalam [-1,1].
atan (x) tan -1 (x) dalam rentang [-pi / 2, pi / 2].
atan2 (y, x) tan -1 (y / x) dalam rentang [-pi, pi].
sinh (x) sinus hiperbolik x
cosh (x) cosinus hiperbolik x
tanh (x) garis singgung hiperbolik x
exp (x) fungsi eksponensial e x
log (x) logaritma natural ln (x), x > 0.
log10 (x) logaritma basis 10 log 10 (x), x > 0.
x y . Kesalahan domain terjadi jika x = 0 dan y <= 0 , atau jika x <0 dan y bukan
pow (x, y)
bilangan bulat.
sqrt (x) akar kuadrat dari x, x> = 0.
ceil (x) integer terkecil tidak kurang dari x , sebagai dobel .
lantai (x) integer terbesar tidak lebih besar dari x , sebagai dobel .
hebat (x) nilai absolut | x |
ldexp (x, n) x*2n
membagi x menjadi fraksi yang dinormalisasi dalam interval [1 / 2,1) yang dikembalikan,
frexp (x, int
*aku p) dan kekuatan 2, yang disimpan dalam * exp . Jika x adalah nol, kedua bagian dari
hasilnya nol.
modf (x, dobel membagi x menjadi bagian integral dan fraksional, masing-masing dengan tanda yang sama dengan x . Itu
*aku p) menyimpan bagian integral dalam * ip , dan mengembalikan bagian fraksional.
sisa floating-point dari x / y , dengan tanda yang sama dengan x . Jika y adalah nol, maka
fmod (x, y)
hasilnya ditentukan implementasi.

B.5 Fungsi Utilitas: <stdlib.h>


Header <stdlib.h> menyatakan fungsi untuk konversi angka, alokasi penyimpanan, dan
tugas serupa. dua kali lipat (const char * s)
atof bertobat s untuk ganda ; itu setara dengan strtod (s, (char **) NULL) .
int atoi (const char * s)
bertobat s ke int ; itu setara dengan (int) strtol (s, (char **) NULL, 10) .
atol panjang (const char * s)
bertobat s untuk panjang ; itu setara dengan strtol (s, (char **) NULL, 10) .
strtod ganda (const char * s, char ** endp)
strtod mengubah awalan s menjadi dua kali lipat , mengabaikan spasi putih terdepan; itu menyimpan a
arahkan ke sembarang sufiks yang tidak dikonversi di * endp kecuali endp adalah NULL . Jika jawabannya mau
meluap, HUGE_VAL dikembalikan dengan tanda yang tepat; jika jawabannya akan mengalir,
nol dikembalikan. Dalam kedua kasus, errno diatur ke ERANGE .
long strtol (const char * s, char ** endp, int base)
strtol mengubah awalan s menjadi panjang , mengabaikan spasi putih utama; itu menyimpan a
arahkan ke sembarang sufiks yang tidak dikonversi di * endp kecuali endp adalah NULL . Jika basis antara 2
dan 36, konversi dilakukan dengan asumsi bahwa input ditulis dalam basis itu. Jika dasar adalah
nol, dasarnya 8, 10, atau 16; leading 0 menyiratkan heksadesimal oktal dan 0x atau 0X .

Halaman 208

209
Huruf dalam kedua kasus mewakili digit dari 10 hingga basis-1 ; 0x atau 0X terkemuka adalah
diizinkan di basis 16. Jika jawabannya akan meluap, LONG_MAX atau LONG_MIN adalah
dikembalikan, tergantung pada tanda hasilnya, dan errno diatur ke ERANGE .
unsigned long strtoul (const char * s, char ** endp, int base)
strtoul sama dengan strtol kecuali bahwa hasilnya tidak ditandai lama dan kesalahan
nilainya ULONG_MAX .
int rand (batal)
rand mengembalikan bilangan bulat pseudo-acak dalam rentang 0 hingga RAND_MAX , yang setidaknya
32767.
void srand (seed int unsigned)
srand menggunakan seed sebagai seed untuk urutan nomor pseudo-acak baru. Itu
benih awal adalah 1.
void * calloc (size_t nobj, size_t size)
calloc mengembalikan pointer ke ruang untuk array objek nobj , masing-masing ukuran ukuran , atau
NULL jika permintaan tidak
dapat dipenuhi. Ruang diinisialisasi ke nol byte.
void * malloc (size_t size)
malloc mengembalikan pointer ke ruang untuk objek ukuran ukuran , atau NULL jika permintaan
tidak bisa dipuaskan. Ruang tidak diinisialisasi.
void * realloc (void * p, size_t size)
realloc mengubah ukuran objek yang ditunjuk oleh p ke ukuran . Isinya akan
tidak berubah hingga minimum ukuran lama dan baru. Jika ukuran baru lebih besar, maka
ruang baru belum diinisialisasi. realloc mengembalikan pointer ke ruang baru, atau NULL jika
permintaan tidak dapat dipenuhi, dalam hal ini * p tidak berubah.
batal gratis (batal * p)
bebas merelokasi ruang yang ditunjuk oleh p ; itu tidak apa-apa jika p adalah NULL . p harus a
penunjuk ke ruang yang sebelumnya dialokasikan oleh calloc , malloc , atau realloc .
batal dibatalkan (batal)
batal menyebabkan program berakhir secara tidak normal, seolah-olah dengan kenaikan gaji (SIGABRT) .
batal keluar (status int)
keluar menyebabkan penghentian program normal. fungsi atexit disebut dalam urutan terbalik
pendaftaran, buka file memerah, aliran terbuka ditutup, dan kontrol dikembalikan
ke lingkungan. Bagaimana status dikembalikan ke lingkungan adalah implementasi-
tergantung, tetapi nol dianggap sebagai penghentian yang berhasil. Nilai EXIT_SUCCESS dan
EXIT_FAILURE juga dapat digunakan.
int atexit (void (* fcn) (void))
atexitmendaftarkan fungsi fcn untuk dipanggil ketika program berakhir secara normal;
mengembalikan non-nol jika pendaftaran tidak dapat dilakukan.
sistem int (const char * s)
sistem melewati string s dengan lingkungan untuk eksekusi. Jika s adalah NULL , sistem
mengembalikan non-nol jika ada prosesor perintah. Jika s bukan NULL , nilai baliknya adalah
tergantung pada implementasi.
char * getenv (const char * name)
getenv mengembalikan string lingkungan yang terkait dengan nama , atau NULL jika tidak ada string.
Detail tergantung pada implementasi.

void * bsearch (const void * key, const void * base,


size_t n, ukuran size_t,
int (* cmp) (const void * keyval, const void * datum))
bsearch mencari basis [0] ... basis [n-1] untuk item yang cocok dengan tombol * . Fungsinya
cmp harus kembali negatif jika argumen pertama (kunci pencarian) kurang dari yang kedua (a
entri tabel), nol jika sama, dan positif jika lebih besar. Item dalam basis array harus dalam
urutan naik. bsearch mengembalikan pointer ke item yang cocok, atau NULL jika tidak ada.

membatalkan qsort (void * base, size_t n, size_t size,

Halaman 209

210
int (* cmp) (const void *, const void *))
qsort menyortir ke dalam urutan naik basis array [0] ... basis [n-1] objek ukuran
ukuran . Fungsi perbandingan cmp adalah seperti dalam bsearch .
int abs (int n)
abs mengembalikan nilai absolut dari argumen intnya .
lab panjang (panjang n)
labs mengembalikan nilai absolut dari argumen panjangnya .
div_t div (int num, int denom)
div menghitung hasil bagi dan sisa num / denom . Hasilnya disimpan di
int anggota quot dan rem dari struktur jenis div_t .
ldiv_t ldiv (num panjang, denom panjang)
ldiv menghitung hasil bagi dan sisa num / denom . Hasilnya disimpan di
anggota lama kuota dan rem dari struktur tipe ldiv_t .

B.6 Diagnostics: <assert.h>


The menegaskan makro digunakan untuk menambah diagnostik untuk program:
void assert (int ekspresi )

Jika ekspresi nol ketika

menegaskan ( ekspresi )

dieksekusi, menegaskan makro akan mencetak pada stderr pesan, seperti

Pernyataan gagal: ekspresi , nama file file , baris nnn

Kemudian panggilan menggugurkan untuk mengakhiri eksekusi. Nama file sumber dan nomor baris berasal dari
macro preprocessor __FILE__ dan __LINE__ .

Jika NDEBUG didefinisikan pada saat <assert.h> dimasukkan, makro pernyataan diabaikan.

B.7 Daftar Argumen Variabel: <stdarg.h>


Header <stdarg.h> menyediakan fasilitas untuk melangkah melalui daftar argumen fungsi
nomor dan jenis tidak dikenal.
Misalkan lastarg adalah parameter bernama terakhir dari fungsi f dengan nomor variabel
argumen. Kemudian menyatakan dalam f variabel jenis va_list yang akan mengarah ke setiap argumen
gantinya:

va_list ap;
ap harus diinisialisasi sekali dengan makro va_start sebelum argumen yang tidak disebutkan namanya
diakses:
va_start (va_list ap, lastarg );
Setelah itu, setiap eksekusi dari va_arg makro akan menghasilkan nilai yang memiliki tipe dan
nilai argumen tanpa nama berikutnya, dan juga akan memodifikasi ap sehingga penggunaan va_arg berikutnya kembali
argumen selanjutnya:

ketik va_arg (va_list ap, ketik );

Makro

void va_end (va_list ap);


harus dipanggil sekali setelah argumen telah diproses tetapi sebelum f keluar.

Halaman 210

211

B.8 Lompatan Non-Lokal: <setjmp.h>


Deklarasi di <setjmp.h> menyediakan cara untuk menghindari panggilan fungsi normal dan kembali
urutan, biasanya untuk memungkinkan pengembalian segera dari panggilan fungsi yang sangat bersarang.
int setjmp (jmp_buf env)
Setjmp makro menyimpan informasi status di env untuk digunakan oleh longjmp . Kembalinya adalah
nol dari panggilan langsung setjmp , dan bukan nol dari panggilan longjmp selanjutnya . SEBUAH
panggilan ke setjmp hanya dapat terjadi dalam konteks tertentu, pada dasarnya pengujian jika , beralih , dan
loop, dan hanya dalam ekspresi relasional sederhana.
if (setjmp (env) == 0)
/ * ke sini melalui telepon langsung * /
lain
/ * ke sini dengan memanggil longjmp * /
membatalkan longjmp (jmp_buf env, int val)
longjmp mengembalikan status yang disimpan oleh panggilan terbaru ke setjmp , menggunakan
informasi yang disimpan dalam env , dan eksekusi dilanjutkan seolah-olah fungsi setjmp baru saja
dieksekusi dan mengembalikan nilai val bukan nol . Fungsi yang mengandung setjmp
tidak boleh diakhiri. Objek yang dapat diakses memiliki nilai yang mereka miliki saat itu
longjmp dipanggil, kecuali bahwa variabel otomatis non- volatile dalam fungsi
panggilan setjmp menjadi tidak terdefinisi jika diubah setelah panggilan setjmp .

B.9 Sinyal: <signal.h>


Header <signal.h> menyediakan fasilitas untuk menangani kondisi luar biasa yang muncul selama
eksekusi, seperti sinyal interupsi dari sumber eksternal atau kesalahan dalam eksekusi.

void (* signal (int sig, void (* handler) (int))) (int)


sinyal menentukan bagaimana sinyal selanjutnya akan ditangani. Jika handler adalah SIG_DFL , the
perilaku default yang ditentukan implementasi digunakan, jika SIG_IGN , sinyal diabaikan;
jika tidak, fungsi yang ditunjuk oleh handler akan dipanggil, dengan argumen tipe
sinyal. Sinyal yang valid termasuk
SIGABRT penghentian abnormal, misalnya, dari aborsi
SIGFPE kesalahan aritmatika, mis., zero divide atau overflow
SIGILL gambar fungsi ilegal, misalnya, instruksi ilegal
SIGINT perhatian interaktif, misalnya interupsi
SIGSEGV akses penyimpanan ilegal, mis., akses di luar batas memori
Permintaan terminasi SIGTERM dikirim ke program ini

sinyal mengembalikan nilai handler sebelumnya untuk sinyal tertentu, atau SIG_ERR jika ada kesalahan
terjadi.

Ketika sinyal sig kemudian terjadi, sinyal dikembalikan ke perilaku standarnya; lalu
fungsi signal-handler disebut, seolah-olah oleh (* handler) (sig) . Jika pawang kembali, eksekusi
akan melanjutkan di tempat itu ketika sinyal terjadi.

Keadaan awal sinyal ditentukan oleh implementasi.

kenaikan gaji int (int sig)


menaikkan mengirim sinyal sig ke program; mengembalikan non-nol jika tidak berhasil.

B.10 Fungsi Tanggal dan Waktu: <time.h>


Header <time.h> menyatakan tipe dan fungsi untuk memanipulasi tanggal dan waktu. Beberapa
fungsi memproses waktu lokal , yang mungkin berbeda dari waktu kalender, misalnya karena waktu
Halaman 211

212
daerah. clock_t dan time_t adalah tipe aritmatika yang mewakili waktu, dan struct tm memegang
komponen waktu kalender:
int tm_sec; detik setelah menit (0,61)
int tm_min; menit setelah jam (0,59)
int tm_hour; jam sejak tengah malam (0,23)
int tm_mday; hari dalam sebulan (1,31)
int tm_mon; bulan sejak Januari (0,11)
int tm_year; tahun sejak 1900
int tm_wday; hari sejak Minggu (0,6)
int tm_yday; hari sejak 1 Januari (0,365)
int tm_isdst; Bendera Daylight Saving Time

tm_isdst positif jika Daylight Saving Time berlaku, nol jika tidak, dan negatif jika
informasi tidak tersedia.
clock_t clock (batal)
clock mengembalikan waktu prosesor yang digunakan oleh program sejak awal eksekusi,
atau -1 jika tidak tersedia. clock () / CLK_PER_SEC adalah waktu dalam detik.
time_t time (time_t * tp)
waktu mengembalikan waktu kalender saat ini atau -1 jika waktu tidak tersedia. Jika tp tidak
NULL , nilai kembali juga ditetapkan ke * tp.
double difftime (time_t time2, time_t time1)
difftime mengembalikan time2-time1 yang dinyatakan dalam detik.
time_t mktime (struct tm * tp)
mktime mengubah waktu lokal dalam struktur * tp menjadi waktu kalender dalam waktu yang sama
representasi yang digunakan oleh waktu . Komponen akan memiliki nilai dalam rentang yang ditunjukkan.
mktime mengembalikan waktu kalender atau -1 jika tidak dapat diwakili.
Empat fungsi berikutnya mengembalikan pointer ke objek statis yang mungkin ditimpa oleh panggilan lain.
char * asctime (const struct tm * tp)
asctime </ tt <mengubah waktu dalam struktur * tp menjadi string
formulir

Sun 3 Jan 15:14:13 1988 \ n \ 0


char * ctime (const time_t * tp)
ctime mengubah waktu kalender * tp ke waktu lokal; itu setara
untuk

asctime (localtime (tp))


struct tm * gmtime (const time_t * tp)
gmtime mengubah waktu kalender * tp menjadi Waktu Universal Terkoordinasi
(UTC). Ia mengembalikan NULL jika UTC tidak tersedia. Nama gmtime miliki
signifikansi historis.
struct tm * localtime (const time_t * tp)
localtime mengubah waktu kalender * tp ke waktu lokal.
strftime size_t (char * s, size_t smax, const char * fmt, const struct tm * tp)
strftime memformat informasi tanggal dan waktu dari * tp ke s sesuai
ke fmt, yang analog dengan format printf. Karakter biasa
(termasuk terminating '\ 0') disalin ke s. Setiap% c adalah
diganti seperti yang dijelaskan di bawah ini, menggunakan nilai yang sesuai untuk lokal
lingkungan Hidup. Tidak lebih dari karakter smax ditempatkan ke s. strftime
mengembalikan jumlah karakter, tidak termasuk '\ 0', atau nol jika lebih
daripada karakter smax diproduksi.
nama hari kerja disingkat.
%Sebuah
nama
%SEBUAH hari kerja penuh.
% b nama bulan disingkat.
% B nama bulan penuh.
% c representasi tanggal dan waktu lokal.
% d hari dalam sebulan ( 01-31 ).
% H jam (jam 24 jam) (00-23) .

Halaman 212

213
%SAYA jam (jam 12 jam) (01-12) .
% j hari dalam setahun (001-366) .
% m bulan (01-12) .
% M menit (00-59) .
% p setara lokal AM atau PM.
% S kedua (00-61) .
% U jumlah minggu dalam setahun (Minggu sebagai hari pertama dalam seminggu) (00-53) .
% w hari kerja ( 0-6 , hari Minggu adalah 0).
% W jumlah minggu dalam setahun (Senin sebagai hari pertama dalam seminggu) (00-53) .
% x representasi tanggal lokal.
% X representasi waktu lokal.
% y tahun tanpa abad (00-99) .
% Y tahun dengan abad.
% Z nama zona waktu, jika ada.
%% %

B.11 Batas yang ditentukan implementasi:


<Limit.h> dan <float.h>
Header <Limit.h> mendefinisikan konstanta untuk ukuran tipe integral.
Nilai-nilai di bawah ini adalah besaran minimum yang dapat diterima; nilai yang lebih besar mungkin
bekas.
CHAR_BIT 8 bit dalam char
atau
CHAR_MAX UCHAR_MAX nilai maksimum char
SCHAR_MAX
CHAR_MIN 0 atau SCHAR_MIN nilai maksimum char
INT_MAX 32767 nilai maksimum int
INT_MIN -32767 nilai minimum int
LONG_MAX 2147483647 nilai maksimum panjang
LONG_MIN -2147483647 nilai minimum panjang
SCHAR_MAX +127 nilai maksimum char yang ditandatangani
SCHAR_MIN -127 nilai minimum char yang ditandatangani
SHRT_MAX +32767 nilai maksimum pendek
SHRT_MIN -32767 nilai minimum pendek
UCHAR_MAX 255 nilai maksimum dari unsigned char
UINT_MAX 65535 nilai maksimum dari int unsigned
ULONG_MAX 4294967295 nilai maksimum dari unsigned long
nilai maksimum yang tidak ditandatangani
USHRT_MAX 65535
pendek
Nama-nama dalam tabel di bawah ini, himpunan bagian dari <float.h>, adalah konstanta yang terkait
untuk aritmatika floating-point. Ketika suatu nilai diberikan, itu mewakili
besarnya minimum untuk jumlah yang sesuai. Setiap implementasi
mendefinisikan nilai yang tepat.
FLT_RADIX 2 radix eksponen, representasi, misalnya, 2, 16
FLT_ROUNDS mode pembulatan titik-mengambang sebagai tambahan
FLT_DIG 6 digit desimal presisi
FLT_EPSILON 1E-5 bilangan terkecil x sedemikian rupa sehingga 1,0 + x! = 1,0
FLT_MANT_DIG jumlah basis FLT_RADIX dalam mantissa
FLT_MAX 1E + 37 angka titik-mengambang maksimum
FLT_MAX_EXP maksimum n sedemikian rupa sehingga n-1 dapat diwakili
FLT_RADIX
FLT_MIN 1E-37 minimum angka floating-point dinormalisasi
FLT_MIN_EXP minimum n sehingga 10 n adalah angka yang dinormalisasi
DBL_DIG 10 digit desimal presisi
DBL_EPSILON 1E-9 bilangan terkecil x sedemikian rupa sehingga 1,0 + x! = 1,0
DBL_MANT_DIG jumlah basis FLT_RADIX dalam mantissa

Halaman 213

214
DBL_MAX 1E + 37 maksimum dua angka floating-point
DBL_MAX_EXP n-1 dapat
maksimum n sedemikian rupa sehingga diwakili
FLT_RADIX
DBL_MIN 1E-37 minimum angka floating-point ganda dinormalisasi
DBL_MIN_EXP minimum n sehingga 10 n adalah angka yang dinormalisasi
Halaman 214

215

Lampiran C - Ringkasan Perubahan


Sejak penerbitan edisi pertama buku ini, definisi bahasa C telah
mengalami perubahan. Hampir semua adalah ekstensi dari bahasa asli, dan dengan hati-hati
dirancang untuk tetap kompatibel dengan praktik yang ada; beberapa ambiguitas diperbaiki dalam aslinya
deskripsi; dan beberapa mewakili modifikasi yang mengubah praktik yang ada. Banyak yang baru
fasilitas diumumkan dalam dokumen yang menyertai kompiler yang tersedia dari AT&T,
dan selanjutnya telah diadopsi oleh pemasok C compiler lainnya. Baru-baru ini, the
Komite ANSI menstandarkan bahasa yang memasukkan sebagian besar perubahan, dan juga
memperkenalkan modifikasi signifikan lainnya. Laporan mereka sebagiannya diikuti oleh beberapa orang
kompiler komersial bahkan sebelum penerbitan standar C formal.
Lampiran ini merangkum perbedaan antara bahasa yang didefinisikan oleh edisi pertama Bahasa Indonesia
buku ini, dan itu diharapkan akan didefinisikan oleh standar akhir. Itu hanya memperlakukan bahasa
itu sendiri, bukan lingkungan dan perpustakaannya; meskipun ini merupakan bagian penting dari standar,
ada sedikit untuk dibandingkan dengan, karena edisi pertama tidak berusaha untuk meresepkan
lingkungan atau perpustakaan.
â € ¢ Preprocessing lebih hati-hati didefinisikan dalam Standar daripada di edisi pertama, dan
extended: berbasis eksplisit token; ada operator baru untuk penggabungan
token ( ## ), dan pembuatan string ( # ); ada baris kontrol baru seperti #elif dan
#pragma ; deklarasi ulang makro dengan urutan token yang sama secara eksplisit diizinkan;
parameter di dalam string tidak lagi diganti. Penyambungan baris dengan \ diizinkan
di mana-mana, tidak hanya dalam string dan definisi makro. Lihat Par.A.12 .
â € ¢ Signifikansi
minimum dari semua pengidentifikasi internal meningkat menjadi 31 karakter; itu
signifikansi mandat terkecil dari pengidentifikasi dengan hubungan eksternal tetap 6 monocase
surat. (Banyak implementasi menyediakan lebih banyak.)

â € ¢ Urutan
trigraph diperkenalkan oleh ?? bolehkan representasi karakter yang kurang
beberapa set karakter. Escapes untuk # \ ^ [] {} | ~ didefinisikan, lihat Par.A.12.1 . Perhatikan itu
pengenalan trigraph dapat mengubah arti string yang mengandung
urutan ?? .
â € ¢ Kata
kunci baru ( void, const, volatile, signed, enum ) diperkenalkan. Itu
kata kunci entri lahir mati ditarik.

â € ¢ Urutanmelarikan diri baru, untuk digunakan dalam konstanta karakter dan string literal, adalah
didefinisikan. Efek mengikuti \ oleh karakter bukan bagian dari pelarian yang disetujui
urutan tidak ditentukan. Lihat Par.A.2.5.2 .
â € ¢ Perubahan sepele favorit semua orang: 8 dan 9 bukan angka oktal.
â € ¢ Standarmemperkenalkan seperangkat sufiks yang lebih besar untuk membuat tipe konstanta eksplisit: U
atau L untuk bilangan bulat, F atau L untuk mengambang. Ini juga memperbaiki aturan untuk tipe yang tidak di -iffix
konstanta ( Par.A.2.5 ).

â € ¢ Literal string yang berdekatan disatukan.

â € ¢ Ada notasi untuk string karakter lebar dan konstanta karakter; Lihat
Par.A.2.6 .

â € ¢ Karakter
serta jenis lainnya, dapat secara eksplisit dinyatakan untuk dibawa, atau tidak dibawa, a
masuk dengan menggunakan kata kunci yang ditandatangani atau tidak . Lokasi long float sebagai

Halaman 215

216
sinonim untuk ganda ditarik, tetapi panjang ganda dapat digunakan untuk menyatakan ekstra-
kuantitas mengambang presisi.
â € ¢ Untuk beberapa waktu, tipe char yang tidak ditandai telah tersedia. Standar ini memperkenalkan
menandatangani kata kunci untuk membuat signness eksplisit untuk char dan objek integral lainnya.
â € ¢ The kekosongan jenis telah tersedia di sebagian besar implementasi untuk beberapa tahun. Itu
Standar memperkenalkan penggunaan tipe void * sebagai tipe pointer generik; sebelumnya
char * memainkan peran ini. Pada saat yang sama, aturan eksplisit diberlakukan terhadap pencampuran
pointer dan integer, dan pointer dari tipe yang berbeda, tanpa menggunakan gips.

â € ¢ Standar
ini menempatkan minima eksplisit pada rentang jenis aritmatika, dan
mandat header ( <Limit.h> dan <float.h> ) memberikan karakteristik masing-masing
implementasi tertentu.
â € ¢ Enumerasi adalah hal baru sejak edisi pertama buku ini.

â € ¢ Standarmengadopsi dari C ++ gagasan kualifikasi tipe, misalnya const


( Par.A.8.2 ).
â € ¢ String tidak lagi dapat dimodifikasi, dan karenanya dapat ditempatkan dalam memori read-only.

â € ¢ `` Konversi aritmatika biasa '' diubah, pada dasarnya dari `` untuk bilangan bulat,
unsigned selalu menang; untuk titik mengambang, selalu gunakan ganda '' untuk `` mempromosikan ke
tipe terkecil yang cukup luas. '' Lihat Par.A.6.5 .
â € ¢ Operatorpenugasan lama seperti = + benar-benar hilang. Juga, operator penugasan adalah
sekarang token tunggal; pada edisi pertama, mereka berpasangan, dan bisa dipisahkan dengan warna putih
ruang.

â € ¢ Lisensikompiler untuk memperlakukan operator asosiatif matematis sebagai komputasi


asosiatif dicabut.

â € ¢ Operator + unary diperkenalkan untuk simetri dengan unary - .


â € ¢ Penunjuk ke fungsi dapat digunakan sebagai penunjuk fungsi tanpa * eksplisit
operator. Lihat Par.A.7.3.2 .
â € ¢ Struktur dapat ditetapkan, diteruskan ke fungsi, dan dikembalikan oleh fungsi.

â € ¢ Menerapkan alamat-operator ke array diizinkan, dan hasilnya adalah pointer ke


array.
â € ¢ The sizeof operator, dalam edisi pertama, menghasilkan jenis int ; selanjutnya banyak
implementasi membuatnya tidak ditandatangani . Standar membuat tipenya secara eksplisit
tergantung pada implementasi, tetapi membutuhkan tipe, size_t , untuk didefinisikan dalam standar
tajuk ( <stddef.h> ). Perubahan serupa terjadi pada tipe ( ptrdiff_t ) dari
perbedaan antara pointer. Lihat Par.A.7.4.8 dan Par.A.7.7 .

â € ¢ Alamat-operator &
mungkin tidak diterapkan pada objek yang didaftarkan register , sekalipun
implementasi memilih untuk tidak menyimpan objek dalam register.

â € ¢ Jenis
ekspresi shift adalah operan kiri; operan yang tepat tidak bisa
mempromosikan hasilnya. Lihat Par.A.7.8 .
â € ¢ Standar
melegalkan pembuatan pointer tepat di luar akhir array, dan
memungkinkan aritmatika dan hubungan di dalamnya; lihat Par.A.7.7 .
Halaman 216

217
â € ¢ Standar
ini memperkenalkan (meminjam dari C ++) gagasan prototipe fungsi
deklarasi yang memasukkan tipe-tipe parameter, dan termasuk yang eksplisit
pengakuan fungsi variadik bersama dengan cara yang disetujui untuk menghadapinya.
Lihat Pars. A.7.3.2 , A.8.6.3 , B.7 . Gaya lama masih diterima, dengan batasan.
â € ¢ Deklarasi
kosong, yang tidak memiliki deklarator dan tidak menyatakan setidaknya suatu struktur,
serikat pekerja, atau enumerasi, dilarang oleh Standar ini. Di sisi lain, deklarasi
hanya dengan struktur atau tag union redecek ulang tag itu meskipun dideklarasikan di luar
cakupan.
â € ¢ Deklarasi data eksternal tanpa penentu atau kualifikasi apa pun (hanya deklarator telanjang)
dilarang.

â € ¢ Beberapaimplementasi, ketika disajikan dengan deklarasi eksternal di blok batin,


akan mengekspor deklarasi ke seluruh file. Standar memperjelas bahwa
ruang lingkup deklarasi semacam itu hanyalah blok.
â € ¢ Cakupan parameter disuntikkan ke dalam pernyataan majemuk fungsi, sehingga
deklarasi variabel di tingkat atas fungsi tidak dapat menyembunyikan parameter.

â € ¢ Ruangnama pengidentifikasi agak berbeda. Standar menempatkan semua tag dalam a


ruang nama tunggal, dan juga memperkenalkan ruang nama terpisah untuk label; Lihat
Par.A.11.1 . Juga, nama anggota dikaitkan dengan struktur atau gabungannya
mereka adalah bagian. (Ini sudah menjadi praktik umum sejak beberapa waktu lalu.)
â € ¢ Serikat pekerja dapat diinisialisasi; initializer mengacu pada anggota pertama.
â € ¢ Struktur, serikat pekerja, dan susunan otomatis dapat diinisialisasi, meskipun dengan cara terbatas.

â € ¢ Array
karakter dengan ukuran eksplisit dapat diinisialisasi dengan string literal dengan tepat
banyak karakter ( \ 0 diam-diam keluar).

â € ¢ Ekspresi pengontrol, dan label case, dari sakelar mungkin memiliki tipe integral apa pun.

Anda mungkin juga menyukai