Внутреннее представление макроса будет примерно таким, где пробелы обозначают границы токенов, а #1
и #2
- магические токены только для внутреннего использования, указывающие, где должны быть заменены параметры:
MIN( #1 , #2 ) --> ( ( #1 ) < ( #2 ) ? ( #1 ) : ( #2 ) )
- то есть препроцессор не использует имена макропараметров для внутренних целей (за исключением реализации правил переопределений).Поэтому не имеет значения, что формальные имена параметров совпадают с фактическими аргументами.
Что может вызвать проблемы, когда тело макроса использует идентификатор, который isn't формальное имя параметра, но этот идентификатор также появляется в расширении формального параметра.Например, если вы переписали свой макрос MIN
с использованием расширений GNU, которые позволяют избежать оценки аргументов дважды ...
#define MIN(x, y) ({ \
__typeof__(x) a = (x); \
__typeof__(y) b = (y); \
a < b ? a : b; \
})
, а затем вы попытались использовать его следующим образом:
int minint(int b, int a) { return MIN(b, a); }
расширение макроса будет выглядеть так:
int minint(int b, int a)
{
return ({
__typeof__(b) a = (b);
__typeof__(a) b = (a);
a < b ? a : b;
});
}
, и функция всегда будет возвращать свой первый аргумент, независимо от того, был он меньше.C не может избежать этой проблемы в общем случае, но многие люди предпочитают, чтобы всегда ставили подчеркивание в конце имени каждой локальной переменной, определенной внутри макроса, и Никогда не ставьте подчеркивания на концах любых других идентификаторов.(Сравните поведение гигиенических макросов Схемы , которые гарантированно не имеют этой проблемы. Common Lisp заставляет вас беспокоиться об этом самостоятельно, но, по крайней мере, у вас есть gensym
, чтобы помочь.)