Pertanyaan Mengapa spesifikasi bahasa C yang lebih tua memerlukan variabel fungsi-lokal untuk dideklarasikan di muka?


Dalam bahasa pemrograman C, semua revisi bahasa saya telah bekerja dengan deklarasi variabel front-up yang ditegakkan sebelum ekspresi non-deklaratif / assignatif akan dievaluasi. C ++ tampaknya telah membebaskan persyaratan ini dari semua versi. Saya juga mengakui versi yang lebih modern dari C telah menghapus persyaratan ini juga, tetapi saya belum menggunakan salah satu standar tersebut.

Pertanyaan yang saya miliki adalah ini: Apa alasan historis yang ada untuk mencegah bahasa C dari menyatakan bebas on-demand daripada di depan?

Tentunya ada sejumlah alasan yang muncul dalam pikiran dari sudut pandang teknik, tetapi tidak satupun dari mereka tampak sangat masuk akal bagi saya.

  1. Mencegah kesalahan perilaku kompilator yang tidak jelas terjadi (seperti loop penguraian tanpa batas, peningkatan memori besar-besaran untuk evaluasi, atau beberapa kasus sudut aneh dengan Macro.)
  2. Mencegah hasil kompiler yang tidak diinginkan. Ini bisa berupa apa saja dari output simbol yang membingungkan proses debug dan kemudahan pengembangan alat debugging, ke pesanan penyimpanan tumpukan yang tidak terduga.
  3. Keterbacaan. Saya menemukan ini sulit untuk menelan juga, mengingat C, sementara dirancang untuk keterbacaan dibandingkan dengan bahasa lain pada zaman itu, tidak memaksakan jenis struktur ini hampir di tempat lain. (Kecuali Anda melihat prototipe sebagai penegakan yang serupa, tetapi jika saya ingat prototipe ditambahkan dalam '89 spec.)
  4. Kompleksitas implementasi dan alasan praktis. Ini adalah hal yang paling ingin saya percayai. Sebagai insinyur, kami harus membuat pertimbangan tertentu untuk mengirim produk yang layak dalam kerangka waktu yang ditentukan. Meskipun saya akan mengakui bahwa lanskap profesional untuk Ilmu Komputer dan Rekayasa Perangkat Lunak telah berubah secara dramatis, Bisnis tetaplah bisnis. Pada akhirnya, saya yakin Bell menginginkan produk akhir yang dapat digunakan dalam lingkungan pemrograman Unix untuk menunjukkan apa yang telah mereka capai.

Adakah yang punya sumber yang bagus untuk mendukung hal-hal di atas? Apakah saya melewatkan sesuatu sepenuhnya? Kita bisa berspekulasi dari fajar hingga senja, tapi saya mencari referensi yang baik dan keras.


32
2018-01-14 18:42


asal


Jawaban:


Melihat awal (edisi ke-6 Unix, 1975) C manual dari halaman rumah Dennis Ritchie, dalam variabel fungsi versi-lokal bisa hanya dideklarasikan pada awal fungsi:

Pernyataan fungsi hanyalah pernyataan majemuk yang mungkin memiliki deklarasi di awal.

pernyataan fungsi: { daftar deklarasimemilih  daftar pernyataan }

daftar deklarasi tidak didefinisikan (kelalaian), tetapi dapat dengan mudah diasumsikan memiliki tata bahasa:

daftar deklarasi: pernyataan  daftar deklarasimemilih.

Tidak ada pernyataan majemuk lain yang diizinkan mengandung deklarasi variabel (atau memang ada).

Ini jelas menyederhanakan implementasi; dalam kode sumber kompiler awal c02.c fungsi header fungsi blkhed()hanya perlu menjumlahkan ruang stack yang digunakan oleh auto deklarasi variabel, pada saat yang sama merekam offset tumpukan mereka, dan memancarkan kode untuk menabrak penunjuk tumpukan dengan jumlah yang tepat. Pada fungsi keluar (oleh return atau jatuh dari akhir) implementasi hanya perlu mengembalikan penunjuk tumpukan yang disimpan.

Fakta bahwa K & R merasa perlu untuk menyatakan bahwa "deklarasi variabel (termasuk inisialisasi) dapat mengikuti penjepit kiri yang memperkenalkan pernyataan majemuk, bukan hanya yang memulai fungsi"Adalah petunjuk bahwa pada saat itu itu adalah fitur yang relatif baru. Ini juga menunjukkan bahwa sintaks deklarasi-inisialisasi gabungan juga merupakan fitur terbaru, dan memang pada deklarator manual tahun 1975 tidak dapat memiliki initializers.

Manual tahun 1975 di bagian 11.1 secara khusus menyatakan bahwa:

C bukan bahasa blok-terstruktur; ini cukup dianggap sebagai cacat.

Pernyataan blok dan deklarasi yang diinisialisasi (K & R) mengatasi cacat itu, dan deklarasi campuran dan kode (C99) adalah kelanjutan logis.


17
2018-01-14 20:11



Di C89, definisi variabel diperlukan pada awal blok. (Lihat standar C untuk definisi blok) Ini adalah sejauh yang saya ketahui untuk menyederhanakan cara variabel ditangani dalam assembler. Misalnya, mari kita lihat fungsi sederhana:

void foo()
{
    int i = 5;
    printf("%i\n", i);
}

ketika gcc menerjemahkan fungsi ini ke dalam kode assembler, panggilan ke foo () akan mendidih ke sekelompok instruksi, termasuk mengatur stackframe untuk lingkup fungsi. stackframe ini mencakup ruang untuk variabel yang didefinisikan dalam ruang lingkup fungsi, dan untuk mencocokkan ruang lingkup yang sama dalam bahasa tingkat C yang lebih tinggi, mereka harus didefinisikan pada awal blok.

Pada akhirnya, ini adalah tentang kemudahan implementasi, dan juga efisiensi, karena mendeklarasikan sekelompok variabel sekaligus, bahwa itu, pada awal blok, memungkinkan compiler untuk mendorong massal mereka di stack, dan sekitar ~ 89 itu juga pertimbangan kinerja.

Tentu saja, jawaban ini sangat disederhanakan dan hanya bertujuan untuk memberikan gambaran singkat tentang mengapa hal ini dilakukan dengan cara yang dilakukan. Untuk Informasi lebih lanjut, Anda mungkin harus membaca beberapa rancangan standar C89 awal.


10
2018-01-14 19:43



Jawaban singkat yang tidak terlalu banyak menjawab: Bahasa C pada awalnya mewarisi pembatasan pesanan deklarasi ini dari pendahulunya: Bahasa B. Mengapa itu dilakukan seperti itu dalam bahasa B saya, sayangnya, tidak tahu.

Perhatikan juga bahwa dalam C yang baru lahir (dijelaskan dalam "C Reference Manual") adalah ilegal untuk menginisialisasi variabel (bahkan yang lokal) dengan ekspresi tidak konstan.

int a 5;
int b a; /* ERROR in nascent versions of C */

(catatan samping: dalam sintaks inisialisasi CRM tidak termasuk = karakter). Dalam kasus umum ini secara efektif meniadakan manfaat utama dari deklarasi variabel dalam-kode: kemampuan untuk menentukan nilai run-time yang bermakna sebagai penginisialisasi. Bahkan dalam C89 / 90 yang jauh lebih modern, pembatasan ini masih secara formal diterapkan pada inisialisasi agregat (meskipun sebagian besar kompiler mengabaikannya)

int a = 5, b = a;
struct { int x, y; } s = { a, b }; /* ERRROR even in C89/90 */ 

Hanya di C99 menjadi mungkin untuk menggunakan nilai run-time untuk semua jenis inisialisasi lokal. Ini akhirnya membuka kunci kekuatan penuh deklarasi variabel di-kode, sehingga sangat logis C99 yang memperkenalkannya.


9
2018-01-14 19:50