问题原因
最近在做嵌入式项目时,编译过程中头文件爆出了“unknown type name …”的错误。经过检查,未知的变量类型所在的头文件已经被包含在本头文件中了,而且也添加进了头文件目录,通过Ctrl键也能跳转,表面看上去没有任何问题,甚至怀疑是编译器出问题了。随后,我查看了编译过程的输出,通过编译顺序发现了问题所在。
接下来我描述一下问题具体出现的过程,假如有"a.h" "b.h" "c.h"三个头文件,内容如下:
a.h:
#ifndef _A_H
#define _A_H
#include "c.h"
typedef struct
{
uint8 a;
uint8 b;
}type_a;
u8 num;
b.h:
#ifndef _B_H
#define _B_H
#include "a.h"
typedef struct
{
type_a a;
uint8 b;
}type_b;
c.h:
#ifndef _C_H
#define _C_H
#include "b.h"
typedef struct
{
type_b a;
uint8 b;
}type_c;
typedef char u8;
当然这只是个很简单的例子,在实际开发中不会出现这样看起来很呆的循环包含的情况。在这个例子中,三个头文件的包含关系是:b包含a,c包含b,而a又包含c。如果编译过程第一个走到c.h, #ifndef _C_H 语句会判断当前没有编译c.h文件,而对c.h进行编译,当执行到 #include "b.h" 这条语句的时候,会寻找名为b.h的头文件并进行编译,而这个时候,c.h中剩下的内容并没有参与编译。同理,在b.h文件中走到 #include "a.h" 这条语句时又会寻找a.h头文件进行编译。而在a.h中,由于包含了c.h头文件,编译器也会去寻找该头文件,然而 #ifndef _C_H 这条语句提示编译器c.h已经参与编译了,因此会跳出c.h的编译,回到a.h中,当继续编译到 u8 num; 时,编译器会发现之前没有编译过名为u8的类型名,最终报出"unknown type name 'u8' "的错误。
这就是为什么我们包含头文件后明明可以跳转找到类型定义,编译器依旧报"unknown type name"的原因。
解决方案
其实了解了原因之后,解决方法很明显:
重新规划头文件内容,调整头文件内容,避免出现循环引用的情况。虽然这一句话看起来很简单,但实际上要对整个项目结构非常了解,熟悉项目涉及的每个头文件的作用。同时调整头文件内容后可能还需要对.c文件也做出相应修改。要是实在不会调整,就只能减少项目功能,精简头文件包含结构了。