Оператор скобок в C. Каков эффект в следующем коде - PullRequest
4 голосов
/ 13 апреля 2010

Я играл с макросом, чтобы включить / отключить трассировки, когда вышел со следующим кодом, когда макрос отключен:

int main()
{

  ("Hello world");

}

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

Чтобы было понятнее, код:

 #ifdef TRACE

    #define trace printf("%s %d -> ",__FILE__, __LINE__);printf
 else

    #define trace
 #endif

int main()
{

  trace("Hello world");

}

Заранее спасибо.

Ответы [ 5 ]

18 голосов
/ 13 апреля 2010

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

"Hello world";

, которое оценивается как значение типа char *, и это значение игнорируется. Вы можете заключить это выражение в избыточную пару ()

("Hello world");

, который ничего не изменит.

Точно так же, как вы можете написать

(5 + 3);

в середине вашего кода и получите выражение, которое оценивается в значение 8, которое немедленно отбрасывается.

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

Предупреждение может быть раздражающим, поэтому написание выражений без эффекта, таких как

"Hello world";

не может быть хорошей идеей. Обычно компиляторы распознают приведение к void как запрос не генерировать это предупреждение

(void) "Hello world";

Так что вы можете рассмотреть возможность переопределения вашего макроса соответственно.

Конечно, используя вышеописанную технику trace, вы должны помнить, что если вы добавите что-то, что имеет побочный эффект в качестве аргумента для вашего макроса

trace("%d\n", i++);

тогда в «отключенном» виде это будет выглядеть следующим образом

("%d\n", i++);

(два подвыражения, объединенные оператором запятой в одно выражение). Побочный эффект увеличения i сохраняется в этом случае, он не отключается. Все это эквивалентно простой

i++;

Также, если вы используете вызов функции в качестве аргумента

trace(get_trace_name());

«отключенная» форма будет выглядеть как

(get_trace_name());

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

3 голосов
/ 13 апреля 2010

Работает это или нет, может зависеть от того, что именно вы передаете в качестве аргументов макроса (см. Проблему побочных эффектов, упомянутую AndreyT). В этом случае это доброкачественно. Однако следующее, вероятно, безопаснее, поскольку при обработке макроса текст не будет вставлен, а TRACE не определено:

#ifdef TRACE
    #define trace printf("%s %d -> ",__FILE__, __LINE__);printf
#else
    #define trace( ... )
#endif

при условии, что ваш компилятор поддерживает переменные макросы. Если это так, возможно, было бы лучше определить следующее:

#ifdef TRACE
    #define trace( fmt, ...) printf("%s %d -> " fmt, __FILE__, __LINE__, __VA_ARGS__ ) ;
#else
    #define trace( ... )
#endif

Обратите внимание, что отсутствие запятой между "%s %d -> " и fmt является преднамеренным и обязательным. Также обратите внимание, что fmt аргумент должен быть константой литеральной строки, чтобы произошла конкатенация литералов смежных строк - переменная любого вида может вызвать ошибку, но это плохая практика использовать переменную для спецификатора формата в любом случае.

2 голосов
/ 13 апреля 2010
("Hello world");

- это выражение, возвращающее постоянный указатель на строку. Это значение не используется.

Скобки не имеют конкретной роли, и вы можете их опустить:

"Hello world";
0 голосов
/ 13 апреля 2010

Если ваш компилятор поддерживает C99 (или вы используете gcc, у которого была эта функция ранее), вы можете использовать переменные макросы:

#ifdef TRACE
#define trace(...) printf("%s %d -> ",__FILE__, __LINE__);printf(__VA_ARGS__)
#else
#define trace(...)
#endif

Это позволяет избежать проблем с побочными эффектами в аргументах. Если у вас строгий компилятор C89, вам нужно просто избегать побочных эффектов ...

0 голосов
/ 13 апреля 2010
#ifdef TRACE
   #define trace printf("%s %d -> ",__FILE__, __LINE__);printf
#else
   #define trace
#endif

int main {
  trace("Hello world");
}

Способ работы макросов в C состоит в том, что компилятор (по существу) * делает литерал замены идентификатора.

Так что в вашем случае есть два варианта в зависимости от значения #IFDEF

trace("Hello world");

может стать

  1. printf("%s %d -> ",__FILE__, __LINE__);printf("Hello world");

или

  1. ("Hello world");

Первый вариант - это последовательность действительного кода C, который состоит из двух printf операторов. Второй вариант - это последовательность допустимого кода C, который состоит из строки (char *) внутри ненужных фигурных скобок.

...