在代码块中调试头文件

Debugging header files in codeblocks

我正在尝试调试代码块中的头文件。我已经尝试了一些选项,例如 "run to cursor" 并通过在头文件中放置断点,但它不会停在那里并通过那里。 如何调试其中的 .h 文件? 如果您需要任何信息,请发表评论。我会提供信息。

调试器会在你的程序被编译后执行你的程序(而且编译器已经 指示将 调试信息 插入已编译的程序)。它可以让你暂停 在您在代码中设置的断点处执行,但它只能在断点处暂停,如果 您已在程序运行时到达的某行可执行代码处设置断点 如果您在某行不是可执行代码或在某行 可执行代码处设置断点 但是程序永远不会到达,那么调试器永远不会到达那个断点。

看看这个玩具 C 程序并考虑每个地方 A,...,I 您可以在其中设置 Code::Blocks 中的断点:

foo.h

#ifndef FOO_H
#define FOO_H

#define FORTY_TWO 42  /* A */
extern void the_answer_is(void); /*B*/

#endif

foo.c

#include "foo.h" /* C */
#include <stdio.h>

static int forty_two = FORTY_TWO; /* D */

void the_answer_is(void) /* E */
{
    int i = forty_two; /* F */
    printf("The answer is...%d\n",i); /* G */
}

main.c

#include "foo.h"

int main(void) /* H */
{
    the_answer_is(); /* I */
    return 0;
}

断点在A?

这是一个预处理器指令。预处理器将其从源中删除 代码在到达编译器之前。所以编译器永远不会看到它。所以 调试器对此一无所知。这里没有可执行代码。一个断点 永远达不到。

B处断点?

这是一个外部函数声明。它提供必要的信息给 编译器和 linker。但它不是可执行代码。 extern void the_answer_is(void); 不是您的程序可以 做的事情 。 永远不会达到此断点。

断点在C?

这里的故事与 A 相同,除了预处理器删除了 #include 指令并将其替换为(预处理的)内容 foo.h。永远不会达到此断点。

断点在D?

这一行是全局静态初始化。它是在编译时完成的, 在你的程序运行之前。没有可执行代码。永远不会达到此断点。

断点在 E?

这一行是一个函数入口点,开始一个函数定义。 它本身不是可执行代码,但它是某些程序的入口点 可执行代码。所以可以到达这里的断点。调试器将 只是 "overshoot" 一点,然后停在第一个可执行行 函数定义。如果函数the_answer_is,您的程序将在此处到达断点 在执行的某个时刻被调用。

断点在F?

这一行定义并初始化int i。这里的断点将是 在与 E 相同的情况下达到,它是调试器所在的位置 实际上会在 E.

的断点处停止

断点在G?

最有趣的一个。这一行是对一个函数的调用 在外部库(标准 C 库)中定义。编译器将看到 extern printf 读取 header <stdio.h> 时的声明 如果您打开了 stdio.h 并在 extern 处放置了一个断点 printf 的声明,它永远不会到达(与 B 相同)。但 这里我们有一个 调用 printf,并且该调用可以在 与 EF.

相同的情况

所以可以到达这个断点。但是假设你到达了它并且你 然后想在调试器中进入这个对[=2​​0=]的调用。你 将无法。调试器将跳过它。 你的程序 已经编译了调试信息,但是其中的库 printf 定义了还没有。您根本不编译该库;你刚才 link 它与您的程序。没有关于库的调试信息, 并访问它的源代码,调试器不能进入它;所以 没有。

您可能认为如果您可以 "step into" a header like <stdio.h> 你会找到 在那里声明的库函数,比如说 printf,你可以 调试它。你不会在那里找到源代码。你会发现的是 外部声明:

extern int printf( const char* format, ... );

<stdio.h> 对你的编译器的作用只是告诉它名字是什么 stdio 库函数的名称及其调用方式。然后 它可以告诉你是否调用了一个尚未声明的函数或调用它 以错误的方式。它不需要 printf 的源代码,因为它已经编译在 标准 C 库,您的程序会自动 link 编辑。

断点在H?

这是你整个程序的入口点,所以这个断点 总是,除非发生灾难性事故。就如同 E,调试器将在下一个可执行行停止。

断点在 I?

这个对the_answer_is的调用总会到达,就像H一样。 因为我们现在知道 the_answer_is 得到所有,我们也知道 断点 E,FG 达到了。

底线

  • #include指令处设置断点是没有意义的 或任何其他预处理器指令。编译器永远看不到它们; 调试器对它们一无所知。

  • 一般来说,在C中,把断点放在header里面是没有意义的 文件,因为它们只包含声明和预处理器 指令,而不是可执行代码。 (在 C++ 中,情况完全不同。 C++ headers 通常包含可执行代码,您可以放置​​断点 在他们里面。)