Посмотрите ISO / IEC 9899: 1999, глава 6.10.3.1.
В нем говорится, что:
После аргументы для вызова функционально-подобного макроса были идентифицированы , происходит замена аргумента. Параметр в списке замены, если ему не предшествует # или ## токен предварительной обработки или после ## токена предварительной обработки (см. Ниже), заменяется соответствующим аргументом после все содержащиеся в нем макросы были расширены , Перед заменой токены предварительной обработки каждого аргумента полностью заменяются макросами , как если бы они формировали остальную часть файла предварительной обработки; другие токены предварительной обработки недоступны.
Таким образом, в va первый аргумент c имеет один токен предварительной обработки __VA_ARGS__
, который, согласно этому параграфу, должен быть заменен макросом , прежде чем заменен на c (который все еще не дает ответа относительно того, какой компилятор прав)
Но позже:
Идентификатор __VA_ARGS__
, который встречается в списке замен
должны обрабатываться так, как если бы это был параметр , а переменная
аргументы должны формировать токены предварительной обработки, используемые для
замени его.
Согласно первому фрагменту, сначала идентифицируются аргументы var . Это: 2
, 3
, printf
, “%d %d %d\n”
и 1
. Теперь замена аргумента происходит. Это означает, что параметры из списка замены var взяты и заменены. Однако во втором фрагменте указано, что идентификатор __VA_ARGS__
должен рассматриваться как параметр, поэтому его необходимо заменить на printf, “%d %d %d\n”, 1
. Теперь внутри параметров нет макросов, поэтому дальнейшая замена не происходит, что приводит к расширению var(2, 3, printf, “%d %d %d\n”, 1);
до va(printf, “%d %d %d\n”, 1, 2, 3);
. Поскольку va является макросом, он также расширяется, давая результат printf(“%d %d %d\n”, 1, 2, 3);
.
Теперь, если вы возьмете рассуждения VS 2008, как он может идентифицировать аргументы va , если один из них равен __VA_ARGS__
из var и может содержать много аргументы? Ну, он рассматривает __VA_ARGS__
как аргумент для va , что в моем определении неверно, так как, согласно первому фрагменту, замена аргумента происходит только через после аргументы для вызова имеют был идентифицирован.
Так что мне кажется, что в var препроцессор должен сначала определить аргументы для вызова макроса va , а затем начать расширение va . А это значит, что, вероятно, gcc здесь.
Также показано в C препроцессоре и конкатенации , как обрабатываются токены, заменяемые макросами по порядку, пока не будет идентификаторов, которые могут быть дополнительно расширены как макросы, или препроцессор обнаружит рекурсию и завершит расширение .