Поведение __LINE__ при использовании в макросе - PullRequest
11 голосов
/ 17 мая 2019

Почему __LINE__ оценивает по-разному в зависимости от того, используется ли он внутри функционального макроса или обычной функции?

Например:

#include<stdio.h>

#define A() printf("%d\n",__LINE__);

int main(void) {
/* 6 */  A();
/* 7 */  A(
/* 8 */    );
/* 9 */  printf("%d\n",__LINE__
/* 10 */  );
}

Я ожидаю получить:

6
7
9

Но вместо этого мы получаем (используя clang-1000.10.44.4):

6
8
9

Обратите внимание, что в функционально-подобном макросе, разбросанном по строкам 7 и 8, последняя строка занятаиспользуется, а не первый.

Документация GCC не содержит подробностей: https://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html

Почему меня это волнует?Я пишу парсер, который должен найти номера строк всех экземпляров макроса A таким образом, чтобы они соответствовали тому, что вернет __LINE__.Гораздо сложнее найти последнюю строку использования макроса, чем first из-за необходимости разбора возможно экранированных аргументов.

1 Ответ

9 голосов
/ 17 мая 2019

Реализация C не заменяет макрос A() до тех пор, пока не увидит закрывающий ). Это ) появляется в строке 8, так что это точка, в которой происходит замена макроса.

Особенности __LINE__ в отношении замены макросов не очень хорошо определены стандартом C. Вы, вероятно, не должны полагаться на конкретное поведение здесь. Конечно, реализация C не может заменить макрос A(), пока он читает только до строки 7, поскольку еще не знает, что будет. Как только он увидит закрывающий ), тогда, когда он заменяет макрос, он может считать, что замещающие токены встречаются в строке 7, в строке 8 или в некоторой смеси - стандарт C не является специфическим в этом отношении; Номера строк в значительной степени не имеют отношения к семантике C на данном этапе, и макрос __LINE__ в значительной степени удобен для отладки и других разработок, а не для производственных программ (хотя он может иметь некоторое использование для них).

В printf реализация C распознает макрос __LINE__, как только видит конец строки. (На самом деле, синтаксический анализ более сложен; входные данные были токенизированы, но эффект заключается в том, что токен __LINE__ распознается при проверке символа конца строки.) Он находится в строке 9, поэтому он заменяется на 9. То, что это аргумент printf, не имеет значения. Реализация C не имеет процесса printf для замены токена __LINE__, который появляется в строке 9; они не взаимодействуют.

...