C/C++前置声明
1. 为什么需要前置声明
考虑如下的场景,当我们需要进行一个嵌套包含的结构体的时候,由于我们并没有先给出A
或者B
的完整定义,这样编译器会报错,因为A
中包含了B
,B
中又包含了A
typedef struct _A {
int val,
B b, // 此时B还没定义,所以A定义不完全
} A;
typedef struct _B {
int val,
A a, // A的定义不完全,导致B定义也不完全
} B;
2. 前置声明
解决方法就是本文提到的前向声明。C语言中,结构体类型在声明之后,未正式定义之前是一个不完全类型(incomplete type),即只知道其是一个类型,但不知道包含哪些成员。不完全类型只能用于定义指向该类型的指针,或声明使用该类型作为形参指针类型或返回指针类型的函数。指针类型对编译器而言大小固定(如32位机上为四字节),就不会出现编译错误。另外,声明使用该类型作为形参或返回类型的函数也是可以的,因为函数名本身也是地址。
前向声明允许你在不需要完整定义结构体的情况下,声明结构体指针或结构体成员。只要你不需要访问该结构体的具体成员,前向声明就足够了。这也是 C 语言中一种常见的技术,用来减少头文件之间的依赖关系。
需要注意的是,在使用前置声明的时候,我们只能定义为指针,因为前置声明只是告诉编译器有这种类型,并没有给出具体的定义,所以当我们进行实例化的时候,需要为其分配内存空间,由于还不知道类型大小,这样一样会报错,使用指针就能避免这个问题
typedef struct _B B; // 前置声明
typedef struct _A
{
int a;
int b;
B c; // 实例化,为其分配内存空间,报错
}A;
struct _B
{
int a;
int b;
A c;
};
正确的用法应该如下:
#include <stdio.h>
typedef struct _B B; // 前置声明 B
typedef struct _A {
int val;
B *b; // 修改为指向 B 的指针
} A;
struct _B { // 这里不再使用 typedef
int val;
A a;
};
int main(){
A a;
B b;
a.val = 10;
a.b = &b;
b.val = 20;
b.a.val = 30;
printf("A.val: %d\n", a.val);
printf("B.val: %d\n", b.val);
printf("B.a.val: %d\n", b.a.val);
return 0;
}
编译通过后输出:
A.val: 10
B.val: 20
B.a.val: 30
Reference
[1]C语言前向声明