Mengapa preprosesor C dalam GCC menafsirkan kata linux
(huruf kecil) sebagai konstanta 1
?
test.c:
#include <stdio.h>
int main(void)
{
int linux = 5;
return 0;
}
Hasil $ gcc -E test.c
(berhenti setelah tahap preprocessing):
....
int main(void)
{
int 1 = 5;
return 0;
}
Yang tentu saja menghasilkan kesalahan.
(BTW: Tidak ada #define linux
dalam stdio.h
file.)
c
linux
gcc
c-preprocessor
ahmedaly50
sumber
sumber
#undef linux
, atau mungkin menggunakan variabel yang berbeda? Saya pikir konstantalinux
digunakan untuk menguji sistem operasi misalnya jika Anda merancang aplikasi lintas-platform dan perlu tahu persis API mana yang akan digunakan (windows, mac, linux, BSD, dll). Ini bukan di stdio.h, tetapi masih didefinisikan jika kernelnya adalah linux. Kode yang sama seharusnya tidak menghasilkan kesalahan di Windows, tetapi menggunakan sesuatu seperti windows atau WINDOWS sebagai variabel mungkin akan, dan sebaliknyaJawaban:
Di Hari Tua (sebelum ANSI), simbol yang telah ditentukan sebelumnya seperti
unix
danvax
merupakan cara untuk memungkinkan kode untuk mendeteksi pada waktu kompilasi sistem apa yang sedang dikompilasi. Tidak ada standar bahasa resmi pada waktu itu (di luar bahan referensi di bagian belakang edisi pertama K&R), dan kode C kompleksitas apa pun biasanya merupakan labirin rumit#ifdef
untuk memungkinkan perbedaan antar sistem. Definisi makro ini umumnya ditetapkan oleh kompiler itu sendiri, tidak didefinisikan dalam file header perpustakaan. Karena tidak ada aturan nyata tentang pengidentifikasi yang dapat digunakan oleh implementasi dan yang disediakan untuk programmer, penulis kompiler merasa bebas untuk menggunakan nama-nama sederhana sepertiunix
dan berasumsi bahwa programmer hanya akan menghindari menggunakan nama-nama itu untuk tujuan mereka sendiri.Standar ANSI C 1989 memperkenalkan aturan yang membatasi simbol apa yang bisa ditetapkan secara hukum oleh suatu implementasi. Makro yang telah ditentukan sebelumnya oleh kompiler hanya dapat memiliki nama yang dimulai dengan dua garis bawah, atau dengan garis bawah diikuti dengan huruf besar, membuat programmer bebas untuk menggunakan pengidentifikasi yang tidak cocok dengan pola itu dan tidak digunakan di perpustakaan standar.
Akibatnya, setiap kompiler yang menentukan
unix
ataulinux
tidak sesuai, karena akan gagal untuk mengkompilasi kode legal yang menggunakan sesuatu sepertiint linux = 5;
.Seperti yang terjadi, gcc tidak sesuai secara default - tetapi dapat dibuat untuk menyesuaikan (cukup baik) dengan opsi baris perintah yang benar:
Lihat manual gcc untuk lebih jelasnya.
gcc akan menghapus definisi ini dalam rilis mendatang, jadi Anda tidak harus menulis kode yang bergantung pada mereka. Jika program Anda perlu tahu apakah sedang dikompilasi untuk target Linux atau tidak, ia dapat memeriksa apakah
__linux__
sudah ditentukan (dengan asumsi Anda menggunakan gcc atau kompiler yang kompatibel dengannya). Lihat manual preprosesor GNU C untuk informasi lebih lanjut.Samping yang sebagian besar tidak relevan: pemenang "Best One Liner" dari Kontes Kode C Internasional 1987 Disusupkan , oleh David Korn (ya, penulis Korn Shell) mengambil keuntungan dari
unix
makro yang telah ditentukan :Mencetak
"unix"
, tetapi karena alasan yang sama sekali tidak ada hubungannya dengan ejaan nama makro.sumber
unix
danvax
" - Hah? Itu pemahaman saya bahwa di Hari Tua, seluruh dunia adalah avax
!unix
definisi). Saya menjelaskan mengapa dalam komentar tentang inti itu jika Anda, atau siapa pun, ingin tahu.Tampaknya ini merupakan "ekstensi GNU" (tidak berdokumen): [ koreksi : Saya akhirnya menemukan disebutkan dalam dokumen. Lihat di bawah.]
Perintah berikut menggunakan
-dM
opsi untuk mencetak semua definisi preprocessor; karena input "file" kosong, itu menunjukkan persis makro yang telah ditentukan. Itu dijalankan dengan gcc-4.7.3 pada instalasi ubuntu standar. Anda dapat melihat bahwa preprosesor sadar standar. Secara total, ada 243 makro dengan-std=gnu99
dan 240 dengan-std=c99
; Saya memfilter output untuk relevansi.Versi "standar gnu" juga
#define unix
. (Menggunakanc11
dangnu11
menghasilkan hasil yang sama.)Saya kira mereka punya alasan, tetapi menurut saya membuat instalasi default gcc (yang mengkompilasi kode C
-std=gnu89
kecuali dinyatakan sebaliknya) tidak sesuai, dan - seperti dalam pertanyaan ini - mengejutkan. Mengotori namespace global dengan makro yang namanya tidak dimulai dengan garis bawah tidak diizinkan dalam implementasi yang sesuai. (6.8.10p2: "Setiap nama makro yang telah ditentukan sebelumnya harus dimulai dengan garis bawah terkemuka diikuti dengan huruf besar atau garis bawah kedua," tetapi, sebagaimana disebutkan dalam Lampiran J.5 (masalah portabilitas), nama-nama seperti itu sering sudah ditentukan sebelumnya.)Ketika saya awalnya menulis jawaban ini, saya tidak dapat menemukan dokumentasi dalam gcc tentang masalah ini, tetapi akhirnya saya menemukannya, bukan dalam perilaku yang ditentukan implementasi C atau dalam ekstensi C tetapi di bagian
cpp
manual 3.7.3 , di mana itu mencatat bahwa:sumber
-std=gnu89
adalah default, dan sejauh yang saya tahu itu adalah default pada solaris, linux dan mac os x ( developer.apple.com/library/mac/documentation/Darwin/Reference/… untuk yang terakhir), dan itu yang mencemari namespace.c89
tidak berusaha untuk menyesuaikan diri dengan standar, jadi jika itu standar saya tidak akan punya keluhan.Karena
linux
ini adalah makro bawaan yang didefinisikan saat kompiler sedang berjalan, atau dikompilasi untuk (jika kompilator silang), Linux.Ada banyak makro yang sudah ditentukan sebelumnya. Dengan GCC, Anda dapat menggunakan:
untuk mendapatkan daftar makro. (Saya tidak berhasil membujuk GCC untuk menerima
/dev/null
secara langsung, tetapi file kosong tersebut tampaknya berfungsi dengan baik.) Dengan GCC 4.8.1 berjalan pada Mac OS X 10.8.5, saya mendapatkan hasilnya:Itu 236 makro dari file kosong. Ketika saya menambahkan
#include <stdio.h>
ke file, jumlah makro yang didefinisikan naik hingga 505. Ini termasuk semua jenis makro pengidentifikasi platform.sumber
cpp -dM < /dev/null
linux
) tidak didefinisikan pada mesin Mac OS X saya - tidak menjalankan Linux (well, ada VM yang menjalankan Linux, tapi ...). Ini mungkin didefinisikan oleh header yang dimasukkan oleh<stdio.h>
; menjalankan file kosong mungkin tidak sama./dev/null
sebagai file sumber tanpa-x
karena tidak memiliki ekstensi, jadi gcc tidak tahu apakah itu C atau C ++. Anda dapat menggunakangcc -E -dM -x c /dev/null
ataugcc -E -dm -x c++ /dev/null
untuk mendapatkan daftar tanpa harus membuat file kosong.cpp
(preprocessor) tidakgcc
. Perhatikan juga saya tidak memberikan/dev/null
sebagai file input, melainkan saya menggunakan redirection untuk membuat/dev/null
menjadistdin
. Juga @ GerrekSB: pada mesin debian sayalinux
ada di daftar.> emptyfile.c
atau: > emptyfile.c
ataudd if=/etc/passwd of=emptyfile.c count=0
atau ...Dari
info gcc
(beri penekanan pada saya):(Ini menggunakan vax dalam contoh bukannya linux karena ketika ditulis mungkin itu lebih populer ;-).
Ide dasarnya adalah bahwa GCC hanya mencoba untuk sepenuhnya mematuhi standar ISO ketika diminta dengan
-ansi
opsi.sumber
-ansi
ataustd=cXX
, di manaXX
adalah89
,90
,99
, atau11
, dan baik-pedantic
atau-pedantic-errors
. Bahkan itu penyederhanaan yang berlebihan; lihat manual untuk detailnya.Gunakan perintah ini
untuk mendapatkan ini
sumber