1.结构体
1.1结构体的声明
1. struct tag 2. { 3. member-list; 4. }variable-list;
例如我们可以描述一个学生:
struct Stu { char name[20];//名字 int age;//年龄 char sex[5];//性别 char id[20];//学号 }; //分号不能丢
1.2结构体的自引用
在结构中可以包含一个类型为该结构体本身的成员,但是要使用指针(类似链表)
错误方式: struct Node { int data; struct Node next; }; //不可行,sizeof(struct Node)是多少?无限大,没有结束的地方 正确方式: struct Node { int data; struct Node* next;//注意不能去掉srruct };//需要有一个指向NULL
1.3结构体变量的定义和初始化
struct Point { int x; int y; }p1; //声明类型的同时定义变量p1 struct Point p2; //定义结构体变量p2 //初始化:定义变量的同时赋初值。 struct Point p3 = {x, y}; struct Stu //类型声明 { char name[15];//名字 int age; //年龄 }; struct Stu s = {"zhangsan", 20};//初始化 struct Node { int data; struct Point p; struct Node* next; }n1 = {10, {4,5}, NULL}; //结构体嵌套初始化 struct Node n2 = {20, {5, 6}, NULL};//结构体嵌套初始化
1.4结构体内存对齐(重难点)
学习结构体对齐=====探讨结构体的大小,这也是面试容易考的地方
内存对齐的优点:拿空间换取时间,提高性能
对齐的规则:
1. 第一个成员在与结构体变量偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
VS中默认的值为8
3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整
体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
可以参考图然后看着对齐规则,看一下就会了
练习题:
//练习1 struct S1 { char c1;//第一个成员从偏移量为0的地方开始 int i;//int大小为4个字节 需要从偏移量为4的地方开始 char c2;//从偏移量为8的地方开始 }; printf("%d\n", sizeof(struct S1));//一共占 1(c1)+3(浪费)+4(i)+1(c2)+3(浪费)=12 //练习2 struct S2 { char c1; char c2; int i; }; printf("%d\n", sizeof(struct S2)); //练习3 struct S3 { double d; char c; int i; }; printf("%d\n", sizeof(struct S3)); //练习4-结构体嵌套问题 struct S4 { char c1; struct S3 s3; double d; }; printf("%d\n", sizeof(struct S4));
在设计代码的时候我们要尽量让空间小的成员集中在一起,有利于节省空间
比如下面的代码,成员一样,但是占的空间却不一样
struct S1 { char c1; int i; char c2; }; struct S2 { char c1; char c2; int i; };
1.5修改默认的对齐数
结构在对齐方式不合适的时候,我么可以自己更改默认对齐数
#pragma pack ( )
#include <stdio.h> #pragma pack(8)//设置默认对齐数为8 struct S1 { char c1; int i; char c2; }; #pragma pack()//取消设置的默认对齐数,还原为默认 #pragma pack(1)//设置默认对齐数为1 struct S2 { char c1; int i; char c2; }; #pragma pack()//取消设置的默认对齐数,还原为默认 int main() { //输出的结果是什么? printf("%d\n", sizeof(struct S1)); printf("%d\n", sizeof(struct S2)); return 0; }
1.6结构体传参
最好使用传地址的方式进行传参,因为传输整个结构体,占用空间比较大,参数压栈系统开销较大,会导致性能下降
struct S { int data[1000]; int num; }; struct S s = {{1,2,3,4}, 1000}; //结构体传参 void print1(struct S s) { printf("%d\n", s.num); } //结构体地址传参 void print2(struct S* ps) { printf("%d\n", ps->num); } int main() { print1(s); //传结构体 print2(&s); //传地址 return 0; }