1. 简介
结构体是一种复合数据类型,它不同于数组,允许将不同类型的数据包含在内组成一个复合的数据类型。C语言中使用struct关键字来表示结构体,同时允许程序员自定义结构体,这使得C语言更加地灵活编程。
但是它不同于面向对象中的类的概念,结构体内只有不同类型的属性,没有方法。
联合体也是一种复合的数据类型,它允许在相同的位置存储不同的数据,C语言中使用union关键字来表示,联合体内的成员不具有单独的内存空间,都是共享内存空间的,也就是说同一时刻只能存储一个值。
2. struct结构体
2.1 struct的声明与初始化
结构体使用struct关键字来定义,可以包含任何基本数据类型及其他复合数据类型。
struct person{ //定义一个结构体person
char name[20];
int age;
int height;
};
上面的示例中,使用关键字struct定义了一个名为person的结构体类型,包含三个不同类型的成员,结构体结尾的分号不要忘记写上。
结构体类型定义好之后,就可以声明结构体类型的变量,然后对结构体内的成员进行初始化。
struct person person1;
person1.name = "小明";
person1.age = 18;
person1.height = 180;
//等同于
struct person person1 = {"小明",18,180};
上面的示例是结构体person的变量声明以及初始化的两种方式,使用大括号{}对成员赋值时,必须与声明时的成员顺序一致,除非是指定成员初始化;如果缺失成员的初始化,会自动初始化为0,和数组一样。
struct person person1 = {.age = 18,.name = "小明"};//指定成员赋值
//等同于
struct person person1 = {.age = 18,.name = "小明".height = 0};//初始化为0
结构体类型的定义与变量声明可以合并在一起。
struct person{ //定义一个结构体person
char name[20];
int age;
int height;
}person1;
如果定义类型与变量声明合并在一起,则允许省略类型名称,定义匿名的结构体类型。
struct{ //定义一个结构体person
char name[20];
int age;
int height;
}person1;
结构体数据类型还可以支持类型定义、变量声明、初始化写在一起。
struct{ //定义一个结构体person
char name[20];
int age;
int height;
}person1 = {"小明",18,180};
2.2 结构体指针
结构体也是数据类型,也可以声明为指针变量,让指针变量指向结构体类型。
struct person{ //定义一个结构体person
char name[20];
int age;
int height;
}*person1;//指针变量指向结构体类型
//等同于
struct person{ //定义一个结构体person
char name[20];
int age;
int height;
};
struct person *person1;
结构体类型可以作为函数的参数传递,可以是值传递,也可以是指针传递。通常情况下,使用指针变量作为参数,不必创建一个结构体副本,避免了复制整个结构体到内存空间。
struct person{ //定义一个结构体person
char name[20];
int age;
int height;
};
//传入结构体类型
void func(struct person person1){
person1.age = 18;
}
struct person person1 = {"小明",18,180};
func(person1);
//传入结构体指针
void func(struct person *person1){
(*person1).age = 18;//等同于person1->age
}
struct person person1 = {"小明",18,180};
func(&person1);
上面的示例中,分别展现了值传递和指针传递的两种方式,值传递时传递的是结构体的副本,修改结构体的数据时不会影响原结构体的值,但是效率会低下;如果是指针传递的话,则是传入的是结构体的地址,即同一个结构体,这样的话只需要操作指针即可,无需把整个结构体都复制到栈中,提高了程序的性能。
传入结构体指针时,就需要使用(*person1)先取值,再使用.符号获取成员,这样写会很麻烦;C语言提供一种更便捷的方式,使用->符号获取结构体成员,比如person1->age。
另外,使用结构体指针作为函数参数时,在函数调用时,实参要传入指针变量的地址。
2.3 结构体位域
C语言中,结构体内的成员支持以二进制位为单位定义,可以自由控制结构体所占用的内存空间,这就是结构体的位域成员(bit field)。位域成员再定义时需要指定所占用的位数。
struct s{//定义含有位域成员的结构体
int a:1;
int b:2;
int c:3;
int :4;//匿名位域,跳过4位
}s1;
上面的示例中定义了一个结构体类型s,含有4个位域成员,前面是类型和位域名称,冒号后面是占用的位数,如果相邻的位域成员类型相同且总位数没有超过类型的大小,则会连续存储;如果超过了类型的大小(包含匿名位域),就从新的内存单元开始存储。上面的int类型是32位且相邻,所以位域会连续存储。
3. union联合体
3.1 union声明与初始化
C语言使用union关键字来定义联合体类型,和结构体类似,都可以定义不同类型的数据。union联合体内的成员共享同一块内存空间,联合体的大小是最大成员的大小,而非所有成员的总和。
union u{//定义一个联合体类型
int i;
float f;
char c[10];
};
上面示例中,使用union关键字定义了联合体u,包含三个不同类型的成员。同样和结构体一样,也需要声明变量并初始化,也是用.符号来访问联合体成员。
union u u1;//声明union变量
//初始化
u1.i = 1;
u1.f = 1.2;
u1.c = "C";
联合体变量声明和初始化也可以合并在一起,如果不指定成员名,默认赋值给第一个成员。
union u u1 = {.i = 1};//指定成员初始化
union u u1 = {1};//不指定成员默认初始化第一个成员,即i
3.2 联合体指针
和结构体一样,联合体也可以声明为指针变量,使指针指向联合体,也是使用->符号访问联合体成员。
union u{
int i;
float f;
char c[10];
};
union u *u1;//声明联合体指针
u1->i = 1;//访问联合体成员
4. 结构体与联合体的区别
- 结构体类型使用struct关键字定义,而联合体类型使用union关键字定义;
- 结构体类型中的每个成员都有独立的内存空间,联合体类型的成员是共享内存,大小是最大成员的大小;
- 结构体类型中的大小是所有成员的总和(对齐原则),联合体类型的大小是最大成员的大小。