2014-01-07 1 views
3

Часто и часто я чувствовал, что некоторые из круглых скобок вокруг аргументов в макроопределениях были излишними. Это слишком неудобно вставлять в скобки все. Если я могу гарантировать, что аргумент не должен быть заключен в скобки, могу ли я опустить круглые скобки? Или в скобках их все рекомендуют?Когда можно опустить круглые скобки вокруг аргументов в макросах?

Я придумал этот вопрос, когда я писал:

#define SWAP_REAL(a, b, temp) do{double temp = a; a = b; b= temp;}while(0) 

Я думаю, что если аргумент выглядит как L-значение в макросе, скобки можно опустить, так как это означает, что аргумент не появится с не другой работа.

Мои рассуждения:

  1. ассоциативная приоритет символов присваивания просто выше, чем запятая х.
  2. Вы не можете путать компилятор с мыслью, что ваш аргумент является выражением с запятой в нем. Например, SWAP(a, b, b) не будет успешно интерпретировать как

    do{double temp = a, b; a, b = b; b= temp;}while(0) 
    

    , который может пройти компиляцию.

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

В следующем примере

#define ADD(a, b) (a += (b)) 

Я думаю, что аргумент a не должен быть в круглых скобках. И в этом конкретном случае не нужен аргумент b, правильно?

@JaredPar:

#include <stdio.h> 
    #define ADD(a, b) (a += (b)) 
    int main(){ 
     int a = 0, b = 1; 
     ADD(a; b, 2); 
     return 0; 
    } 

Это не может быть успешно компилируется на моей VS2010. Error C2143: syntax error : missing ')' before ';'


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

Правила макрокоманд:

  1. НЕ делать макросы с побочными эффектами!
  2. Что касается макросов без побочного эффекта, просто скопируйте все аргументы, не задумываясь!
  3. Что касается макросов с побочным эффектом, прекратите быть обсессивно! Обозначьте их все! TnT
+5

Если вам нужно использовать макрос, который не полностью скопирован в скобки, сколько времени вы потратите на выяснение, безопасно ли оно во всех случаях? –

+1

@ KeithThompson наоборот, как вы узнаете, что вы написали макрос правильно, если не знаете, что нужно вставить в скобки? – djechlin

+0

Возможно, вместо этого рассмотрим обратный вопрос - какие макросы, которые можно было бы правдоподобно написать, на самом деле будут разбиты оборонительной скобкой? – Owen

ответ

2

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

a op b где op некоторый оператор, как +, * и т.д. всегда будет связывать неправильно, если a содержит выражение низкого приоритета как 1 || 2.

примечание я нет претензии, что это безопасно в этих случаях. Есть более творческие способы разбить макросы. Кстати, не используйте макросы с побочными эффектами в первую очередь. В более общем плане, не используйте макросы как функции. (См., Например, Эффективный C++).

+0

Спасибо, djechlin. Вы дали мне подсказку, почему я столкнулся с проблемой: я инкапсулировал код с побочными эффектами в качестве макроса. Если в макросе нет назначения, просто скопируйте все переменные. Но есть случаи, когда я хочу объединить два или более присвоений вместе, и я не думаю, что они заслуживают быть функцией (слишком короткими являются они). Поэтому я объединил их в макрос. В этой ситуации, должен ли я по-прежнему сочетать их как функцию, особенно в чистом C? – Moon

3

Вот один случай, когда он делает очевидную разницу

ADD(x;y, 42) 

С это приводит скобок к ошибке компиляции, но без него приводит к коду, который компилирует.

(x;y) += 42; // With parens errors 
x;y += 42; // Without parens compiles 

Это может выглядеть как глупый пример, но это возможно объединение макросов вместе могут легко привести к странным кода выражений, как выше.

Зачем рисковать здесь? Это всего 2 символа

+0

Привет, JaredPar. Спасибо за ваш ответ, но я думаю, что ваш пример неверен. Я пробовал ваш код, и он будет выгружать ошибки во время компиляции. Сообщение об ошибке «ошибка C2143: синтаксическая ошибка: отсутствует») «до»; ». Кроме того, ADD - это просто самый простой пример. В этом случае «#define SWAP_INT (a, b) делает {int temp = a; a = b; b = temp;} while (0)", я думаю, a и b не должны быть заключены в скобки. – Moon

+0

, и это не просто случай из двух символов ... – Moon

+0

@Moon Я проверил, что если x и y определены, то код выполняется, так как мой ответ указан – JaredPar

0

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

Кроме того, я думаю, что когда макро аргументов сами передаются в качестве аргументов функции, то вы абсолютно не должны их скобок:

#define CALLOC(s, n) calloc(s, n) 

Вы можете играть дьявольскую игру, требующую такой макрос (CALLOC({3, 4})) , но вы получаете то, что заслуживаете (ошибка компиляции). Я не знаю, как вызвать этот макрос, который будет работать, если вместо макроса вы написали те же аргументы, что и прямой вызов calloc().

Однако, если вы используете аргументы в большинстве арифметических выражений, то вам нужно обернуть их в скобках:

#define MIN(x, y) (((x) < (y)) ? (x) : (y)) 

Очевидно, что если вы вызываете его с аргументами с побочными эффектами, то вы получите то, что Вы получаете. Но аргументы не будут неверно истолкованы, как они могли бы быть, если Вы писали:

#define MIN(x, y) x < y ? x : y 

, а затем вызывается как:

MIN(x = 3 * y + 1, y = 2 * x - 2); 

comment по Moon предлагает SWAP_INT макрос.

Следующий код, использующий этот макрос, компилируется при компиляции с использованием параметров по умолчанию, но не скомпилирован с набором -DWITH_PARENTHESES.

#include <stdio.h> 

#ifdef WITH_PARENTHESES 
#define SWAP_INT(a, b) do { int temp = (a); (a) = (b); (b) = temp; } while (0) 
#else 
#define SWAP_INT(a, b) do { int temp = a; a = b; b = temp; } while (0) 
#endif 

int main(void) 
{ 
    int p = 319; 
    int q = 9923; 
    int x = 23; 
    int y = 300; 

    printf("p = %8d, q = %8d, x = %8d, y = %8d\n", p, q, x, y); 
    SWAP_INT(p, q);    // OK both ways 
    SWAP_INT(x, y);    // OK both ways 
    printf("p = %8d, q = %8d, x = %8d, y = %8d\n", p, q, x, y); 
    SWAP_INT(x /= y, p *= q); // Compiles without parentheses; fails with them 
    printf("p = %8d, q = %8d, x = %8d, y = %8d\n", p, q, x, y); 
    return 0; 
} 

Выход:

p =  319, q =  9923, x =  23, y =  300 
p =  9923, q =  319, x =  300, y =  23 
p = 41150681, q =  13, x =  0, y = 3165437 

Это не является безопасным и эффективным способом замены целых чисел - макрос без скобок не является хорошей идеей.

+0

Я пробовал 'CALLOC ({3, 4})' на VS2010, и он не может передать компиляцию. Не могли бы вы дать мне еще одно объяснение? Я думал, вы имеете в виду «CALLOC ({3, 4})» можно успешно вызвать, но 'calloc ({3, 4})' can not ".Не понял ли я вас снова? ... – Moon

+0

Это подделка; он не должен компилироваться; вы получаете то, что заслуживаете - сообщение об ошибке - для вызова макроса/функции с недопустимыми аргументами. –