CSDN话题挑战赛第2期
参赛话题:学习笔记
前言
阅读这篇文章,博主会带你认识、定义结构体,还会介绍如何计算结构体大小。
一.认识结构体
结构体是由一批数据组合而成的结构型数据。组成结构型数据的每个数据称为结构型数据的成员。
二.结构体声明
struct tag
{
member-list;
}variable-list;
假如我们想描述一本书,如果用单一的变量我们可能无法涵盖所有的信息。
这时我们可以使用结构体。
struct book
{
char name[20];//书名
int date;//出版日期
char sex[5];//类型
char id[20];//作者
}; //分号不能丢
三.特殊的声明
3.1 不完全声明
在声明结构体的时候,可以不完全声明,比如匿名结构体类型。
struct
{
int a;
char b;
float c;
}x;
struct
{
int a;
char b;
float c;
}a[20], *p;
上面两个结构在定义时省略掉了结构体标签(tag)
3.2 p = &x合法么
注意:虽然结构体内含有的变量相同,但编译器会把它们当成不同的类型,从而导致报错。因此是不合法的。
四.结构体自引用
4.1 结构体能够包含自己么
struct Node
{
int data;
struct Node next;
};
此时编译器会报错,为什么呢?
next是我们定义的的一个结构体变量,需要占用内存,但他的大小是多大呢?next里面又含有一个next,如果真的这样定义,next的大小会变成无穷大,因此编译失败。
那么应该如何编译呢?
struct Node
{
int data;
struct Node* next;
};
当结构体自引用的时候,我们使用指针,因为指针的大小是确定的,因此不会出现上面的情况。
4.2 typedef
typedef可以给结构体重命名,比如
typedef struct Node
{
int data;
}Node;
这样我们在定义结构体变量的时候,就可以使用重定义的名字,会稍微便捷一点。
struct Node num;
Node num;
五.结构体变量定义和初始化
有了前面的铺垫,那如何定义结构体变量其实很简单。
struct Point
{
int x;
int y;
}p1; //声明类型的同时定义变量p1
struct Point p2; //定义结构体变量p2
可以声明的同时定义如p1,也可以在后面定义如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};//结构体嵌套初始化
当我们在在结构体中嵌套结构体时,定义时注意在{}里面再使用一个{}来给里面的结构体赋值。
六.结构体内存对齐
既然我们已经掌握了结构体的基本使用,那么应该怎么样计算结构体的大小呢?是将所有成员所占的空间相加么?显然不是的。(这是一个考点)
计算结构体之前,我们需要先了解一下计算的规则。
- 第一个成员在与结构体变量偏移量为0的地址处。
- 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
(VS中默认的值为8)- 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
- 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整
体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
1.计算结构体大小
- 练习1
struct S1
{
char c1;
int i;
char c2;
};
printf("%d\n", sizeof(struct S1));
运行结果:
我们画个图来解释这个题:
第一个元素对齐偏移量为0的地址处,char类型占据一个字节,i为int类型,大小为4小于VS默认值8,因此i对齐4,又因为整体大小需要占据最大对齐数(这道题为4)的整数倍处,因此大小为12.
- 练习2.
struct S3
{
double d;
char c;
int i;
};
struct S4
{
char c1;
struct S3 s3;
double d;
};
printf("%d\n", sizeof(struct S4));
运行结果:
依旧用一个图来解释:
用练习1的方法我们能轻易地算出S3的大小是16,S3里的最大对齐数是8,因此S3对齐到8的位址,d对齐到24的位址,加起来为32是最大对齐数8的倍数。
由此我们还能得出一个结论:
将较小的变量放在一起定义会比较节省空间。
七·.修改对齐数
#pragma pack(1)//设置默认对齐数为1
struct S2
{
char c1;
int i;
char c2;
};
运行结果:
八.结构体传参
我们都知道函数传参既能传值又能传址,那么当我们想要传递结构体的时候我们应该传哪一种呢?
函数传参的时候,参数需要压栈,会有时间和空间上的系统开销。
如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。因此最好传址。
最后
如果你觉得博主写的还不错的话,记得点赞收藏加关注,感谢支持!