为了调试目的,我可以在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)
为了调试目的,我可以在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)
Answers:
您应该使用预处理宏__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__);
#define S1(N) #N
#define S2(N) S1(N)
#define LINESTR S2(__LINE__)
。参见c-faq.com/ansi/stringize.html
__func__
它不是一个宏,它是一个隐式声明的变量。
作为C ++标准的一部分,存在一些可以使用的预定义宏。C ++标准的第16.8节定义了__LINE__
宏。
__LINE__
:当前源行的行号(十进制常数)。
__FILE__
:源文件的假定名称(字符串文字)。
__DATE__
:源文件的翻译日期(字符串文字...)
__TIME__
:源文件的翻译时间(字符串文字...)
__STDC__
:是否__STDC__
已预定义
__cplusplus
:名称__cplusplus
在何时定义为值199711L编译C ++转换单元
因此您的代码将是:
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>
C ++ 20提供了一种通过使用std :: source_location实现此目的的新方法。目前可以在gcc和clang中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
被调用位置的行号进行初始化。
您可以在这里在线尝试。
尝试__FILE__
和__LINE__
。
您可能还会发现__DATE__
并__TIME__
有用。
尽管除非必须在客户端上调试程序并因此需要记录这些信息,否则应使用常规调试。
raw code
(例如:__)。@mmyers试图提供帮助,但是他只跳过了下划线之一,因此您留下了斜体的标记语法。不过,我同意,这里的选票有点苛刻。
由于我现在也面临这个问题,并且无法在此处提出另一个不同但也有效的问题的答案,因此,我将为以下问题提供示例解决方案:仅获取已调用函数的行号使用模板的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 Standard中,可以为使用模板的函数提供默认模板值。在C ++ 11之前的版本中,非类型参数的默认值似乎仅适用于类模板参数。因此,在C ++ 11中,将不需要具有上述重复的函数定义。在C ++ 11中,具有const char *模板参数也有效,但无法将其与如__FILE__
或此处__func__
提到的文字一起使用。
因此,最后,如果您使用的是C ++或C ++ 11,这可能是一个比使用宏来获取调用行更有趣的选择。
使用__LINE__
,但是它的类型是什么?
LINE当前源行的假定行号(在当前源文件中)(整数常量)。
作为一个整数常数,代码通常可以假定值为__LINE__ <= INT_MAX
,因此类型为 int
。
要使用C打印,printf()
需要匹配的说明符:"%d"
。在使用C ++的C ++中,这要少得多cout
。
值得关注的问题:如果行号超过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在调试中,有时代码根本无法按预期工作。*printf()
与简单函数相比,调用复杂函数本身可能会引发问题fputs()
。