什麼是C代碼中的“: - !!”?


Answers

:是一個位域。 至於!! ,這是邏輯雙重否定 ,因此返回0代表假或1代表真。 而-是一個負號,即算術否定。

這只是一個竅門,讓編譯器在無效輸入上陷入困境。

考慮BUILD_BUG_ON_ZERO 。 當-!!(e)評估為負值時,會產生編譯錯誤。 否則-!!(e)評估為0,0寬度位域的大小為0.因此,宏評估的值為0的size_t

在我看來這個名字很弱,因為當輸入為零時,構建實際上會失敗。

BUILD_BUG_ON_NULL非常相似,但產生一個指針而不是int

Question

我在/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); }))

什麼:-!! 做?




那麼,我很驚訝這個語法的替代方法沒有被提及。 另一個常見(但較老)的機制是調用一個未定義的函數,並且如果斷言是正確的,則依賴於優化器來編譯函數調用。

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

儘管這種機制起作用(只要啟用優化),但它有一個缺點,即在鏈接之前不報告錯誤,此時它無法找到函數you_did_something_bad()的定義。 這就是為什麼內核開發人員開始使用像負大小的位域寬度和負尺寸數組(後者在GCC 4.4中停止構建破解)的技巧。

為了對編譯時斷言的需求表示同情,GCC 4.3引入了error函數屬性 ,它允許你擴展這個舊的概念,但是會產生一個編譯時錯誤和你所選擇的消息 - 不會再隱晦的“消極尺寸陣列“錯誤信息!

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

事實上,從Linux 3.9開始,我們現在有一個名為bug.h的宏,它使用了這個特性,並且bug.h大多數宏都相應地進行了更新。 不過,這個宏不能用作初始值設定項。 但是,使用語句表達式 (另一個GCC C擴展),您可以!

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

這個宏只會對它的參數進行一次評估(如果它有副作用),並創建一個編譯時錯誤,說“我告訴過你不要給我五個!” 如果表達式評估為5或不是編譯時常量。

那麼為什麼我們不使用這個而不是負尺寸的位域呢? 唉,目前對語句表達式的使用有許多限制,包括它們作為常量初始值設定項(對於枚舉常量,位域寬度等)的使用,即使語句表達式完全是自己的常量(即可以完全評估在編譯時和否則傳遞__builtin_constant_p()測試)。 此外,它們不能在功能體外使用。

希望GCC能很快修改這些缺點,並允許常量語句表達式用作常量初始化器。 這裡面臨的挑戰是定義什麼是合法常數表達式的語言規範。 C ++ 11為這種類型或事物添加了constexpr關鍵字,但在C11中不存在對應關係。 雖然C11得到了靜態斷言,這將解決這個問題的一部分,但它不會解決所有這些缺點。 所以我希望gcc可以通過-std = gnuc99&-std = gnuc11或者其他一些方法來擴展constexpr的功能,並允許它在語句表達式中使用。 人。