web-dev-qa-db-ja.com

GNU CマクロenvSet(name)で、(void) ""名は何を意味しますか?

今日この構文に遭遇しましたが、それが何を意味するのか理解できませんでした:

// Uses the GNU C statement expression extension
#define envSet(name) ({ \
static int initialised; \
static bool set; \
(void) "" name; \
if (!initialised || !g_cacheEnv) { \
    const char *value = getenv(name); \
    set = value != NULL; \
    initialised = true; \
} \
set; \
})

私が理解できない特定の行は:

(void) "" name; \

誰かがこれにいくつかの光を当ててもらえますか?

65
OMGtechy

nameが文字列リテラルであり、他の型ではないことを静的に保証する方法のように見えます。

もし、するなら (void)"" "hello";の場合、これは有効なC式です。

しかし、(void)"" 1;すると、構文エラーが発生します。

76
Lundin

2つの連続する文字列リテラルが連結されます。おそらく、nameが文字列リテラルであるかどうかをチェックしています。そうでない場合、コンパイラはエラーを報告します。

(void)キャストは、「効果のないステートメント」などの警告を抑制します。

45
milleniumbug

コードを見ると、その目的は、最初に呼び出されたときにgetenvを呼び出して結果をキャッシュし、その後getenvを呼び出さなくても、キャッシュされた結果を使用することだと思います。 。 getenvを文字列リテラルと共に使用すると、その後のすべての呼び出しで同じ環境変数が要求されます。その環境変数を変更できるものがなければ、結果として同じ結果を返します。コードに、後で変更された文字列へのポインターが与えられた場合、キャッシュされた結果は新しい文字列に対して正しくない可能性が高いため、 ""トリックの目的は、それが起こらないようにすることです。

使用される可能性のあるすべての文字列リテラルには、それに関連付けられた独自の静的変数が必要になるため、示されたコードスニペットを関数にすることはできません。一方、各繰り返しに必要なコードの量は少し多く見えます。さらに、同じ変数をコードの複数の場所でテストすると、それぞれが独自の変数のセットと環境チェックコードを使用する可能性があります。

関数の使用方法によっては、呼び出されるたびに環境変数をテストする必要があるコードよりもはるかに高速になる可能性があり、事前設定なしでループ内で呼び出された関数内から使用できる場合があります。クライアントコードが「事前設定」関数と呼ばれる場合、名前のルックアップをそこで行う必要があるため、ループ内でルックアップが行われたかどうかを確認する必要がなくなります)。

7
supercat