GNU/Linux >> Belajar Linux >  >> Linux

Apa :-!! dalam kode C?

Beberapa orang tampaknya mengacaukan makro ini dengan assert() .

Makro ini mengimplementasikan pengujian waktu kompilasi, sedangkan assert() adalah pengujian waktu proses.


Ini sebenarnya adalah cara untuk memeriksa apakah ekspresi e dapat dievaluasi menjadi 0, dan jika tidak, untuk menggagalkan pembangunan .

Makro agak salah nama; itu harus lebih seperti BUILD_BUG_OR_ZERO , bukan ...ON_ZERO . (Sesekali ada diskusi tentang apakah ini nama yang membingungkan .)

Anda harus membaca ungkapan seperti ini:

sizeof(struct { int: -!!(e); }))
  1. (e) :Menghitung ekspresi e .

  2. !!(e) :Secara logis meniadakan dua kali:0 jika e == 0; jika tidak 1 .

  3. -!!(e) :Meniadakan ekspresi secara numerik dari langkah 2:0 jika itu 0; jika tidak -1 .

  4. struct{int: -!!(0);} --> struct{int: 0;} :Jika nol, maka kami mendeklarasikan struct dengan bitfield bilangan bulat anonim yang lebarnya nol. Semuanya baik-baik saja dan kami melanjutkan seperti biasa.

  5. struct{int: -!!(1);} --> struct{int: -1;} :Sebaliknya, jika tidak nol, maka itu akan menjadi angka negatif. Mendeklarasikan bidang bit apa pun dengan negatif width adalah kesalahan kompilasi.

Jadi kita akan berakhir dengan bitfield yang memiliki lebar 0 dalam sebuah struct, yang bagus, atau bitfield dengan lebar negatif, yang merupakan kesalahan kompilasi. Lalu kita ambil sizeof bidang itu, jadi kami mendapatkan size_t dengan lebar yang sesuai (yang akan menjadi nol jika e adalah nol).

Beberapa orang bertanya:Mengapa tidak menggunakan assert saja ?

jawaban keithmo di sini mendapat tanggapan yang baik:

Makro ini mengimplementasikan pengujian waktu kompilasi, sementara assert() adalah pengujian waktu proses.

Tepat sekali. Anda tidak ingin mendeteksi masalah di kernel Anda saat runtime yang bisa ditangkap lebih awal! Ini adalah bagian penting dari sistem operasi. Sejauh mana masalah dapat dideteksi pada waktu kompilasi, jauh lebih baik.


Yah, saya cukup terkejut bahwa alternatif untuk sintaks ini belum disebutkan. Mekanisme umum lainnya (tetapi lebih lama) adalah memanggil fungsi yang tidak ditentukan dan mengandalkan pengoptimal untuk mengompilasi pemanggilan fungsi jika pernyataan Anda benar.

#define MY_COMPILETIME_ASSERT(test)              \
    do {                                         \
        extern void you_did_something_bad(void); \
        if (!(test))                             \
            you_did_something_bad(void);         \
    } while (0)

Meskipun mekanisme ini berfungsi (selama pengoptimalan diaktifkan), mekanisme ini memiliki kelemahan karena tidak melaporkan kesalahan hingga Anda menautkan, dan pada saat itu gagal menemukan definisi untuk fungsi you_did_something_bad(). Itu sebabnya pengembang kernel mulai menggunakan trik seperti lebar bidang bit berukuran negatif dan larik berukuran negatif (yang kemudian berhenti memecahkan build di GCC 4.4).

Untuk bersimpati pada kebutuhan akan pernyataan waktu kompilasi, GCC 4.3 memperkenalkan error atribut function yang memungkinkan Anda memperluas konsep lama ini, tetapi menghasilkan kesalahan waktu kompilasi dengan pesan yang Anda pilih -- tidak ada lagi pesan kesalahan "array berukuran negatif" samar!

#define MAKE_SURE_THIS_IS_FIVE(number)                          \
    do {                                                        \
        extern void this_isnt_five(void) __attribute__((error(  \
                "I asked for five and you gave me " #number))); \
        if ((number) != 5)                                      \
            this_isnt_five();                                   \
    } while (0)

Faktanya, pada Linux 3.9, kami sekarang memiliki makro yang disebut compiletime_assert yang menggunakan fitur ini dan sebagian besar makro di bug.h telah diperbarui sebagaimana mestinya. Namun, makro ini tidak dapat digunakan sebagai penginisialisasi. Namun, menggunakan ekspresi pernyataan (ekstensi C GCC lainnya), Anda bisa!

#define ANY_NUMBER_BUT_FIVE(number)                           \
    ({                                                        \
        typeof(number) n = (number);                          \
        extern void this_number_is_five(void) __attribute__(( \
                error("I told you not to give me a five!"))); \
        if (n == 5)                                           \
            this_number_is_five();                            \
        n;                                                    \
    })

Makro ini akan mengevaluasi parameternya tepat satu kali (kalau-kalau memiliki efek samping) dan membuat kesalahan waktu kompilasi yang mengatakan "Sudah kubilang jangan beri aku lima!" jika ekspresi bernilai lima atau bukan konstanta waktu kompilasi.

Jadi mengapa kita tidak menggunakan bidang bit berukuran negatif ini? Sayangnya, saat ini ada banyak batasan penggunaan ekspresi pernyataan, termasuk penggunaannya sebagai penginisialisasi konstanta (untuk konstanta enum, lebar bidang bit, dll.) bahkan jika ekspresi pernyataan benar-benar konstan dengan sendirinya (yaitu, dapat dievaluasi sepenuhnya pada waktu kompilasi dan melewati __builtin_constant_p() uji). Selanjutnya, mereka tidak dapat digunakan di luar badan fungsi.

Mudah-mudahan, GCC akan segera mengubah kekurangan ini dan mengizinkan ekspresi pernyataan konstanta digunakan sebagai inisialisasi konstanta. Tantangannya di sini adalah spesifikasi bahasa yang mendefinisikan ekspresi konstanta hukum. C ++ 11 menambahkan kata kunci constexpr hanya untuk jenis atau hal ini, tetapi tidak ada padanan di C11. Meskipun C11 mendapatkan pernyataan statis, yang akan menyelesaikan sebagian dari masalah ini, C11 tidak akan menyelesaikan semua kekurangan ini. Jadi saya harap gcc dapat membuat fungsionalitas constexpr tersedia sebagai ekstensi melalui -std=gnuc99 &-std=gnuc11 atau semacamnya dan mengizinkan penggunaannya pada ekspresi pernyataan et. al.


: adalah bidang bit. Adapun !! , itu adalah negasi ganda logis dan mengembalikan 0 untuk false atau 1 untuk benar. Dan - adalah tanda minus, yaitu negasi aritmatika.

Itu semua hanya tipuan untuk membuat kompiler muntah pada input yang tidak valid.

Pertimbangkan BUILD_BUG_ON_ZERO . Ketika -!!(e) mengevaluasi ke nilai negatif, yang menghasilkan kesalahan kompilasi. Jika tidak -!!(e) dievaluasi menjadi 0, dan bitfield dengan lebar 0 memiliki ukuran 0. Dan karenanya makro dievaluasi menjadi size_t dengan nilai 0.

Nama itu menurut saya lemah karena sebenarnya build gagal ketika inputnya tidak nol.

BUILD_BUG_ON_NULL sangat mirip, tetapi menghasilkan pointer daripada int .


Linux
  1. Apa itu Kode Keluar Bash di Linux

  2. Apa yang rentan tentang kode C ini?

  3. Apa arti EXPORT_SYMBOL dalam kode kernel Linux?

  1. Kode kesalahan apa yang dikembalikan oleh proses yang segfault?

  2. Bagaimana jika [[ $? -ne 0 ]]; berarti dalam .ksh

  3. Wordpress - Persiapan Wawancara Kerja WordPress

  1. Apa itu pengguna Linux?

  2. “e:Sub-proses /usr/bin/dpkg Mengembalikan Kode Kesalahan (1) ” Apa Artinya?

  3. Apa itu gssapi-with-mic?