一文理清c中的各种结构体定义和前向声明


一直都是半c++半c,用惯了class,使用struct的时候比较少, 偏偏经常见到c有各种各样的结构体定义,让人百思不得其解,本文理清它们的本质。

1.结构体的定义

c语言中的结构体定义一般是这么做的,大概是起初设计的不完善?导致使用结构体的使用需要在前面加上struct,而在C语言中,struct A 是被视为一个整体而不是struct, A,所以后面的变量哪怕和类型一样,也不会冲突

定义
struct A{
        int num;
}; 
使用
struct A A;

c语言作者为了方便定义变量(同时更是为了方便定义结构体类型,见2),开发了这样的语法,

struct A{
        int num;
}A; 

2. typedef和struct

typedef 和struct 混在一起是导致结构体定义方式丰富的一大原因,typedef 用于类型声明有好多看起来奇怪的地方,譬如:

声明一种变量类型
typedef int Integer; 

声明一个函数指针类型
typedef void (* fn)(int a, int b);

声明一个结构体的类型为A
typedef struct A{ 
        int num ;
}A;

但是只要把上面的typedef去掉,上面的语句就变成了定义了一个变量Integer, 定义了一个函数指针变量fn, 定义了一个结构体变量A,也就是说typedef用在任何定义变量语句前面,都会把它从变量定义改成类型定义, 设计者这么做也是为了一套原则多处使用,不想增加语言的学习成本。
typedef隐藏了一个非常重要的一点: 使用typedef 定义结构体类型的时候,如果本身类型不存在,本身的类型也会产生,这是什么意思呢?如下:

typedef struct A{
        int num;
}A_t;

除了使用这种方式定义
A_t temp;
还可以使用这种方式定义,struct A这种类型也产生了
struct A temp;

3.为什么struct支持省略类型定义?

前面说过 struct A 这两者是一体的,不知道出于什么心态,偏偏结构体的定义支持了省略,出现了以下的写法,这样就定义了一个不知道名字类型的结构体变量A

struct {
        int num
}A;

但是这种东西绝对不是为了只是定义一个两个变量而存在的,毕竟定义完变量后类型就匿名了,用不了了,区分度又不好; 其实,省略类型定义主要是为了使 typedef在声明一种结构体类型更加简单而出现的,而不是为了定义变量, 如下:

定义如下
typedef stuct{
      int num;
}A_t;
使用的时候,是没有问题的
A_t A={
    .num = 0,
};

4.结构体类型的前向声明

在解决头文件循环依赖的时候,常需要用到前向声明, 结构体的前向声明如下:

声明了一个结构体类型A
struct A;
定义一个变量
struct A temp;     error 错误, struct A 不完整
struct A *temp;    ok
补充结构体类型为A的定义
struct A{
        int num;
};

这种前向声明和c++的class不同,只能声明指针,声明变量会报错,让我很讶异,这种在同文件内容中扫描,在编译期就可以完成,不用等到跨文件链接阶段才做的事情居然不行?令人难以理解, 好消息是现在比较新的gcc都已经支持了,(笔者使用gcc 5.4),但是g++不行,所以尽量不要再g++中写struct?


最后, 由于经常会使用typedef定义struct,说一说这种类型的前向声明目前,如下:

对于下面的结构体类型,可以使用如下的前向声明
typedef struct A A_t;

typedef struct A{
      int num;
}A_t;

我们知道typedef的时候,不但产生了类型A_t,也产生了Struct A, 所以上面的typedef才合理没有报错,是因为它在typedef的时候先声明了 struct A, 相当于

struct A; 
typedef struct A A_t

以及, c语言对于struct类型和变量的区分做的比较好,对于struct A 和A是不同的东西在c中是不同的东西,c把struct A这两个合在一起看作一个东西, 所以经常也可以看到这么做结构体的声明,也是完全可以的

typedef struct A{
      int num;
}A;

strcut A A; 这样声明也可以
A A; 两者等价

推荐阅读更多精彩内容