结构体重点知识大盘点

0、结构体基础知识

1、结构体是一些值的集合,这些值被称为成员,它们的类型是可以不同的。(与数组相似,但数组元素的类型都是相同的)。用来描述由基本数据类型组成的复杂类型。

2、结构体也有全局的和局部的。

3、struct+标签是一种结构体类型,可以用typedef重命名,以省略struct的书写。

1、结构体不完全声明

匿名结构体类型(省略标签)。(只想使用一次)

写结构体类型时直接创建变量,之后只能使用变量,无法找不到匿名结构体类型。 

值得注意的是:

struct
{
int a;
char b;
float c;
}x;
struct
{
int a;
char b;
float c;
}a[20], *p;

尽管两个匿名结构体的成员完全相同,将其  p=&x 的操作仍然不行(类型不兼容)。编译器认为它们并不是一种类型,结论是匿名结构体仅能使用一次。

2、结构体的自引用

在链表中,一个节点既要存储一个数据,又要能够找到下一个节点。

因此我们可以将每个节点的类型设置为结构体类型。

当我们在这个结构体类型中包含下一个节点的结构体时,会导致结构体的大小无限增大,为避免这一错误,并且顺利找到下一节点,我们可以创建一个结构体指针,指向下一个节点。

struct Node
{
int data;
struct Node next;
};

struct Node
{
int data;
struct Node* next;
}

注意:typedef重命名必须在一个类型已经完整定义后才能使用,否则将无法识别。

typedef struct Node
{
int data;
struct Node* next;
}Node;

 {}中的struct不可省略。

3、结构体变量的定义和初始化

结构体变量的定义有局部和全局。

初始化时类似数组,使用{},结构体内有其他结构体时,可以嵌套使用{}。

还可以乱序/不规则初始化,只需要 用   .找到相应成员,然后赋值即可。

struct Point
{
	int x;
	int y;
}p1 = {10, 20};

struct Point p2 = {0,0};

struct S
{
	int num;
	char ch;
	struct Point p;
	float d;
};

int main()
{
	struct Point p3 = {1,2};
	struct S s = { 100, 'w', {2,5}, 3.14f};
	struct S s2 = {.d=1.2f, .p.x=3,.p.y=5, .ch = 'q', .num=200};
	printf("%d %c %d %d %f\n", s.num, s.ch, s.p.x, s.p.y, s.d);
	printf("%d %c %d %d %f\n", s2.num, s2.ch, s2.p.x, s2.p.y, s2.d);
}

4、结构体重难点内存对齐

牺牲空间以换取时间、效率。

由于结构体存在内存对齐,我们在计算结构体大小时,不能简单的将其成员的类型所占大小简单的加和,而要考虑内存存储,以及CPU计算的知识。

结构体内存对齐规则:

1、结构体的第一个成员对齐到结构体在内存中存放位置的0偏移处。

2、对于之后的成员,每个成员都要对齐到一个对齐数的整数倍处。

这个对齐数是  成员自身大小和默认对齐数的较小值。

在VS环境下,默认对齐数是8,在Linux和gcc环境下无默认对齐数(为成员自身大小)

3、结构体的总大小为所有成员最大对齐数的整数倍

4、如果结构体中嵌套了结构体,要将嵌套的结构体对齐到它自己的成员中最大对齐数的整数倍处

第一个成员c1放在0偏移处(灰色部分)。 

i大小为4,默认对齐数为8,较小值为4,因此i对齐到4的倍数,从4开始存储(黄色部分)。

而蓝色部分123的空间会被浪费掉。(蓝色)

c2大小为1,因此对齐到1的倍数,即接着存放,放到8的位置(红色部分)。

此时结构体占用了9个字节。

所有成员的最大对齐数为4,结构体总大小为其整数倍,最后得到12byte

9-11的空间会被浪费掉(蓝色)

为观察成员在内存中的存储,我们可以用offsetof宏计算成员相对起始位置的偏移量。

 

简单记忆:先取小,再对齐存放,再取大,求总大小。 

结构体中包含一个结构体

内部的那个结构体,按照其成员中最大的对齐数 的整数倍来对齐。

最终结构体的总大小是所有对齐数中最大的那个的整数倍。

5、内存对齐的原因 

1. 平台原因(移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特
定类型的数据,否则抛出硬件异常。

2. 性能原因:
数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访
问。

总结:除移植性外,内存对齐是为了读取时尽量一次读取完整,或每次读取都是有效的数据,当CPU进行无数次访问时,就会有效地提高性能、效率,空间换取时间。

当然,当成员顺序没有更多要求时,为了不浪费过多空间,我们在对结构体成员顺序的涉及上,可以将占用内存较小的成员集中放在一起,相当于将这些小成员替换到那些浪费的空间中。

6、修改默认对齐数

前面我们知道,VS中默认对齐数是8。但是某些情况下,经过我们的计算,可以将其修改。

利用#pragma预处理指令。

#pragma()为取消默认对齐数,里面放多少,就把默认对齐数修改为多少。

修改为1后,每个成员都是连续存放(即没有对齐)。当然,这种操作后可能会产生移植性的问题,以及性能降低的问题,为了避免,需要我们程序员进行相应的计算(按需更改)。

7、结构体传参 

结构体成员一般较多,结构体占用的内存空间也较大。

传结构体时,用结构体接收。  形参接收后用.访问结构体的成员。

传结构体的地址时,用相应结构体的指针来接收。用->访问结构体的成员。

函数传参时,参数需要压栈,就会有时间和空间上的系统开销。当传递的结构体对象较大时,系统开销就较大,进而使得性能下降。

因此,结构体传参时尽量传递地址。

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值