目录
一、结构体
结构是一些值的集合,这些值成为成员变量,结构的每个成员可以是不同类型的变量
1.结构体声明
结构体的声明并不占用空间,只是提供一个图纸,定义变量时才会占用空间
struct tag
{
member-list 成员列表
}variable-list; 变量列表
描述一个学生
struct Stu
{
char name[20];
char tele[12];
char sex[10];
int age;
}s4,s5,s6; 这三个为全局变量
在函数内部声明为局部变量
typedef重命名
typedef可以重新定义名字
typedef struct Stu
{
char name[20]
short age;
char tale[12]
char sex[5];
}Stu; Stu是类型,与上面的s1,s2,s3全局变量不同
int main()
{
struct Stu s1;
Stu s1;
上面两个等价
}
匿名结构体类型 / 指针类型
匿名结构体类型
struct
{
int a;
char b;
}sa; sa是结构体变量名字
匿名结构体指针类型
struct
{
int a;
char c;
}*psa;
2.结构体的自引用
struct Node
{
int date;
struct Node* next;
};
typedef引发的自引用问题
正确的用法
typedef struct Node
{
int date;
struct Node* next;
}Node;
int main()
{
struct Node n1;
Node n2;
return 0;
}
错误的用法
typedef struct
{
int date
Node* next;
}Node;
先有鸡现蛋的问题,会报错
3.结构体变量的定义和初始化
- 可以在声明的同时定义变量,为全局变量
- 在函数内部定义结构体变量为局部变量
- 可以在定义的同时赋值,使用{ } 的格式
声明结构体类型
struct T
{
double weight;
short age;
};
声明结构体类型
struct S
{
char c;
struct T st;
int a;
double d;
char arr[20];
};
int main()
{
struct S s = { 'c' , { 55.6 , 30 }, 100 , 3.14 , "hello bit" }; 初始化 结构体嵌套初始化
printf("%c %d %lf %s\n",s.c,s.a,s.d,s.arr); 结构体成员访问
printf("%lf\n",s.st.weight); 结构体成员访问
return 0;
}
4.结构体成员访问
- 通过 . 操作符访问
- 结构体指针,通过 -> 访问
5.结构体传参
传值调用
会创建一个新的空间,空间和时间都浪费严重
传址调用
传过去四个字节的大小,空间占用小,时间少速度快
总结
- 结构体传参的时候,要传结构体的地址
- 函数传参的时候,参数时需要压栈的,如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销比较大,会导致性能的下降
6.内存对齐
规则
- 第一个成员与结构体变量偏移量为0地址处
- 对齐数 = 编译器默认的对齐数 与 该成员大小的较小值
- 其它成员要对齐到某个数(对齐数)的整数倍的地址处
- 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍
- 如果嵌套了结构体,嵌套的结构体对齐到自己最大对齐数的整数倍
struct s3 16个字节最大对齐数8
{
double d;
char c;
int i;
};
struct s4 32个字节最大对齐数8
{
char c1;
struct s3 s3;
double d;
};
内存对齐的意义
结构体的内存对齐是拿空间换取时间的做法。
在设计结构体的时候,既要满足对齐,又要节省空间空间
平台原因
不是所有的硬件平台都能访问任意地址上的任意数据;
某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常
性能原因
数据结构(尤其是栈)应尽可能的在自然边界上对齐
原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;
而对齐的内存访问仅需要一次访问。
修改默认对齐数
#pragma 预处理指令,可以改变默认对齐数
设置默认对齐数
#pragma pack(4)
struct S
{
char c1;
double d;
};
#pragma pack() 取消设置的默认对齐数
结构在对齐方式不合适的时候,可以自己更改默认对齐数
7.位段
C语言允许在一个结构体中以位为单位来指定其成员所占内存长度,这种以位为单位的成员称为“位段”或称“位域”( bit field) 。利用位段能够用较少的位数存储数据。
规则
位段代表比特位
位段的声明和结构是类似的,有两个不同
1.位段成员必须是int,unsigned int,signed int
2.位段的成员名后有一个冒号和一个数字
struct A
{
int _a:2; 2个比特位
int _b:5; 5个比特位
int _c:10; 10个比特位
int _d:30; 30个比特位
共47个比特位 6 个字节
};
int main()
{
printf("%d",sizeof(strcut A));
return 0;
}
打印结果为8
位段的内存分配
- 位段的成员可以是int unsigned,int signed,int或者是char(属于整型家族)类型
- 位段的空间上是按照需要以4个字节(int)或者1个字节(char)的方式来开辟的,不足就新开辟
- 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段
struct A
{
int _a:2; 2个比特位
int _b:5; 5个比特位
int _c:10; 10个比特位
int _d:30; 30个比特位
共47个比特位 6 个字节
};
最后面的数字不能大于32
不够放就开辟一个新空间
位段的跨平台问题
- int位段被当成有符号数还是无符号数是不确定的
- 位段中最大位的数目不能确定(16位机器最大16,32位机器最大32,27在16位机器会出问题)
- 位段总的成员在内存中从左向右分配,还是从右向左分配尚未定义
- 当一个结构包含两个位段,第二个位段成员比较大,无法容纳与第一个位段剩余的位时
是舍弃剩余的位还是利用,这是不确定的
位段的应用
二、枚举
把可能的取值一一列举
一周7天,性别男女,一年12个月份
1.枚举定义
定义枚举类型
enum Sex
{
枚举的可能取值
MALE = 2, MALE, MALE = 2,
FEMALE, FEMALE = 8, FEMALE,
SECRET = 8, SECRET SECRET.
};
enum Color
{
RED, 0
GREEN, 1
BLUE 2
};
int main()
{
定义枚举类型变量
enum Sex s = MALE;
enum Color c = BLUE;
enum Color c = 2; 这是int类型,会报错
整数不能直接赋给一个枚举变量,必须强制类型转换为枚举变量才行(enum day)
printf("%d %d %d\n",RED,GREEN,BLUE);
打印结果为 0 1 2
printf("%d %d %d\n",MALE,FEMALE,SECRET);
打印结果为 2 3 8
0 8 9
2 3 4
return 0;
}
2.优点
- 增加代码的可读性和可维护性
- 和#define定义的标识符比表,枚举有类型检查,更加严谨
- 防止了命名污染
- 便于调试
- 使用方便,一个可以定义多个常量
3.在内存中的存储
sizeof(枚举变量)得到的结果为一个整型int
enum Color
{
RED,
GREEN,
BLUE
};
int main()
{
enum Color c = RED;
为整型类型,4个字节大小
printf("%d\n", sizeof(c));
打印结果为4
return 0;
}
三、联合
联合是一种特殊的自定义类型,这种类型定义的变量也包含一系列的成员,这些成员公用一块空间(所以联合也叫共用体)
1.联合定义
union In
{
char c;
int i;
};
int main()
{
union Un n;
printf("%d\n", szieof(u));
printf("%p\n", &u);
printf("%p\n", &(u.c));
printf("%p\n", &(u.i));
打印的地址都相同
公用同一块空间
return 0;
}
2.特点
- 联合的成员是公用一块内存空间的,这样一个联合变量占用的内存空间 等于它最大成员 所占的内存空间
- 在同一时刻只能使用一个成员,不能同时使用
- 不能对联合变量名赋值,也不能企图引用联合变量名来得到一个值。
- 联合变量的地址和其各个成员的地址是同一个地址。
- 不能用联合变量作为函数的参数或者是返回值,当需要在函数间传递联合变量时,可以使用指向联合变量的指针来实现!
3.内存中的存储
- 联合大小至少是最大成员的大小
- 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍
union Un
{
int a; 4默认8对齐数4
char arr[5]; 1默认8对齐数1
char arr[5] 有5个字节,4是最大对齐数
5不是最大对齐数4的倍数
浪费3个字节,凑到8个字节
};
int main()
{
union Un u;
printf("%d\n", sizeof(u));
打印结果为8
return 0;
}