踩坑记录——明明包含了头文件,却提示找不到结构体定义
明明包含了头文件,却提示找不到结构体定义
1. 踩坑现象
在开发过程中,在头文件 b.h 中定义了结构体 my_struct_test_t,详细定义如下:
#ifndef _B_H_
#define _B_H_
#include "a.h"
typedef struct
{
int a;
int b;
}my_struct_test_t;
#endif /* end of b.h */
源文件 b.c 用到了该结构体,代码如下:
#include "b.h"
void fun_test_b(my_struct_test_t* test)
{
test->a = 100;
test->b = 200;
}
同时在另一个源文件 a.c 中定义了一个函数,函数传参也用到了 my_struct_test_t 类型,代码如下:
#include "a.h"
void fun_test_a(my_struct_test_t* test)
{
test->a = 10;
test->b = 20;
}
在头文件 a.h 中声明一下函数 fun_test,为了能够用到 my_struct_test_t 这个结构体,#include “b.h” 这个头文件,代码如下:
#ifndef _A_H_
#define _A_H_
#include "b.h"
void fun_test(my_struct_test_t* test);
#endif /* end of a.h */
点击编译,报错如下:
诡异的现象出现了,竟然找不到 my_struct_test_t 这个结构体类型。
2. 入坑原因
郁闷了好久,终于找到原因了,因为平时喜欢全家桶式的 #include 所有头文件,即使在 b.h 这个头文件没用到 a.h 里的内容,也包含了 a.h 这个文件。
这样的交叉互相包含在编程的时候用起来很爽,但在编译的预处理阶段,将 #include 的文件复制过去时,就会因为定义顺序问题,有的变量时会出现定义的前面使用,导致编译器报错。
如:编译器在编译的预处理阶段处理 b.c 这个文件的过程如下:
生成的 b.i 文件内容如下:
# 1 "../User/b.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "../User/b.c"
# 1 "../User/b.h" 1
# 1 "../User/a.h" 1
# 1 "../User/b.h" 1
# 5 "../User/a.h" 2
void fun_test_a(my_struct_test_t* test);
# 5 "../User/b.h" 2
typedef struct
{
int a;
int b;
}my_struct_test_t;
# 2 "../User/b.c" 2
void fun_test_b(my_struct_test_t* test)
{
test->a = 100;
test->b = 200;
}
这里可以发现,因为在 b.h 的头文件中 包含了 a.h 这个文件,导致在预处理时,将 a.h 中的内容 复制到 b.h 内容前面。
因为在 a.h 中的函数 void fun_test_a(my_struct_test_t* test)
用到了 b.h 中的结构体类型定义,但该函数的定义又在预处理阶段被放到了结构体定义的前面, 导致了编译器报错,提示找不到该类型的定义。
3. 避免入坑
找到了原因,解决方法很简单,
-
尽量不要采用全家桶式的方式 #include 所有头文件,尽量做到用到哪个文件,包含哪个文件, 减少头文件的嵌套和交叉引用。
-
在源文件中应仅包含必需的头文件,且尽量不要在头文件中包含其它头文件
上面的例子中,可以将 b.h 中的 #include “a.h”
放到 结构体类型定义的后面,这样就不会出现这个问题。
#ifndef _B_H_
#define _B_H_
typedef struct
{
int a;
int b;
}my_struct_test_t;
#include "a.h"
#endif /* end of b.h */
也可将 #include “a.h”
放在 b.c 这个文件中。
posted on 2023-05-19 21:27 Wahahahehehe 阅读(5478) 评论(1) 编辑 收藏 举报