c语言自定义类型:结构体

一. 结构体类型的声明

二. 结构体变量的创建和初始化

三. 结构体内存对⻬

四. 结构体传参

1.结构的声明

struct tag
{
 member-list;

}variable-list;

例如描述⼀个学⽣:

struct Stu
{
	char name[20];//名字 
	int age;//年龄 
	char sex[5];//性别 
	
};

2. 1结构体变量的创建和初始化

2. 2结构的特殊声明

在声明结构的时候,可以不完全的声明。

struct
{
	int a;
	char b;
	float c;
};

例如上图,但是我们要知道匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使⽤⼀次

3. 1结构体内存对齐

这是结构体中非常重要的知识对⻬规则

 ⾸先得掌握结构体的对⻬规则:1.结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处

2.其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。

对⻬数=编译器默认的⼀个对⻬数与该成员变量⼤⼩的较⼩值。VS 中默认的值为 8  -Linux中gcc没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩

3.结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的 整数倍。

4.如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构 体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。

通过一段代码来深入理解结构体内存对齐吧

用画图的方式来分析

struct S1
{
 char c1;
 int i;
 char c2;
};
printf("%d\n", sizeof(struct S1));

结构体变量S在内存中开辟出一块空间, 结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处,char类型占一个字节,占的空间如图所示

2.其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。也就是说i变量要对齐到(对⻬数=编译器默认的⼀个对⻬数与该成员变量⼤⼩的较⼩值)的整数倍的地址处,而vs中的值为8,i变量大小是4,二者较小值为4,所以要对其4的整数倍的地址处,1.2.3.4在4处开始存放大小为4个字节的空间,如图所示

3.c2变量大小为一个字节,与8对比(vs中对齐数为8),二者较小值为1,对齐到1的整数倍的地址处,任何数都是1的整数倍,所以直接往下一个地址处存放就行了。

4.结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的 整数倍。上述3个成员变量的对齐数分别是1、4、1其中4是最大的,所以总大小应该为4的整数倍4、8、12,4和8都不能存入(前面已经使用了9个字节的空间了),所以总大小应该为12。 

接下来如果碰见了嵌套型的结构体该如何去做呢,也用一个代码来分析

struct S3
{
 double d;
 char c;
 int i;
};
printf("%d\n", sizeof(struct S3));

struct S4
{
 char c1;
 struct S3 s3;
 double d;
};
printf("%d\n", sizeof(struct S4));

1.首先我们去计算S3的大小,第一个double类型变量存入在偏移量为0的地址处,大小是8,第二个char类型变量大小为1,存入到对齐数为1的整数倍的地址处,也就是向下紧接着存一个字节的空间,第三个int类型变量大小为4,存入到对齐数为4的整数倍的地址处,因为此时所占空间是9,不满足4的整数倍,所以要跳过3个字节处在开始存放变量大小为4个字节的空间。现在所占用的大小是16个字节,而总大小是最大对齐数的整数倍,分别是8、1、4其中8最大,所以总大小为8的整数倍,16正好满足,所以该结构体的总大小为16

2.计算完S3大小以后,我们来看这个嵌套类型,首先char类型存放一个字节,然后存放结构体类型,如果出现了嵌套结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,也就是S3的成员,很明显最大对齐数是8,从0开始偏移8个字节存入,完成以后再存double类型变量,对齐数为8且此时正好所占字节数为24,紧接着向下存入大小为8个字节的空间,现在占用32个字节。存完以后,结构体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。也就是包含S4中成员的对齐数,S3:8、4、1;S4:1、8     所以最大对齐数还是8,8的整数倍为32满足条件,所以该结构体的总大小为32,图示如下

3.2 为什么存在内存对⻬?

⼤部分的参考资料都是这样说的:

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

 2. 性能原因: 数据结构(尤其是栈)应该尽可能地在⾃然边界上对⻬。原因在于,为了访问未对⻬的内存,处理器需要 作两次内存访问;⽽对⻬的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节,则地 址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对⻬成8的倍数,那么就可以 ⽤⼀个内存操作来读或者写值了。否则,我们可能需要执⾏两次内存访问,因为对象可能被分放在两 个8字节内存块中。

总体来说:结构体的内存对⻬是拿空间来换取时间的做法。

那在设计结构体的时候,我们既要满⾜对⻬,⼜要节省空间,如何做到: 让占⽤空间⼩的成员尽量集中在⼀起

struct S1
{
 char c1;
 int i;
 char c2;
};
struct S2
{
 char c1;
 char c2;
 int i;
};

S1 和 S2 类型的成员⼀模⼀样,但是 S1 和 S2 所占空间的⼤⼩有了⼀些区别。

3.3修改默认对⻬数

 #pragma 这个预处理指令,可以改变编译器的默认对⻬数。

#include <stdio.h>
#pragma pack(1)//设置默认对⻬数为1 
struct S
{
 char c1;
 int i;
 char c2;
};
#pragma pack()//取消设置的对⻬数,还原为默认 
int main()
{
 printf("%d\n", sizeof(struct S));
 return 0;
}

这段代码最后打印的结果是6, vs本来对齐数是8,现在变为1了,所以当int类型变量存入时的地址就发生了改变,原本的对齐数的较小值应该为4,现在的对齐数较小值为1了所以可以紧接着存放,图示如下

4. 结构体传参

 这是结构体传参的两种方式,这两种方式哪个更好呢?答案是第二种结构体地址传参更好。为何呢?

原因: 函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。 如果传递⼀个结构体对象的时候,结构体过⼤,参数压栈的的系统开销⽐较⼤,所以会导致性能的下降。

结论: 结构体传参的时候,要传结构体的地址。

以上就是c语言中自定义类型之结构体的一些知识点的梳理和理解了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值