Странная проблема определения макроса - PullRequest
0 голосов
/ 16 декабря 2010

Я хочу определить макрос во время компиляции на основе значения другого макроса. Однако этот код не выполняется должным образом:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIXTEEN 16
#define TWO (SIXTEEN % 8 == 0)? (SIXTEEN / 8) : ((SIXTEEN / 8) + 1)

int main();

int main() {
    printf("max = %d\n", TWO);
    int i;
    for (i = 0; i < TWO; i++) {
        printf("%d\n", i);
    }
    return 0;
}

Это печатает:

max = 2
0
1
2
...

и продолжается до тех пор, пока не прекратится, Когда следует печатать просто:

max = 2
0
1

и выход.

Если я делаю это вместо этого, это работает:

#define TWO 2

Я подумал, что это проблема с определением макроса ... однако, если я сделаю следующее с исходным #define, похоже, он будет работать:

...
int count = TWO;
for (i = 0; i < count; i++) {
...

Кто-нибудь может объяснить, что здесь происходит?

Ответы [ 3 ]

11 голосов
/ 16 декабря 2010

Проблема в том, что токен TWO заменяется токенами, с которыми вы определили макрос, поэтому:

i < TWO

становится таким:

i < (SIXTEEN % 8 == 0)? (SIXTEEN / 8) : ((SIXTEEN / 8) + 1) 

Из-заприоритет оператора, это читается как:

(i < (SIXTEEN % 8 == 0))
    ? (SIXTEEN / 8) 
    : ((SIXTEEN / 8) + 1) 

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

#define TWO ((SIXTEEN % 8 == 0)? (SIXTEEN / 8) : ((SIXTEEN / 8) + 1))
            ^                                                       ^

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

2 голосов
/ 16 декабря 2010

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

Не использовать круглые скобки оправдано, только если символ #define 'd является одним токеном, например 5 или "hello world".

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

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

1 голос
/ 16 декабря 2010

Разверните макрос и посмотрите на цикл for после расширения макроса:

for (i = 0; i < (16 % 8 == 0)? (16 / 8) : ((16 / 8) + 1); i++)

Видите?i < (16 % 8 == 0) является условием оператора ?:.Вам нужно поставить пару скобок вокруг определения TWO.

...