Имеет ли значение порядок, когда #defines используют другие #defines?
В соответствии с ответом на этот вопрос, следующий код является законным:
#define three 3
#define nine three*3
int main()
{
std::cout << nine;
return 0;
}
И конечно, он компилируется и работает нормально. Однако в ответе на упомянутый вопрос также говорится, что нужно соблюдать порядок таких директив #define
, и тот, который будет использоваться в других #define
, должен быть определен перед ними. Но следующий код:
#define nine three*3
#define three 3
int main()
{
std::cout << nine;
return 0;
}
Также компилируется и работает отлично, и он печатает "9".
Является ли мой компилятор меня легко отключить, или порядок действительно не имеет значения С#defines, которые используют другие #define
s? Не будет ли компиляция неудачной в более сложном проекте?
Следует упомянуть то, что упомянутый вопрос говорит о C, а мой код - в С++. Это где (предполагаемые) различия в поведении происходят?
Ответы
Ответ 1
three
макрос должен быть определен только перед использованием макроса nine
.
Вы можете даже изменить three
перед каждым использованием nine
:
#define nine three*3
#define three 3
int main()
{
std::cout << nine; //9
#undef three
#define three 4
std::cout << nine; //12
#undef three
//no `three` macro defined here
int three = 2;
std::cout << nine; //three * 3 == 6
return 0;
}
Ответ 2
Препроцессор выполняет несколько прогонов и заканчивается только тогда, когда не найдено других вхождений всех определений. Таким образом, ваши образцы кода работают, но препроцессору требуется еще один запуск в случае второго. Вы должны быть осторожны с рекурсивными определениями. Тогда ppc никогда не выйдет.
Ответ 3
Эти шаги будут выполняться на этапе препроцессора, и все литералы будут заменены их значением. Это можно проверить, если мы скомпилируем этот параметр:
$g++ -save-temps basic.cpp -o out
Это результат после этапа препроцессора (файл basic.ii):
int main()
{
std::cout << 3*3;
return 0;
}
для вашей примерной программы. Таким образом, порядок не должен иметь значения, поскольку он является находкой и заменой, и это делается до фактической компиляции программы.
Ответ 4
На самом деле это из-за двухэтапного парсера. На первом шаге он пытается разрешить весь символ, а на втором шаге - фактическое значение.