2016-09-30 2 views
65

я наткнулся на этот синтаксис сегодня и не мог понять, что это значит:В макросе envSet (имя) GNU C, что означает (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; \ 

Может кто-нибудь, пожалуйста, пролить некоторый свет на это?

+0

Я не думаю, что это расширение ... –

+2

@EugeneSh. конкретная строка, о которой я говорил, - нет, но контекст, в котором он использовался, равен :) – OMGtechy

+2

Ну, тогда название, вероятно, немного вводит в заблуждение. –

ответ

76

Это похоже на способ статически гарантировать, что name является строковым литералом, а не каким-либо другим типом.

Если вы делаете (void)"" "hello";, то это действительное выражение C.

Но если вы сделаете что-то вроде (void)"" 1;, тогда вы получите синтаксическую ошибку.

+32

Стоит упомянуть, что добавление '(void)' заключается в том, чтобы отключить «неиспользуемое» (или другое) предупреждение. –

+9

И, возможно, стоит также упомянуть, что это использует тот факт, что смежные строковые литералы конкатенируются препроцессором, поэтому он не будет работать, если правая часть ('name') не является строковым литералом. Приведение применяется к результату конкатенации, а не к '' '' само по себе. Изменить: о, не видел другого ответа. Хех. – unwind

+1

Технически, строковое литерала конкатенации происходит _after_ препроцессора, но _before_ все остальное. Он сам получает фазу перевода (номер 6). Наиболее очевидным способом это видно, что они не объединены в выход '-E', и вы также можете наблюдать его с помощью различных макрокоманд (расширение макросов - это фаза 4). – zwol

45

Два последовательных строковых литерала объединяются. Предположительно, он проверяет, является ли name строковым литералом. Если это не так, компилятор сообщит об ошибке.

(void) литье будет подавлять предупреждения как «заявление без эффекта».

+1

спасибо, я согласился с другим, потому что он представил пример того, как PP справится с этим :) – OMGtechy

+2

@OMGtechy в общем, вы можете принимать любые ответьте, не давая оправдания. Просто говорю ... :) –

+1

Похоже, что это не работает на 'envSet (+1)'.Кроме того, можно ожидать, что 'getenv (name)' заставит компилятор проверять «имя», если не использует сумасшедшие слабые параметры проверки, что заставляет меня задаться вопросом, предназначено ли это для какой-то своеобразной среды или просто странное упражнение. – PJTraill

7

Ознакомившись с кодом, я считаю, что цель состоит в том, чтобы он вызывал getenv при первом вызове, кешировал результат, а затем после этого использовал кешированный результат без необходимости звонить getenv. Если getenv используется со строковым литералом, то все последующие вызовы будут запрашивать одну и ту же переменную среды; если ничто не может изменить эту переменную среды, они, следовательно, возвратят тот же результат. Если в коде был указан указатель на строку, которая впоследствии была изменена, результат кэширования, скорее всего, не был бы правильным для новой строки, поэтому цель трюка - убедиться, что этого не произойдет.

Поскольку каждый строковый литерал, который может быть использован, должен иметь связанную с ним статическую переменную, указанный фрагмент кода не может быть разумно преобразован в функцию. С другой стороны, количество кода, необходимого для каждого повторения, выглядит немного. Кроме того, если одна и та же переменная тестируется более чем в одном месте в коде, каждый из них может иметь свой собственный набор переменных и код проверки среды.

В зависимости от того, как эта функция будет использоваться, она может оказаться намного быстрее, чем код, который должен проверять переменную окружения каждый раз, когда он вызывается, и может использоваться из функции, вызываемой в цикле без (если клиентский код называется функцией «предварительной настройки», там должен быть проверен поиск имени, что исключает необходимость проверки внутри цикла, чтобы проверить, был ли поиск выполнен).

+0

Это действительно тот контекст, в котором он используется, и почему :) – OMGtechy

+0

Спасибо, что предоставили самый тщательный и осмысляющий ответ. Я не могу не думать, что ... 'static const char * const name_const = name;/* Требовать константу времени компиляции */const char * value = getenv (name_const); '... было бы намного лучше! Или я чего-то не хватает? – PJTraill

+0

@PJTraill: Учитывая 'char foo [16];', я думаю, что этот подход будет принимать '(const char *) foo', хотя содержимое' foo' может быть изменено произвольно между запросами. – supercat

Смежные вопросы