为了调试目的,我可以在 C/C++ 编译器中获取行号吗? (某些编译器的标准方式或特定方式)
例如
if(!Logical)
printf("Not logical value at line number %d \n",LineNumber);
// How to get LineNumber without writing it by my hand?(dynamic compilation)
您应该使用预处理器宏 __LINE__
和 __FILE__
。它们是预定义的宏,是 C/C++ 标准的一部分。在预处理期间,它们分别被一个常量字符串替换,该字符串包含一个表示当前行号的整数和当前文件名。
其他预处理器变量:
__func__ : 函数名(这是 C99 的一部分,并非所有 C++ 编译器都支持它)
__DATE__ :形式为“Mmm dd yyyy”的字符串
__TIME__ :形式为“hh:mm:ss”的字符串
您的代码将是:
if(!Logical)
printf("Not logical value at line number %d in file %s\n", __LINE__, __FILE__);
作为 C++ 标准的一部分,您可以使用一些预定义的宏。 C++ 标准的第 16.8 节定义了 __LINE__
宏。
__LINE__:当前源行的行号(十进制常量)。 __FILE__:源文件的假定名称(字符串文字)。 __DATE__:源文件的翻译日期(字符串文字...) __TIME__:源文件的翻译时间(字符串文字...) __STDC__:__STDC__ 是否已预定义 __cplusplus:名称__cplusplus 已定义编译 C++ 翻译单元时的值 199711L
所以你的代码是:
if(!Logical)
printf("Not logical value at line number %d \n",__LINE__);
您可以使用与 printf() 具有相同行为的宏,除了它还包含调试信息,例如函数名称、类和行号:
#include <cstdio> //needed for printf
#define print(a, args...) printf("%s(%s:%d) " a, __func__,__FILE__, __LINE__, ##args)
#define println(a, args...) print(a "\n", ##args)
这些宏的行为应该与 printf() 相同,同时包含类似 java stacktrace 的信息。这是一个主要的示例:
void exampleMethod() {
println("printf() syntax: string = %s, int = %d", "foobar", 42);
}
int main(int argc, char** argv) {
print("Before exampleMethod()...\n");
exampleMethod();
println("Success!");
}
这导致以下输出:
main(main.cpp:11) 之前 exampleMethod()... exampleMethod(main.cpp:7) printf() 语法: string = foobar, int = 42 main(main.cpp:13) 成功!
#include
更改为 <stdio.h>
printf
不是 async-signal-safe。所以这个宏不能在信号处理程序中使用。
C++20 提供了一种通过使用 std::source_location 来实现此目的的新方法。这目前可以在 gcc 中以 std::experimental::source_location
和 #include <experimental/source_location>
形式访问。
像 __LINE__
这样的宏的问题是,如果你想创建一个日志函数,输出当前行号和一条消息,你总是必须将 __LINE__
作为函数参数传递,因为它在呼叫站点。像这样的东西:
void log(const std::string msg) {
std::cout << __LINE__ << " " << msg << std::endl;
}
将始终输出函数声明的行,而不是实际调用 log
的行。另一方面,使用 std::source_location
您可以编写如下内容:
#include <experimental/source_location>
using std::experimental::source_location;
void log(const std::string msg, const source_location loc = source_location::current())
{
std::cout << loc.line() << " " << msg << std::endl;
}
在这里,loc
被初始化为指向调用 log
的位置的行号。 You can try it online here.
使用 __LINE__
(即双下划线 LINE 双下划线),预处理器会将其替换为遇到的行号。
签出 __FILE__
和 __LINE__
宏
试试 __FILE__
和 __LINE__
。
您可能还会发现 __DATE__
和 __TIME__
很有用。
虽然除非您必须在客户端调试程序并因此需要记录这些信息,否则您应该正常使用调试。
raw code
(例如:`__`)。 @mmyers 试图提供帮助,但他只转义了一个下划线,因此您只剩下 italics 的标记语法。不过,我同意这里的投票有点苛刻。
对于那些可能需要它的人,一个“FILE_LINE”宏可以轻松打印文件和行:
#define STRINGIZING(x) #x
#define STR(x) STRINGIZING(x)
#define FILE_LINE __FILE__ ":" STR(__LINE__)
由于我现在也面临这个问题,并且我无法为提出的不同但有效的问题 here 添加答案,因此我将为以下问题提供一个示例解决方案:仅获取函数所在位置的行号使用模板在 C++ 中调用。
背景:在 C++ 中,可以使用非类型整数值作为模板参数。这与将数据类型用作模板参数的典型用法不同。所以想法是使用这样的整数值进行函数调用。
#include <iostream>
class Test{
public:
template<unsigned int L>
int test(){
std::cout << "the function has been called at line number: " << L << std::endl;
return 0;
}
int test(){ return this->test<0>(); }
};
int main(int argc, char **argv){
Test t;
t.test();
t.test<__LINE__>();
return 0;
}
输出:
该函数已在行号调用:0 该函数已在行号调用:16
这里要提到的一件事是,在 C++11 标准中,可以为使用模板的函数提供默认模板值。在 C++11 之前,非类型参数的默认值似乎只适用于类模板参数。因此,在 C++11 中,不需要像上面那样重复函数定义。在 C++11 中,具有 const char* 模板参数也是有效的,但不能将它们与 __FILE__
或 __func__
等文字一起使用,如 here 所述。
所以最后,如果你使用 C++ 或 C++11,这可能是一个非常有趣的替代方案,而不是使用宏来获取调用行。
使用 __LINE__
,但它的类型是什么?
LINE 当前源代码行(一个整数常量)的假定行号(在当前源文件中)。
作为一个整数常量,代码通常可以假定值为 __LINE__ <= INT_MAX
,因此类型为 int
。
要在 C 中打印,printf()
需要匹配说明符:"%d"
。在使用 cout
的 C++ 中,这是一个少得多的问题。
迂腐的担忧:如果行号超过 INT_MAX
1(在 16 位 int
上可以想象得到),希望编译器会产生警告。例子:
format '%d' expects argument of type 'int', but argument 2 has type 'long int' [-Wformat=]
或者,代码可以强制使用更广泛的类型来阻止此类警告。
printf("Not logical value at line number %ld\n", (long) __LINE__);
//or
#include <stdint.h>
printf("Not logical value at line number %jd\n", INTMAX_C(__LINE__));
避免printf()
要避免所有整数限制:stringify。无需调用 printf()
即可直接打印代码:在错误处理2 中避免这样做是一件好事。
#define xstr(a) str(a)
#define str(a) #a
fprintf(stderr, "Not logical value at line number %s\n", xstr(__LINE__));
fputs("Not logical value at line number " xstr(__LINE__) "\n", stderr);
1 拥有如此大的文件当然是糟糕的编程实践,但机器生成的代码可能会很高。
2 在调试中,有时代码根本无法按预期工作。与简单的 fputs()
相比,调用 *printf()
之类的复杂函数本身可能会产生问题。
不定期副业成功案例分享
#define S1(N) #N
#define S2(N) S1(N)
#define LINESTR S2(__LINE__)
。请参阅c-faq.com/ansi/stringize.html__func__
不是宏,而是隐式声明的变量。