__LINE__ 在宏中使用时的行为

Behavior of __LINE__ when used in a macro

为什么 __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__ 将 return 对齐。由于需要解析可能转义的参数,因此很难找到宏用法的 last 行而不是 first 行。

C 实现在看到结束 ) 之前不会替换 A() 宏。 ) 出现在第 8 行,所以这是宏替换发生的点。

__LINE__ 关于宏替换的细节在 C 标准中没有很好地指定。您可能不应该依赖此处的特定行为。当然,C 实现 不能 替换 A() 宏,因为它只读到第 7 行,因为它还不知道接下来会发生什么。一旦它看到结束 ),然后,当它替换宏时,它可能会认为替换标记出现在第 7 行或第 8 行或某些混合上——C 标准对此没有具体说明;在这一点上,行号在很大程度上与 C 语义无关,__LINE__ 宏在很大程度上是为了调试和其他开发工作的便利,而不是生产程序的功能(尽管它可能对它们有一些用途)。

printf 中,C 实现在看到行尾时立即识别 __LINE__ 宏。 (实际上,解析涉及更多;输入已被标记化,但效果是 __LINE__ 标记在检查 end-of-line 字符时被识别。)它在第 9 行,因此被替换通过 9。它是 printf 的参数这一事实无关紧要。 C 实现没有进程 printf 来替换第 9 行出现的 __LINE__ 标记;他们不互动。

经过我自己的几次测试,你不能依赖这里的行为,因为即使 gcc 也改变了它在不同版本中的行为。

#include <stdio.h>                                                                                                                                            
#define ShowLine() printf("__LINE__ evals to %d", __LINE__);                                                                                                  
int main()                                                                                                                                                    
{                                                                                                                                                             
    ShowLine(                                                                                                                                                
              );                                                                                                                                              
    return 0;                                                                                                                                                 
}

此代码显示

  • 5 与 gcc 11
  • 6 与 gcc 7
  • 6 声响 13