11

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 考虑第一行,并打印 4。

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

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

6.10.8 预定义的宏名称

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

(...)

6.8.10.1 强制宏

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

(...)

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

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

例如,是否有人会争辩说,只要源本身没有改变,当前源行的假定行号就应该是稳定的?

4

1 回答 1

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()。

于 2019-06-05T17:56:41.877 回答