前言
结构体
结构体的声明
结构的基础知识
结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。
结构的声明
1 | struct tag |
注意:程序应尽可能少定义全局变量,在程序中到处引用全局变量,会导致程序难以控制,容易出错。且声明不占用内存
例如描述一个学生1
2
3
4
5
6
7
8struct Stu
{
char name[20];//名字
int age;//年龄
char sex[5];//姓名
char telp[11];//电话号
char id[20];//学号
};//分号不能丢
特殊的声明
特殊声明就是不完全声明又叫匿名结构体类型声明
1 | //匿名结构体类型声明 |
对于上面声明,均省略了结构体标签,而对于匿名结构体类型声明,编译器会把上面两个声明当成完全不同的 两个类型,所以对于p = &x是非法的
结构的成员
结构的成员可以是标量,数组,指针,甚至是其他结构体。
结构体成员的访问
- 结构体变量访问成员: 结构变量成员通过点操作符(.)访问,点操作符接收两个操作数。
- 结构体访问指向变量的成员: 有时候我们得到不是一个结构体变量,而是一个指向结构体的指针,就应该用(->)操作符。
1 | struct Stu |
结构自引用
简而言之就是在结构中包含一个类型为结构本身的成员
1 | typedef struct Node |
结构的不完整声明
1 | struct B; |
结构体变量定义及初始化
有了结构体类型,定义及初始化就很简单了
1 | struct Stu |
结构体内存对齐
目前,我们已经掌握了结构体的基本使用了,现在来开始讨论一个问题:计算结构的大小。当然也是一个热门考点:结构体内存对齐
如何计算?
结构体内存对齐规则:
- 第一个成员在与结构体变量偏移量为0的地址处;
- 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。对齐数=编译器默认对齐数 与该成员自身大小中的最小值。VS默认值为8,linux中gcc默认值为4.
- 结构体总大小为最大对齐数的整数倍;
- 如果嵌套了结构体的情况,嵌套的结构体对齐到自己最大对齐数的整数倍处,结构体的大小就是所有对齐数中最大对齐数的整数倍。
注意:1
2
3
...
为什么存在内存对齐?
平台原因(移植原因):
- 不是所有硬件平台都能访问任意地址上的数据,某些硬件平台只能在某些地址处取某些特定类型的数据,否则肯呢个抛出硬件异常。
性能原因:
- 数据结构(尤其是栈)应该尽可能的在自然边界上对齐,原因在于:访问未对齐的内存,处理器需要两次访问,而访问对齐内存,处理器只需访问一次。
总的来说:结构体内存对齐就是用空间换取时间的做法,而我们在设计的时候既要满足内存对齐,还要满足节省空间,那么唯一做法:让占用空间小的成员尽可能集中在一起。
1 | //练习一 |
附加:
1 | //宏,表示结构体成员变量在内存中偏移量包含于头文件#include <stddef.h> |
结构体传参
讲解函数栈桢的时候,我们知道函数传参是需要参数压栈的,如果传递的是一个结构体对象,而结构体过大,参数压栈的系统开销就会很大,所以导致系统性能下降。
结论
结构体传参的时候要传结构体指针。
位段
什么是位段?
位段声明与结构体类型相似,有两个不同:
- 位段成员必须int 、unsigned int 或 signed int。
- 位段成员名后面有一个冒号和一个数字。
位段内存分配
- 位段成员可以是int 、unsigned int、signed int、或者char(整形家族)类型。
- 位段的空间上按四个字节(int)或者一个字节(char)方式开辟
- 位段涉及很多不确定因素,位段是不跨平台的,注重可移植性程序因避免使用位段。
eg:1
2
3
4
5
6
7
8struct A
{
int a:2;
int b:5;
int c:10;
int d:30;
};
printf("%d",sizeof(struct A)); //8
位段跨平台问题
- int位段当成有符号还是无符号是不确定的
- 位段中最大位数目不确定的
- 位段中成员是从左到右还是从右到左是不确定的
- 当一个结构体包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的还是利用,是不确定的
总结
跟结构相比,位段可以达到同样的效果,还节省空间,但是因为存在跨平台问题,所以运用的就不是很多了。
枚举
枚举顾名思义就是一一列举。
比如:星期、颜色、性别等;
枚举类型定义
1 | enum Color |
以上定义的enum Color 就是枚举类型,{}中内容就是可能取值,也叫枚举常量,默认从零开始一次递增1,当然有时候也可以自定义赋值:1
2
3
4
5
6enum Color
{
RED = 1,
GREEN = 4,
BLUE = 8
};
枚举优点
- 增加代码可读性与可维护性
- 与#define相比有类型检查,更加严谨
- 防止命名污染
- 便于调试
- 使用方便,一次可定义多个常量
联合(共用体)
联合定义
联合也是一种特殊自定义类型,该类型定义变量也包含一系列成员,特征是这些成员公用一块空间。
eg
1 | //联合类型声明 |
联合特点
联合成员公用一块内存空间,这样一个联合变量大小,至少是最大成员大小。
经典应用
判断当前计算机大小端存储:
1 | int CheckSystem() |
联合大小的计算
- 联合大小至少是最大成员大小
- 当最大成员大小不是最大对齐数整数倍时,就要对齐到最大对齐数的整数倍
eg1
2
3
4
5
6
7
8
9
10
11
12union UN1
{
char arr[5];
int i;
};
union UN2
{
short c[7];
int i;
};
printf("%d",sizeof(union UN1)); //8
printf("%d",sizeof(union UN2)); //16
结构体与联合巧妙使用:
1 | //将long类型ip地址,转换为电分十进制形式 |
结语
通过自定义类型学习,掌握了内存对齐等相关运用,以及如何检验大小端问题。
好好学习,天天编程
