个人认为C语言的学习重点在于四个方面,分别是:数组、指针、结构体、动态内存管理。这四个方面是重点也是难点。在我最开始学习的时候也是朦朦胧胧感觉学习的不是很透彻,不过经过后面的慢慢细品之后,才有中顿悟的感觉。下面我来总结一下结构体方面的知识吧。
1、结构体定义
结构体是一个自定义数据类型,这里可以做一个类比。让我们回顾一下什么是数组,数组是一种元素类型相同的集合,这个集合统称为数组。那么,结构体可以理解成一组元素类型不同的集合。再清晰地来说,结构是一些不同类型值的集合,这些值被称为成员变量(结构的每个成员变量可以是不同类型的值)。
1.1、结构体的声明
例如这里描述一个学生,假设成员变量有姓名、年龄、性别和学号,那么这个结构体就可以被定义为:
struct Stu
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
};//分号不能丢
在声明结构体变量时要有struct关键字,Stu为结构体变量的名字,它里面含有四个成员变量,在结构体的大括号后面需要以分号结尾。
在有些时候也会看到匿名结构体,其实就是在定义的时候省略了结构体变量的名称,这种匿名结构体在使用过程中很不方便。只有在定义结束之后立刻创建全局的结构体变量才能正常使用。否则在主函数中是无法创建结构体变量的。
1.2、结构体的自引用
结构体的自引用顾名思义就是一个结构体变量中的成员变量是另外一个结构体。但在定义时将被引用结构的地址信息存放在目标结构体变量中。这里举个例子:
struct Node
{
int data;
struct Node* next;
};
1.3、结构体变量的定义和初始化
有了结构体类型,那如何定义变量,其实很简单。
struct Point
{
int x;
int y;
}p1; //声明类型的同时定义变量p1
struct Point p2; //定义结构体变量p2
p1是在创建结构体类型时就创建的结构体变量,p2是在结构体创建之后创建的变量。
初始化可按照如下方式进行:
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、结构体内存对齐
在学习一个数据类型的时候,往往需要计算它的大小。那么,结构体类型的大小如何来计算呢?先利用sizeof()操作符观察一下下面两段程序的运行结果。
#include <stdio.h>
//第一段
struct S1
{
char c1;
int i;
char c2;
}S1;
//第二段
struct S2
{
char c1;
char c2;
int i;
};
int main()
{
struct S1 S1;
struct S2 S2;
printf("S1:%zd\n", sizeof(struct S1));
printf("S2:%zd\n", sizeof(struct S2));;
return 0;
}
按照常理,我们可以看到S1和S2中的成员变量都是一样的,只是在顺序上有所差异。然而,就是这顺序上的差异导致了它们的大小有了巨大的差异。执行这段代码:

两个结构体变量的大小发生了如此大的差异,这源于结构体类型的内存对齐。先来看一下结构体的对齐规则:
1.第一个成员在与结构体变量偏移量为0的地址处。
2.其他成员变量要对齐到对齐数的整数倍地址处。
对齐数:编译器默认的一个对齐数 与 改成员大小的较小值。(VS默认对齐数为:8)
3.结构体的总大小为最大对齐数(每个成员都有一个对齐数)的整数倍处。
4.如果有嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(包括嵌套结构体的对齐数)的整数倍。
有了上面的对齐规则,我们来分析一下上面的代码:

同样的道理,我们来分析一下S2的内存分布:

我们可以看到,成员的顺序会对结构体的大小产生很大的影响。那么,我们在设计结构体的时候,我们既要满足对齐,又要节省空间时应该将占用空间小的成员尽量集中在一起。
问题:为什么要进行结构体对齐??
主要有两个原因:第一、硬件层面:数据按照对齐规则存储能够提高访问效率,处理器一次读取多个字节时,对其的数据可以一次性完整的读取,减少读取的次数。第二、利于跨平台和代码移植,遵循通用对齐规则可以保证结构体在不同编译环境、不同硬件架构下的行为一致,避免因内存布局出现错误
1.5、结构体传参
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;
}
在需要传递结构体变量时,可以直接传值也可以传址。尽量选择传地址的方式,因为这样会很节省空间(地址只有4/8个字节)。
***上面就是本人认为比较重要的部分了,希望可以帮助到读者。***

3322

被折叠的 条评论
为什么被折叠?



