GNU/Linux >> Znalost Linux >  >> Linux

Co je :-!! v C kódu?

Zdá se, že někteří lidé si tato makra pletou s assert() .

Tato makra implementují test v době kompilace, zatímco assert() je běhový test.


Toto je ve skutečnosti způsob, jak zkontrolovat, zda lze výraz e vyhodnotit jako 0, a pokud ne, sestavení se nezdaří .

Makro je poněkud špatně pojmenováno; mělo by to být něco jako BUILD_BUG_OR_ZERO , spíše než ...ON_ZERO . (Občas se vedly diskuse o tom, zda se nejedná o matoucí název .)

Měli byste číst výraz takto:

sizeof(struct { int: -!!(e); }))
  1. (e) :Vypočítejte výraz e .

  2. !!(e) :Logicky dvakrát negovat:0 pokud e == 0; jinak 1 .

  3. -!!(e) :Numericky negovat výraz z kroku 2:0 pokud to bylo 0; jinak -1 .

  4. struct{int: -!!(0);} --> struct{int: 0;} :Pokud by byla nula, pak deklarujeme strukturu s anonymním celočíselným bitovým polem, které má šířku nula. Vše je v pořádku a pokračujeme jako obvykle.

  5. struct{int: -!!(1);} --> struct{int: -1;} :Na druhou stranu, pokud není nula, pak to bude nějaké záporné číslo. Deklarování jakéhokoli bitového pole pomocí záporné šířka je chyba kompilace.

Buď tedy skončíme s bitovým polem, které má ve struktuře šířku 0, což je v pořádku, nebo bitovým polem se zápornou šířkou, což je chyba kompilace. Pak vezmeme sizeof toto pole, takže dostaneme size_t s příslušnou šířkou (která bude nula v případě e je nula).

Někteří lidé se ptali:Proč prostě nepoužít assert ?

Keithmova odpověď zde má dobrou odezvu:

Tato makra implementují test v době kompilace, zatímco sustain() je test za běhu.

Naprosto správně. Nechcete zjišťovat problémy ve vašem jádru za běhu, který mohl být zachycen dříve! Je to kritická součást operačního systému. V jakékoli míře lze problémy detekovat v době kompilace, tím lépe.


No, docela mě překvapuje, že nebyly zmíněny alternativy k této syntaxi. Dalším běžným (ale starším) mechanismem je volání funkce, která není definována, a spoléhání se na optimalizátor, že volání funkce zkompiluje, pokud je vaše tvrzení správné.

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

I když tento mechanismus funguje (pokud jsou povoleny optimalizace), má nevýhodu v tom, že nehlásí chybu, dokud nepropojíte odkaz, v tom okamžiku se mu nepodaří najít definici funkce you_did_something_bad(). To je důvod, proč vývojáři jádra začínají používat triky, jako jsou záporné šířky bitového pole a pole záporné velikosti (pozdější z nich přestalo narušovat sestavení v GCC 4.4).

V pochopení pro potřebu asercí v době kompilace zavedl GCC 4.3 error atribut function, který vám umožní rozšířit tento starší koncept, ale vygenerovat chybu při kompilaci se zprávou dle vašeho výběru – žádné další záhadné chybové zprávy „negative size array“!

#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)

Ve skutečnosti od Linuxu 3.9 nyní máme makro nazvané compiletime_assert který používá tuto funkci a většinu maker v bug.h byly odpovídajícím způsobem aktualizovány. Přesto toto makro nelze použít jako inicializátor. Nicméně pomocí výrazů příkazů (další GCC C-extension), můžete!

#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;                                                    \
    })

Toto makro vyhodnotí svůj parametr přesně jednou (v případě, že má vedlejší účinky) a vytvoří chybu při kompilaci, která říká "Říkal jsem ti, abys mi nedával pět!" pokud je výraz vyhodnocen jako pět nebo není konstantou v době kompilace.

Proč tedy nepoužíváme toto místo bitových polí záporné velikosti? Bohužel v současné době existuje mnoho omezení použití výrazů příkazu, včetně jejich použití jako konstantních inicializátorů (pro výčtové konstanty, šířku bitového pole atd.), i když je výraz příkazu zcela konstantní sám o sobě (tj. může být plně vyhodnocen v době kompilace a jinak předá __builtin_constant_p() test). Dále je nelze použít mimo tělo funkce.

Doufejme, že GCC tyto nedostatky brzy opraví a umožní použití konstantních výrazů příkazů jako konstantních inicializátorů. Problémem je zde jazyková specifikace definující, co je právní konstantní výraz. C++11 přidal klíčové slovo constexpr právě pro tento typ nebo věc, ale žádný protějšek v C11 neexistuje. I když C11 získal statická tvrzení, která vyřeší část tohoto problému, nevyřeší všechny tyto nedostatky. Takže doufám, že gcc dokáže zpřístupnit funkci constexpr jako rozšíření přes -std=gnuc99 &-std=gnuc11 nebo něco podobného a umožní její použití na výrazech příkazů et. al.


: je bitfield. Pokud jde o !! , což je logická dvojitá negace, a tak vrací 0 pro false nebo 1 za pravdu. A - je znaménko mínus, tj. aritmetická negace.

Všechno je to jen trik, jak přimět kompilátor k barfování o neplatných vstupech.

Zvažte BUILD_BUG_ON_ZERO . Když -!!(e) vyhodnotí se na zápornou hodnotu, což způsobí chybu kompilace. Jinak -!!(e) se vyhodnotí na 0 a bitové pole šířky 0 má velikost 0. Makro se tedy vyhodnotí jako size_t s hodnotou 0.

Název je podle mého názoru slabý, protože sestavení ve skutečnosti selže, když vstup není nula.

BUILD_BUG_ON_NULL je velmi podobný, ale dává spíše ukazatel než int .


Linux
  1. Co jsou výstupní kódy Bash v Linuxu

  2. Jak zjistit, co znamená „errno“?

  3. Co to znamená, když se řekne linuxové jádro je preemptivní?

  1. Jaký chybový kód vrací proces, který segfaults?

  2. Co se stane, když [[ $? -ne 0]]; znamená v .ksh

  3. Wordpress - Příprava na pracovní pohovor WordPress

  1. Co je uživatel Linuxu?

  2. „e:Dílčí proces /usr/bin/dpkg vrátil kód chyby (1) “ Co to znamená?

  3. Co je to 500 interní chyba serveru