GNU/Linux >> Belajar Linux >  >> Linux

Mengapa preprosesor C mengartikan kata linux sebagai konstanta 1?

Ini tampaknya merupakan "ekstensi GNU" (tidak berdokumen):[koreksi :Saya akhirnya menemukan penyebutan di dokumen. Lihat di bawah.]

Perintah berikut menggunakan -dM opsi untuk mencetak semua definisi preprocessor; karena input "file" kosong, itu menunjukkan dengan tepat makro yang telah ditentukan. Itu dijalankan dengan gcc-4.7.3 pada instalasi ubuntu standar. Anda dapat melihat bahwa preprosesor sadar standar. Total ada 243 macro dengan -std=gnu99 dan 240 dengan -std=c99; Saya memfilter output untuk relevansi.

$ cpp --std=c89 -dM < /dev/null | grep linux
#define __linux 1
#define __linux__ 1
#define __gnu_linux__ 1

$ cpp --std=gnu89 -dM < /dev/null | grep linux
#define __linux 1
#define __linux__ 1
#define __gnu_linux__ 1
#define linux 1

$ cpp --std=c99 -dM < /dev/null | grep linux
#define __linux 1
#define __linux__ 1
#define __gnu_linux__ 1

$ cpp --std=gnu99 -dM < /dev/null | grep linux
#define __linux 1
#define __linux__ 1
#define __gnu_linux__ 1
#define linux 1

Versi "standar gnu" juga #define unix . (Menggunakan c11 dan gnu11 menghasilkan hasil yang sama.)

Saya kira mereka punya alasan, tetapi menurut saya membuat instalasi default gcc (yang mengkompilasi kode C dengan -std=gnu89 kecuali ditentukan lain) tidak sesuai, dan - seperti dalam pertanyaan ini - mengejutkan. Mencemari ruang nama global dengan makro yang namanya tidak dimulai dengan garis bawah tidak diizinkan dalam implementasi yang sesuai. (6.8.10p2:"Nama makro lainnya yang telah ditentukan sebelumnya harus dimulai dengan garis bawah di depan diikuti dengan huruf besar atau garis bawah kedua," tetapi, sebagaimana disebutkan dalam Lampiran J.5 (masalah portabilitas), nama tersebut sering kali telah ditentukan sebelumnya.)

Ketika saya awalnya menulis jawaban ini, saya tidak dapat menemukan dokumentasi apa pun di gcc tentang masalah ini, tetapi akhirnya saya menemukannya, bukan dalam perilaku yang ditentukan implementasi C atau dalam ekstensi C tetapi dalam cpp manual bagian 3.7.3, yang mencatat bahwa:

Kami perlahan-lahan menghapus semua makro yang telah ditentukan sebelumnya yang berada di luar ruang nama yang dicadangkan. Anda tidak boleh menggunakannya dalam program baru…


Di Masa Lalu (pra-ANSI), simbol yang telah ditentukan sebelumnya seperti unix dan vax adalah cara untuk memungkinkan kode untuk mendeteksi pada waktu kompilasi untuk apa sistem itu dikompilasi. Tidak ada standar bahasa resmi saat itu (di luar bahan referensi di belakang edisi pertama K&R), dan kode C dengan kompleksitas apa pun biasanya berupa labirin kompleks #ifdef s untuk memungkinkan perbedaan antara sistem. Definisi makro ini umumnya diatur oleh kompiler itu sendiri, tidak ditentukan dalam file header perpustakaan. Karena tidak ada aturan nyata tentang pengidentifikasi mana yang dapat digunakan oleh implementasi dan mana yang dicadangkan untuk pemrogram, penulis kompiler merasa bebas untuk menggunakan nama sederhana seperti unix dan berasumsi bahwa pemrogram hanya akan menghindari penggunaan nama tersebut untuk tujuan mereka sendiri.

Standar ANSI C 1989 memperkenalkan aturan yang membatasi simbol apa yang dapat ditentukan sebelumnya oleh implementasi secara hukum. Makro yang ditentukan sebelumnya oleh kompiler hanya dapat memiliki nama yang diawali dengan dua garis bawah, atau dengan garis bawah diikuti dengan huruf besar, sehingga pemrogram bebas menggunakan pengidentifikasi yang tidak cocok dengan pola tersebut dan tidak digunakan di pustaka standar.

Akibatnya, setiap kompiler yang menentukan unix atau linux tidak sesuai, karena akan gagal mengkompilasi kode legal sempurna yang menggunakan sesuatu seperti int linux = 5; .

Seperti yang terjadi, gcc tidak sesuai secara default -- tetapi dapat dibuat agar sesuai (cukup baik) dengan opsi baris perintah yang tepat:

gcc -std=c90 -pedantic ... # or -std=c89 or -ansi
gcc -std=c99 -pedantic
gcc -std=c11 -pedantic

Lihat manual gcc untuk detail lebih lanjut.

gcc akan menghapus definisi ini secara bertahap dalam rilis mendatang, jadi Anda sebaiknya tidak menulis kode yang bergantung padanya. Jika program Anda perlu mengetahui apakah sedang dikompilasi untuk target Linux atau tidak, program dapat memeriksa apakah __linux__ didefinisikan (dengan asumsi Anda menggunakan gcc atau kompiler yang kompatibel dengannya). Lihat manual praprosesor GNU C untuk informasi lebih lanjut.

Sisi yang sebagian besar tidak relevan:pemenang "Best One Liner" dari Kontes Kode C Internasional yang Dikaburkan tahun 1987, oleh David Korn (ya, penulis Korn Shell) memanfaatkan unix yang telah ditentukan sebelumnya makro:

main() { printf(&unix["\021%six\012\0"],(unix)["have"]+"fun"-0x60);}

Mencetak "unix" , tetapi karena alasan yang sama sekali tidak ada hubungannya dengan ejaan nama makro.


Linux
  1. Linux – Mengapa Perlu Waktu Lama Untuk Mendeteksi Usb Stick?

  2. Linux – Mengapa Kernel Tidak Dapat Menjalankan Init?

  3. Mengapa ID Lun World Wide Dimulai dengan Angka 3 di Linux dm-multipath

  1. Bagaimana cara kerja perintah 'ls' di Linux/Unix?

  2. Apa arti &di akhir perintah linux?

  3. Mengapa Ctrl + V tidak menempel di Bash (Linux shell)?

  1. Mengapa saya beralih dari Mac ke Linux

  2. Kisah Linux Saya:Mengapa orang mengenalkan Raspberry Pi

  3. Mengapa output dari beberapa program Linux tidak menuju ke STDOUT atau STDERR?