Kata Pengantar
Dalam
komentar saya
, saya merujuk beberapa kali ke buku Andrew Tanenbaum tentang Desain dan Implementasi Sistem Operasi,
edisi pertamanya, dan bagaimana C diwakili di dalamnya. Dan komentar ini selalu menarik. Saya memutuskan sudah waktunya untuk menerbitkan terjemahan pengantar ini ke C. Itu masih relevan. Meskipun pasti ada yang belum mendengar tentang bahasa pemrograman
PL / 1 , dan mungkin bahkan tentang sistem operasi
Minix .
Deskripsi ini juga menarik dari sudut pandang historis dan untuk memahami sejauh mana bahasa C telah berkembang sejak kelahirannya dan industri TI secara keseluruhan.
Saya ingin segera melakukan reservasi bahwa bahasa kedua saya adalah Prancis:

Tapi ini diimbangi oleh
pengalaman pemrograman selama 46 tahun.
Jadi, mari kita mulai, giliran Andrew Tanenbaum.
Pengantar Bahasa C (hal. 350 - 362)
Bahasa pemrograman C dibuat oleh Dennis Ritchie dari AT&T Bell Laboratories sebagai bahasa pemrograman tingkat tinggi untuk mengembangkan sistem operasi UNIX. Saat ini, bahasa tersebut banyak digunakan di berbagai bidang. C sangat populer dengan pemrogram sistem karena memungkinkan Anda untuk menulis program secara sederhana dan ringkas.
Buku utama yang menggambarkan bahasa C adalah buku bahasa pemrograman C (1978) oleh Brian Kernigan dan Dennis Ritchie. Buku-buku tentang bahasa C ditulis oleh Bolon (1986), Gehani (1984), Hancock dan Krieger (1986), Harbison and Steele (1984) dan banyak lainnya.
Dalam aplikasi ini, kami akan mencoba memberikan pengantar yang cukup lengkap untuk C, sehingga mereka yang terbiasa dengan bahasa tingkat tinggi seperti Pascal, PL / 1, atau Modula 2 akan dapat memahami sebagian besar kode MINIX yang diberikan dalam buku ini. Fitur C yang tidak digunakan dalam MINIX tidak dibahas di sini. Banyak titik halus dihilangkan. Penekanannya adalah pada membaca program C, daripada menulis kode.
A.1. Dasar-dasar bahasa C
Program C terdiri dari serangkaian prosedur (sering disebut fungsi, meskipun tidak mengembalikan nilai). Prosedur ini berisi deklarasi, operator, dan elemen lain yang bersama-sama memberi tahu komputer apa yang harus dilakukan. Gambar A-1 menunjukkan prosedur kecil di mana tiga variabel integer dinyatakan dan diberi nilai. Nama prosedurnya adalah utama. Prosedur tidak memiliki parameter formal, seperti yang ditunjukkan oleh tidak adanya pengidentifikasi antara tanda kurung di belakang nama prosedur. Tubuh prosedur tertutup dalam kurung ({}). Contoh ini menunjukkan bahwa C memiliki variabel, dan bahwa variabel-variabel ini harus dideklarasikan sebelum digunakan. C juga memiliki operator, dalam contoh ini adalah operator penugasan. Semua pernyataan harus diakhiri dengan tanda titik koma (tidak seperti Pascal, yang menggunakan titik dua di antara pernyataan, bukan setelahnya).
Komentar dimulai dengan karakter “/ *” dan diakhiri dengan karakter “* /” dan dapat menjangkau beberapa baris.
main () { int i, j, k; i = 10; j = i + 015; k = j * j + 0xFF; } . Al. .
Prosedur ini mengandung tiga konstanta. Constant 10 dalam tugas pertama
ini adalah konstanta desimal biasa. Konstanta 015 adalah konstanta oktal
(sama dengan 13 dalam desimal). Konstanta oktal selalu mulai dari nol. Konstanta 0xFF adalah konstanta heksadesimal (sama dengan 255 desimal). Konstanta heksadesimal selalu dimulai dengan 0x. Ketiga jenis digunakan dalam C.
A.2. Tipe data dasar
C memiliki dua jenis data (variabel) utama: integer dan karakter, masing-masing dinyatakan sebagai int dan char. Tidak ada variabel boolean yang terpisah. Variabel int digunakan sebagai variabel boolean. Jika variabel ini berisi 0, maka itu berarti false / false, dan nilai lainnya berarti benar / benar. C juga memiliki tipe floating point, tetapi MINIX tidak menggunakannya.
Anda dapat menerapkan "kata sifat" pendek, panjang, atau tidak bertanda tangan ke tipe int yang menentukan rentang nilai (kisaran tergantung pada kompiler). Kebanyakan prosesor 8.088 menggunakan bilangan bulat 16-bit untuk int dan int pendek dan bilangan bulat 32-bit untuk int panjang. Bilangan bulat tak bertanda (unsigned int) pada prosesor 8088 memiliki rentang dari 0 hingga 65535, dan tidak dari -32768 hingga +32767, seperti halnya bilangan bulat biasa (int). Karakter membutuhkan 8 bit.
Specifier register juga diperbolehkan untuk int dan char, dan merupakan petunjuk bagi kompiler bahwa variabel yang dideklarasikan harus ditempatkan dalam register agar program bekerja lebih cepat.
Beberapa iklan ditampilkan dalam gambar. A - 2.
int i; short int z1, z2; / * */ char c; unsigned short int k; long flag_poll; register int r; . -2. .
Konversi antar tipe diperbolehkan. Misalnya operator
flag_pole = i;
diizinkan meskipun saya bertipe int dan flag_pole panjang. Dalam banyak kasus
perlu atau berguna untuk memaksa konversi antara tipe data. Untuk konversi paksa, cukup untuk memasukkan tipe target di dalam tanda kurung di depan ekspresi untuk konversi. Sebagai contoh:
( (long) i);
menginstruksikan untuk mengonversi integer i menjadi panjang sebelum meneruskannya sebagai parameter ke prosedur p, yang mengharapkan parameter panjang.
Saat mengonversi antar jenis, perhatikan tandanya.
Saat mengkonversi karakter ke integer, beberapa kompiler memperlakukan karakter sebagai ditandatangani, yaitu, dari - 128 hingga +127, sementara yang lain memperlakukannya sebagai
unsigned, yaitu dari 0 hingga 255. Dalam MINIX, ekspresi seperti
i = c & 0377;
yang mengkonversi dari (karakter) ke integer, dan kemudian melakukan logika AND
(ampersand) dengan konstanta oktal 0377. Hasilnya adalah 8 bit yang tinggi
diatur ke nol, sebenarnya memaksa c untuk dianggap sebagai nomor unsigned 8-bit, dalam kisaran 0 hingga 255.
A.3. Jenis dan petunjuk majemuk
Pada bagian ini, kita akan melihat empat cara untuk membangun tipe data yang lebih kompleks: array, struktur, serikat, dan pointer. Array adalah kumpulan / set elemen dengan tipe yang sama. Semua array di C dimulai dengan elemen 0.
Pengumuman
int a [10];
mendeklarasikan array a dengan 10 integer yang akan disimpan dalam elemen array dari [0] ke [9]. Kedua, array bisa tiga atau lebih dimensi, tetapi tidak digunakan dalam MINIX.
Struktur adalah kumpulan variabel, biasanya dari berbagai jenis. Struktur dalam C mirip dengan catatan dalam Pascal. Operator
struct {int i; char c;} s;
menyatakan s sebagai struktur yang mengandung dua anggota, integer i dan karakter c.
Untuk menetapkan anggota i dari struktur s ke 6, tulis ungkapan berikut:
si = 6;
di mana operator titik menunjukkan bahwa elemen i milik struktur s.
Serikat pekerja juga merupakan sekumpulan anggota, mirip dengan struktur, kecuali bahwa setiap saat hanya satu dari mereka yang dapat bergabung dalam serikat pekerja. Pengumuman
union {int i; char c;} u;
berarti Anda dapat memiliki integer atau karakter, tetapi tidak keduanya. Compiler harus mengalokasikan ruang yang cukup untuk menggabungkan sehingga dapat mengakomodasi elemen menggabungkan terbesar (dari sudut pandang memori yang ditempati). Serikat pekerja hanya digunakan di dua tempat di MINIX (untuk mendefinisikan pesan sebagai gabungan dari beberapa struktur yang berbeda, dan untuk mendefinisikan blok disk sebagai gabungan dari blok data, blok i-simpul, blok katalog, dll.).
Pointer digunakan untuk menyimpan alamat mesin di C. Mereka digunakan sangat, sangat sering. Tanda bintang (*) digunakan untuk menunjukkan penunjuk dalam iklan. Pengumuman
int i, *pi, a [10], *b[10], **ppi;
mendeklarasikan integer i, pointer ke integer pi, array a dari 10 elemen, array b dari 10 pointer ke integer, dan pointer ke pointer ppi ke integer.
Aturan sintaksis yang tepat untuk deklarasi kompleks yang menggabungkan array, pointer, dan tipe lainnya agak rumit. Untungnya, MINIX hanya menggunakan deklarasi sederhana.
Gambar A-3 menunjukkan deklarasi array z dari struktur tabel struct, yang masing-masing memiliki
tiga anggota, integer i, pointer cp ke karakter dan karakter c.
struct table { int i; / * */ char *cp, c; } z [20]; . - 3. .
Susunan struktur umum dalam MINIX. Lebih jauh, tabel nama dapat dideklarasikan sebagai struktur tabel struct yang dapat digunakan dalam deklarasi selanjutnya. Sebagai contoh
register struct table *p;
mendeklarasikan p sebuah pointer ke struktur tabel struct dan menyarankan untuk menyimpannya
dalam daftar. Selama eksekusi program, p dapat mengindikasikan, misalnya, z [4] atau
untuk setiap elemen lain di z, semua 20 elemen di antaranya adalah struktur tabel tipe struct.
Untuk membuat p sebuah pointer ke z [4], tulis saja
p = &z[4];
di mana ampersand sebagai operator unary (monadic) berarti "ambil alamat yang mengikutinya." Salin nilai member i ke variabel integer n
struktur yang ditunjukkan oleh p dapat dilakukan sebagai berikut:
n = p->i;
Perhatikan bahwa panah digunakan untuk mengakses anggota struktur melalui pointer. Jika kita menggunakan variabel z, maka kita harus menggunakan operator titik:
n = z [4] .i;
Perbedaannya adalah bahwa z [4] adalah struktur, dan operator titik memilih elemen
dari tipe komposit (struktur, susunan) secara langsung. Menggunakan petunjuk, kami tidak memilih peserta secara langsung. Pointer menginstruksikan Anda untuk terlebih dahulu memilih struktur dan hanya kemudian memilih anggota struktur ini.
Terkadang lebih mudah untuk memberi nama ke tipe gabungan. Sebagai contoh:
typedef unsigned short int unshort;
mendefinisikan unshort sebagai unsigned short (unsigned short integer). Sekarang unshort dapat digunakan dalam program sebagai tipe utama. Sebagai contoh
unshort ul, *u2, u3[5];
mendeklarasikan integer unsigned pendek, sebuah pointer ke integer unsigned pendek, dan
array bilangan bulat tak bertanda pendek.
A.4. Operator
Prosedur dalam C berisi deklarasi dan pernyataan. Kami sudah melihat deklarasi, jadi sekarang kami akan mempertimbangkan operator. Tujuan dari operator kondisional dan loop pada dasarnya sama dengan dalam bahasa lain. Gambar A - 4 menunjukkan beberapa contohnya. Satu-satunya hal yang perlu diperhatikan adalah kurung kurawal digunakan untuk mengelompokkan operator, dan pernyataan while memiliki dua bentuk, yang kedua mirip dengan pernyataan berulang Pascal.
C juga memiliki pernyataan for, tetapi tidak terlihat seperti pernyataan for dalam bahasa lain. Pernyataan for memiliki bentuk berikut:
for (<>; <>; <>) ;
Hal yang sama dapat diungkapkan melalui pernyataan sementara:
<> while(<>) { <>; <> }
Sebagai contoh, perhatikan pernyataan berikut:
for (i=0; i <n; i = i+l) a[i]=0;
Operator ini menetapkan elemen n pertama array a menjadi nol. Eksekusi operator dimulai dengan menetapkan i ke nol (ini dilakukan di luar loop). Kemudian operator diulangi sampai i <n, sambil melakukan penugasan dan peningkatan i. Tentu saja, alih-alih operator yang menetapkan nilai ke elemen saat ini dari array nol, mungkin ada operator gabungan (blok) yang tertutup kurung keriting.
if (x < 0) k = 3; if (x > y) { i = 2; k = j + l, } if (x + 2 <y) { j = 2; k = j - 1; } else { m = 0; } while (n > 0) { k = k + k; n = n - l; } do { / * while */ k = k + k; n = n - 1; } while (n > 0); . A-4. if while C.
C juga memiliki operator yang mirip dengan operator kasus di Pascal. Ini adalah pernyataan beralih. Contoh ditunjukkan pada Gambar A-5. Bergantung pada nilai ekspresi yang ditentukan dalam sakelar, satu atau lain pernyataan kasus dipilih.
Jika ekspresi tidak cocok dengan salah satu pernyataan kasus, maka pernyataan standar dipilih.
Jika ekspresi tidak terkait dengan pernyataan kasus dan pernyataan default tidak ada, eksekusi berlanjut dari pernyataan berikutnya setelah pernyataan beralih.
Perlu dicatat bahwa untuk keluar dari blok kasus, gunakan pernyataan istirahat. Jika tidak ada pernyataan break, blok kasus berikutnya akan dieksekusi.
switch (k) { case 10: i = 6; break; case 20: i = 2; k = 4; break; / * default* / default: j = 5; } . A-5. switch
Pernyataan break juga bertindak di dalam loop for dan while. Harus diingat bahwa jika pernyataan break berada di dalam serangkaian loop bersarang, output hanya naik satu tingkat.
Pernyataan terkait adalah pernyataan lanjut, yang tidak keluar dari loop,
tetapi menyebabkan penyelesaian iterasi saat ini dan awal iterasi berikutnya
segera. Ini pada dasarnya adalah pengembalian ke atas loop.
C memiliki prosedur yang dapat dipanggil dengan atau tanpa parameter.
Menurut Kernigan dan Ritchie (p. 121), itu tidak diperbolehkan untuk mentransfer array,
struktur atau prosedur sebagai parameter, meskipun melewati pointer ke semua ini
diizinkan. Apakah ada buku atau tidak (itu akan muncul di ingatan saya: - "Jika ada kehidupan di Mars, jika tidak ada kehidupan di Mars"), banyak kompiler C memungkinkan struktur sebagai parameter.
Nama array, jika ditulis tanpa indeks, berarti pointer ke array, yang menyederhanakan transfer pointer array. Jadi, jika a adalah nama array jenis apa pun, ia dapat diteruskan ke g dengan menulis
g();
Aturan ini hanya berlaku untuk array, aturan ini tidak berlaku untuk struktur.
Prosedur dapat mengembalikan nilai dengan menjalankan pernyataan pengembalian. Pernyataan ini dapat berisi ekspresi, yang hasilnya akan dikembalikan sebagai nilai prosedur, tetapi pemanggil dapat dengan aman mengabaikan nilai pengembalian. Jika prosedur mengembalikan nilai, maka nilai jenis ditulis sebelum nama prosedur, seperti yang ditunjukkan pada Gambar. A-6. Seperti parameter, prosedur tidak dapat mengembalikan array, struktur, atau prosedur, tetapi dapat mengembalikan pointer ke mereka. Aturan ini dirancang untuk implementasi yang lebih efisien - semua parameter dan hasil selalu sesuai dengan satu kata mesin (di mana alamat disimpan). Kompiler yang memungkinkan penggunaan struktur sebagai parameter biasanya juga memungkinkan penggunaannya sebagai nilai balik.
int sum (i, j) int i, j ; { return (i + j); } . -6. , .
C tidak memiliki I / O. bawaan Input / output diimplementasikan dengan memanggil fungsi pustaka, yang paling umum diilustrasikan di bawah ini:
printf («x=% dy = %oz = %x \n», x, y, z);
Parameter pertama adalah string karakter antara tanda kutip (pada kenyataannya, ini adalah array karakter).
Setiap karakter yang bukan persentase hanya dicetak apa adanya.
Ketika persentase terjadi, parameter berikut dicetak dalam bentuk yang ditentukan oleh surat berikut persentase:
d - mencetak sebagai bilangan bulat desimal
o - cetak sebagai integer oktal
u - mencetak sebagai bilangan bulat desimal yang tidak ditandatangani
x - cetak sebagai hex integer
s - cetak sebagai serangkaian karakter
c - cetak sebagai satu karakter
Huruf D, 0, dan X juga diperbolehkan untuk pencetakan angka panjang desimal, oktal, dan heksadesimal.
A.5. Ekspresi
Ekspresi dibuat dengan menggabungkan operan dan operator.
Operator aritmatika seperti + dan - dan operator relasional seperti <
dan> mirip dengan rekan mereka dalam bahasa lain. % Operator
menggunakan modulo. Perlu dicatat bahwa operator persamaan adalah ==, dan operator kesenjangan adalah! =. Untuk memeriksa apakah a dan b sama, Anda dapat menulis seperti ini:
if (a == b) <>;
C juga memungkinkan Anda untuk menggabungkan operator penugasan dengan operator lain
a += 4;
setara dengan rekaman
= + 4;
Operator lain juga dapat digabungkan dengan cara ini.
C memiliki operator untuk memanipulasi bit kata. Operasi shift dan bitwise logis diizinkan. Operator shift kiri dan kanan adalah <<
dan >> masing-masing. Operator Logikal Bitwise &, | dan ^, yang logis AND (AND), termasuk OR (OR) dan OR eksklusif (XOP), masing-masing. Jika saya memiliki nilai 035 (oktal), maka ekspresi i & 06 memiliki nilai 04 (oktal). Contoh lain, jika i = 7, maka
j = (i << 3) | 014;
dan dapatkan 074 untuk j.
Grup operator penting lainnya adalah operator unary, yang masing-masing hanya menerima satu operan. Sebagai operator unary, ampersand & mendapatkan alamat variabel.
Jika p adalah pointer ke integer dan i adalah integer, operator
p = &i;
menghitung alamat i dan menyimpannya dalam variabel p.
Kebalikan dari mengambil alamat adalah operator yang mengambil pointer sebagai input dan menghitung nilai di alamat itu. Jika kita baru saja menetapkan alamat i ke pointer p, maka * p memiliki arti yang sama dengan saya.
Dengan kata lain, sebagai operator unary, tanda bintang diikuti oleh pointer (atau
ekspresi memberi pointer) dan mengembalikan nilai elemen yang ditunjuknya. Jika saya memiliki nilai 6, maka operator
j = *;
akan menetapkan j angka 6.
Operator! (tanda seru adalah operator negasi) mengembalikan 0 jika operandnya bukan nol, dan 1 jika operatornya adalah 0.
Ini terutama digunakan dalam pernyataan if, misalnya
if (!x) k=0;
memeriksa nilai x. Jika x adalah nol (salah), maka k diberi nilai 0. Sebenarnya, operator! membatalkan kondisi yang mengikutinya, sama seperti operator yang tidak ada di Pascal.
Operator ~ adalah operator komplemen bitwise. Setiap 0 di operan
menjadi 1, dan setiap 1 menjadi 0.
Ukuran operator melaporkan ukuran operan dalam byte. Sehubungan dengan
array 20 integer pada komputer dengan integer 2 byte, misalnya sizeof akan memiliki nilai 40.
Kelompok operator terakhir adalah operator kenaikan dan penurunan.
Operator
++;
berarti peningkatan p. Berapa banyak p akan meningkat tergantung pada jenisnya.
Integer atau karakter bertambah 1, tetapi pointer bertambah oleh
ukuran objek yang ditunjukkan dengan cara ini, jika a adalah susunan struktur, dan p adalah penunjuk ke salah satu dari struktur ini, dan kami menulis
p = &a[3];
untuk membuat p point ke salah satu struktur dalam array, lalu setelah meningkatkan p
akan menunjuk ke [4] tidak peduli seberapa besar strukturnya. Operator
p--;
mirip dengan operator p ++, kecuali ia menurun daripada meningkatkan nilai operan.
Dalam pernyataan
n = k++;
di mana kedua variabel adalah bilangan bulat, nilai asli k ditugaskan untuk n dan
barulah k meningkat. Dalam pernyataan
n = ++ k;
k bertambah pertama, kemudian nilai barunya disimpan dalam n.
Dengan demikian, operator ++ (atau -) dapat ditulis sebelum atau setelah operandnya, yang menghasilkan berbagai nilai.
Pernyataan terakhir adalah ini? (tanda tanya) yang memilih satu dari dua alternatif
dipisahkan oleh titik dua. Misalnya, seorang operator,
i = (x < y ? 6 : k + 1);
bandingkan x dengan y. Jika x kurang dari y, maka saya mendapat nilai 6; jika tidak, variabel i mendapat nilai k + 1. Tanda kurung adalah opsional.
A.6. Struktur program
Program C terdiri dari satu atau lebih file yang berisi prosedur dan deklarasi.
File-file ini dapat dikompilasi secara individual menjadi file objek, yang kemudian dihubungkan satu sama lain (menggunakan tautan) untuk membentuk program yang dapat dieksekusi.
Tidak seperti Pascal, deklarasi prosedur tidak dapat disarangkan, oleh karena itu semuanya ditulis pada "tingkat atas" dalam file program.
Itu diperbolehkan untuk mendeklarasikan variabel di luar prosedur, misalnya, di awal file sebelum deklarasi pertama prosedur. Variabel ini bersifat global, dan dapat digunakan dalam prosedur apa pun di seluruh program, kecuali kata kunci statis mendahului deklarasi. Dalam hal ini, variabel-variabel ini tidak dapat digunakan di file lain. Aturan yang sama berlaku untuk prosedur. Variabel yang dideklarasikan di dalam prosedur adalah lokal untuk prosedur.
Prosedur dapat mengakses variabel integer v yang dideklarasikan dalam file lain (asalkan variabel tersebut tidak statis), menyatakannya eksternal:
extern int v;
Setiap variabel global harus dinyatakan tepat sekali tanpa atribut eksternal untuk mengalokasikan memori untuknya.
Variabel dapat diinisialisasi ketika dideklarasikan:
int size = 100;
Susunan dan struktur juga dapat diinisialisasi. Variabel global yang tidak diinisialisasi secara eksplisit menerima nilai default nol.
A.7. C preprocessor
Sebelum file sumber ditransfer ke kompiler C, itu diproses secara otomatis
sebuah program yang disebut preprocessor. Ini adalah output dari preprocessor, bukan
Program asli diumpankan ke input dari kompiler. Performa preprosesor
Tiga konversi dasar dalam file sebelum meneruskannya ke kompiler:
1. Penyertaan file.
2. Definisi dan penggantian makro.
3. Kompilasi bersyarat.
Semua arahan preprosesor dimulai dengan tanda nomor (#) di kolom 1.
Saat melihat arahan
#include "prog.h"
dipenuhi oleh preprocessor, itu termasuk file prog.h, baris demi baris, dalam
program untuk diteruskan ke kompiler. Ketika arahan #include ditulis sebagai
#include <prog.h>
kemudian file yang disertakan dicari di direktori / usr / include bukan direktori kerja. Ini adalah praktik umum dalam C untuk mengelompokkan deklarasi yang digunakan oleh beberapa file dalam file header (biasanya dengan akhiran .h), dan memasukkannya jika perlu.
. Sebagai contoh
#define BLOCK_SIZE 1024
BLOCK_SIZE 1024.
10 «BLOCK_SIZE»
4- «1024» , . . , .
— . MINIX
, 8088, . :
#ifdef i8088 < 8088> #endif
i8088 , #ifdef i8088 #endif ; .
cc -c -Di8088 prog.c
#define i8088
i8088, 8088 . MINIX 68000s , .
, , . A-7 (a). prog.h, :
int x; #define MAXAELEMENTS 100
,
cc -E -Di8088 prog.c
, , , . A-7 (b).
, , C .
, , #.
cc -c -Dm68000 prog.c
. :
cc -c prog.c
. ( , , -Dflags.)
.8.
, C, . :
while (n--) *p++ = *q++;
p q , n . n- , q, , . , 0, , .
:
for (i = 0; i < N; i++) a[i] = 0;
N 0. :
for (p = &a[0]; p < &a[N]; p++) *p = 0;
p , . , p N- . , , .
. Sebagai contoh
if (a = f (x)) < >;
f, a
, , () (). , . Operator
if (a = b) < >;
b a, a, .
if (a == b) < >;
, .
Kata penutup
. , , . . , .