You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

关于预处理指令的困惑:头文件内容如何正确引入child.c?

头文件卫士如何确保内容正确引入且不重复定义?

先看你给出的三个文件内容:

grandparent.h

#ifndef GRANDPARENT_H
#define GRANDPARENT_H
struct foo { int member; };
#endif /* GRANDPARENT_H */

parent.h

#include "grandparent.h"

child.c

#include "grandparent.h"
#include "parent.h"

你的疑问点在于:既然头文件卫士靠GRANDPARENT_H宏来判断,那第一次引入时,宏和结构体内容是怎么一起被放进child.c的?其实核心是预处理器处理头文件卫士的逻辑是「先判断,再执行块内所有内容」,我给你拆解child.c的预编译全过程:


  1. 第一次处理#include "grandparent.h"
    预处理器看到这个include,会直接把grandparent.h的内容拉到child.c里。此时GRANDPARENT_H还没被定义,所以#ifndef GRANDPARENT_H的判断结果是——预处理器会把#ifndef#endif之间的所有代码都保留并插入到child.c中,包括:

    • #define GRANDPARENT_H(把这个宏标记为已定义)
    • struct foo { int member; };(结构体定义)

    这一步完成后,child.c里已经有了struct foo的完整定义,同时GRANDPARENT_H宏也被标记为已存在。

  2. 处理#include "parent.h"
    预处理器把parent.h的内容拉进来,发现里面又是#include "grandparent.h",于是再次处理这个嵌套的include。
    这时候检查GRANDPARENT_H,发现它已经被定义了,所以#ifndef GRANDPARENT_H的判断结果是——预处理器会直接跳过#ifndef#endif之间的所有内容,既不会重复定义GRANDPARENT_H,也不会再次插入struct foo的定义,完美避免了重复定义问题。


简单来说,头文件卫士就像一扇带锁的门:

  • 第一次推门(include)时,门没锁,你不仅能把屋里的结构体(内容)拿出来,还会顺手把门锁上(定义宏);
  • 之后再推门(重复include)时,门已经锁死了,你拿不到屋里的东西,自然不会重复拿结构体定义,但第一次已经把结构体拿到手了,所以代码里已经有了完整的定义,能正常编译。

内容的提问来源于stack exchange,提问作者rooni

火山引擎 最新活动