Cコードで ": - !!"とは何ですか?


Answers

:はビットフィールドです。 !! つまり、 論理二重否定で 、falseの場合は0 、trueの場合は1を返します。 そして-はマイナス記号、すなわち算術否定です。

無効な入力をbarfにコンパイルするのは単なる手品です。

BUILD_BUG_ON_ZERO検討して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 compiletime_assertでは、この機能を使用するbug.hというマクロがあり、それに応じてbug.hほとんどのマクロが更新されています。 それでも、このマクロはイニシャライザとして使用できません。 しかし、by 文の式 (別の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;                                                    \
    })

このマクロは、そのパラメータを正確に1回評価し(副作用がある場合に備えて)、「私に5つを与えないように言った」というコンパイル時のエラーを作成します。 式が5に評価されるか、コンパイル時定数でない場合。

ではなぜ、負のサイズのビットフィールドの代わりにこれを使用しないのですか? あいにく、ステートメント式が完全に一定であっても、定数式(列挙型定数、ビットフィールド幅など)としての使用など、ステートメント式の使用には多くの制限があります(つまり、完全に評価できるコンパイル時には__builtin_constant_p()テストに合格します)。 また、関数本体の外部で使用することはできません。

うまくいけば、GCCはこれらの短所を直ちに修正し、一定のステートメント表現を定数イニシャライザとして使用できるようにすることを望む。 ここでの課題は、正当な定数式が何であるかを定義する言語仕様です。 C ++ 11はこの型やもののconstexprキーワードを追加しましたが、C11には対応するものはありません。 C11はこの問題の一部を解決する静的アサーションを取得しましたが、これらの欠点をすべて解決することはできませんでした。 ですから、gccが-std = gnuc99&-std = gnuc11などの拡張機能としてconstexpr機能を利用できるようにして、そのようなものをステートメント表現などで使用できるようにしたいと思います。 al。






Links