Saya bertemu dengan kode makro aneh ini di /usr/include/linux/kernel.h :
/* Force a compilation error if condition is true, but also produce a
result (of value 0 and type size_t), so the expression can be used
e.g. in a structure initializer (or where-ever else comma expressions
aren't permitted). */
#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
#define BUILD_BUG_ON_NULL(e) ((void *)sizeof(struct { int:-!!(e); }))
Apa yang :-!!
harus dilakukan
c
linux
macros
linux-kernel
chmurli
sumber
sumber
sizeof
tidak "mengevaluasi" jenisnya, hanya saja bukan nilainya. Itu jenis yang tidak valid dalam kasus ini.Jawaban:
Ini, pada dasarnya, adalah cara untuk memeriksa apakah ekspresi e dapat dievaluasi menjadi 0, dan jika tidak, gagal membangun .
Makro ini agak salah nama; itu harus sesuatu yang lebih seperti
BUILD_BUG_OR_ZERO
, daripada...ON_ZERO
. (Ada diskusi sesekali tentang apakah ini nama yang membingungkan .)Anda harus membaca ungkapan seperti ini:
(e)
: Hitung ekspresie
.!!(e)
: Meniadakan secara logis dua kali:0
jikae == 0
; jika tidak1
.-!!(e)
: Secara numerik meniadakan ekspresi dari langkah 2:0
jika itu0
; jika tidak-1
.struct{int: -!!(0);} --> struct{int: 0;}
: Jika nol, maka kami mendeklarasikan struct dengan bitfield integer anonim yang memiliki lebar nol. Semuanya baik-baik saja dan kami melanjutkan seperti biasa.struct{int: -!!(1);} --> struct{int: -1;}
: Di sisi lain, jika bukan nol, maka itu akan menjadi angka negatif. Mendeklarasikan bitfield apa pun dengan lebar negatif adalah kesalahan kompilasi.Jadi kita akan berakhir dengan bitfield yang memiliki lebar 0 dalam struct, yang baik-baik saja, atau bitfield dengan lebar negatif, yang merupakan kesalahan kompilasi. Lalu kita ambil
sizeof
bidang itu, jadi kita dapatkan asize_t
dengan lebar yang sesuai (yang akan menjadi nol dalam kasus di manae
nol).Beberapa orang bertanya: Mengapa tidak menggunakan saja
assert
?jawaban keithmo sini memiliki respons yang baik:
Tepat sekali. Anda tidak ingin mendeteksi masalah di kernel Anda saat runtime yang bisa ditangkap lebih awal! Ini bagian penting dari sistem operasi. Sejauh mana masalah dapat dideteksi pada waktu kompilasi, jauh lebih baik.
sumber
static_assert
untuk tujuan terkait.!
,<
,>
,<=
,>=
,==
,!=
,&&
,||
) selalu menghasilkan 0 atau 1. ekspresi lain dapat menghasilkan hasil yang dapat digunakan sebagai kondisi, tetapi hanya nol atau non-nol; misalnya,isdigit(c)
di manac
adalah digit, dapat menghasilkan setiap non-nol nilai (yang kemudian diperlakukan sebagai benar dalam kondisi).Ini
:
adalah bitfield. Adapun!!
, itu adalah negasi ganda logis dan mengembalikan0
untuk false atau1
true. Dan itu-
adalah tanda minus, yaitu negasi aritmatika.Itu semua hanya trik untuk membuat kompiler untuk muntah pada input yang tidak valid.
Pertimbangkan
BUILD_BUG_ON_ZERO
. Ketika-!!(e)
mengevaluasi ke nilai negatif, itu menghasilkan kesalahan kompilasi. Kalau tidak-!!(e)
mengevaluasi ke 0, dan bitfield lebar 0 memiliki ukuran 0. Dan karenanya makro mengevaluasi kesize_t
dengan nilai 0.Nama saya lemah dalam pandangan saya karena build sebenarnya gagal ketika inputnya tidak nol.
BUILD_BUG_ON_NULL
sangat mirip, tetapi menghasilkan pointer daripadaint
.sumber
sizeof(struct { int:0; })
ketat sesuai?0
? Astruct
dengan hanya bitfield kosong, benar, tapi saya tidak berpikir bahwa struct dengan ukuran 0 diperbolehkan. Misalnya, jika Anda membuat array jenis itu, elemen array individual masih harus memiliki alamat yang berbeda, bukan?0
lebar, tetapi tidak jika tidak ada anggota bernama lain dalam struktur.(C99, 6.7.2.1p2) "If the struct-declaration-list contains no named members, the behavior is undefined."
Jadi misalnyasizeof (struct {int a:1; int:0;})
sangat ketat tetapisizeof(struct { int:0; })
tidak (perilaku tidak terdefinisi).Beberapa orang tampaknya membingungkan makro ini
assert()
.Makro ini menerapkan uji waktu kompilasi, sedangkan
assert()
uji runtime.sumber
Yah, saya cukup terkejut bahwa alternatif untuk sintaks ini belum disebutkan. Mekanisme umum lainnya (tetapi lebih tua) adalah untuk memanggil fungsi yang tidak didefinisikan dan mengandalkan pengoptimal untuk mengkompilasi-keluar panggilan fungsi jika pernyataan Anda benar.
Meskipun mekanisme ini berfungsi (selama optimisasi diaktifkan) ia memiliki kelemahan yaitu tidak melaporkan kesalahan hingga Anda menautkan, pada saat itu gagal menemukan definisi untuk fungsi you_did_something_bad (). Itu sebabnya pengembang kernel mulai menggunakan trik-trik seperti lebar bit-field berukuran negatif dan array berukuran negatif (yang kemudian berhenti memecah build di GCC 4.4).
Dalam simpati terhadap kebutuhan akan pernyataan waktu kompilasi, GCC 4.3 memperkenalkan
error
atribut fungsi yang memungkinkan Anda untuk memperluas konsep yang lebih lama ini, tetapi menghasilkan kesalahan waktu kompilasi dengan pesan pilihan Anda - tidak ada lagi cryptic "array berukuran negatif "pesan kesalahan!Bahkan, pada Linux 3.9, kami sekarang memiliki makro yang disebut
compiletime_assert
yang menggunakan fitur ini dan sebagian besar makrobug.h
telah diperbarui. Namun, makro ini tidak dapat digunakan sebagai penginisialisasi. Namun, menggunakan ekspresi pernyataan (ekstensi-GCC lain), Anda bisa!Makro ini akan mengevaluasi parameternya tepat sekali (kalau-kalau ada efek samping) dan membuat kesalahan waktu kompilasi yang mengatakan, "Saya bilang jangan memberi saya lima!" jika ekspresi bernilai lima atau bukan konstanta waktu kompilasi.
Jadi mengapa kita tidak menggunakan ini alih-alih bidang bit berukuran negatif? Sayangnya, saat ini ada banyak pembatasan penggunaan ekspresi pernyataan, termasuk penggunaannya sebagai inisialisasi konstan (untuk konstanta enum, lebar bidang bit, dll.) Bahkan jika ekspresi pernyataan benar-benar konstan sendiri (yaitu, dapat sepenuhnya dievaluasi pada waktu kompilasi dan dinyatakan lulus
__builtin_constant_p()
tes). Lebih lanjut, mereka tidak dapat digunakan di luar fungsi tubuh.Semoga, GCC akan segera mengubah kekurangan ini dan memungkinkan ekspresi pernyataan konstan untuk digunakan sebagai inisialisasi konstan. Tantangannya di sini adalah spesifikasi bahasa yang mendefinisikan apa yang merupakan ekspresi konstanta hukum. C ++ 11 menambahkan kata kunci constexpr untuk tipe atau hal ini saja, tetapi tidak ada padanan dalam C11. Sementara C11 memang mendapatkan pernyataan statis, yang akan menyelesaikan bagian dari masalah ini, C11 tidak akan menyelesaikan semua kekurangan ini. Jadi saya berharap bahwa gcc dapat membuat fungsionalitas constexpr tersedia sebagai ekstensi melalui -std = gnuc99 & -std = gnuc11 atau semacamnya dan memungkinkan penggunaannya pada pernyataan pernyataan et. Al.
sumber
so the expression can be used e.g. in a structure initializer (or where-ever else comma expressions aren't permitted).
" Makro mengembalikan ekspresi tipesize_t
error: bit-field ‘<anonymous>’ width not an integer constant
Yang memungkinkan hanya konstanta. Jadi, apa gunanya?Ini menciptakan
0
bitfield ukuran jika kondisinya salah, tetapi bitfield size-1
(-!!1
) jika kondisinya benar / tidak nol. Dalam kasus sebelumnya, tidak ada kesalahan dan struct diinisialisasi dengan anggota int. Dalam kasus terakhir, ada kesalahan kompilasi (dan tentu saja tidak ada-1
bitfield ukuran yang dibuat).sumber
size_t
dengan nilai 0 jika kondisinya benar.