是否可以保证__LINE__指令的一致性?

时间:2019-06-05 17:10:44

标签: c c-preprocessor language-lawyer predefined-macro

在某些情况下,GCC 9最近更改了__LINE__指令的行为。下面的程序说明了此更改:

#include <stdio.h>
#define expand() __LINE__
int main() {
  printf("%d\n",expand(
                ));
  return 0;
}

由于宏expand()(扩展为__LINE__)跨越多行,因此GCC最高为8.3(而Clang最高为8.0)考虑了扩展的最后一行的编号,即打印5。但是GCC 9考虑了 first 行,并显示4。

(Godbolt链接:https://godbolt.org/z/3Nk2al

C11标准对__LINE__的确切行为不是很精确,除了:

  

6.10.8预定义的宏名称

     

以下子节中列出的预定义宏的值(__FILE____LINE__除外)在整个翻译单元中保持不变。

     

(...)

     

6.8.10.1强制宏

     

以下宏名称应由实现定义:

     

(...)

     

__LINE__当前源行(一个整数常量)的假定行号(在当前源文件中)。

我认为这意味着确切的值是实现定义的,因此不能期望其值在不同的编译器版本或不同的编译器中保持不变。还是在标准的其他地方有关于这种效果的争论?

例如,有人可以说只要源本身没有变化,当前源行的假定行号就应该稳定吗?

1 个答案:

答案 0 :(得分:1)

虽然通常很难从特定指令中找到行号,即GDB试图从崩溃的某些代码中得出行号,但printf __LINE__指令作为编译器是相对简单的生成该数字作为特定位置的静态数字。

C11标准本身只是说,在宏中时,行号不应更改,即__LINE__应该反映宏扩展后在程序中的位置,而不是代码行宏位于。这样,您可以通过创建一个打印行号然后调用您的函数的宏来执行诸如提供函数被调用方的行号之类的操作。例如:

#define f(x) ({ printf("called f(x) at line=%d\n", __LINE__); f__real(x); })

对于所显示的确切行号,这取决于编译器且不属于任何标准。即您的示例可以改写为:

int main() {
  printf("%d\n", 
         __LINE__);
}

,在这种情况下,有效的响应可以是2或3。

如果您尝试动态确定行号,则通过系统回溯库提供。即Linux上的backtrace()。