对于结构体的一些了解

本文详细介绍了C语言中结构体的声明、创建、内存对齐原则、传参策略以及位段的使用,探讨了内存对齐对性能的影响,并强调了结构体传参时地址传递的效率优势。
摘要由CSDN通过智能技术生成

1、结构体类型的声明

首先我们要了解一下结构体的概念,结构是一些值的集合,这些值被成为成员变量。结构的每个成员都可以是不同类型的变量。

1.1、结构的声明

include "stdio.h"
int main()
{
    struct tag
    {
          member-list;
    }
}variable-list;

就像我们要描述一个人的信息时

我们描述一个人时,可以记录它的一些身份信息,这就是结构体的作用

1.2、结构体的创建与初始化

1.3结构体的特殊声明

在声明结构体时,也可以不完全声明

可以看到我们在定义结构体时,省略了struct后面的标签,这就叫匿名结构体,当我们进行第150行代码时,我们可以发现报错了,原因就是p与x两者的类型不是同一个类型,也就是说,匿名的结构体类型,如果没有对结构体重命名的话,基本上只能使用一次。

1.4结构的自引用

正常来说的话,是不能进行自引用的,应为结构体里面存在一个结构体的话,那么这个结构体的内存就是无穷大了,这样肯定是不行的。

如上图所示,这才是正确的引用,因为地址的大小我们是明确的,这样的情况才是正确的。

2、结构体的内存对齐

首先我们要先了解一下对齐规则

1、结构体的第一个成员对齐到和结构体变量其实位置偏移量为0的地址处。

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

对齐数 = 编译器默认的一个对齐数与该成员变量大小的较小值。

在vs里面默认是8

linux中的gcc里面没有对齐数,对齐数就是成员自身大小

3、结构体总大小为最大对齐数(结构体中每个成员都有一个对齐数)的整数倍。

4、如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍,结构体的整体大小就是所有最大对齐数的整数倍。

我们都知道在vs里面系统默认对齐数是8,而我们要去用成员的对齐数与默认数去进行比较,比默认大的话,则对齐数是vs默认对齐数8,比默认的对齐数小的话,则对齐数就是小的,也就是说(在vs里面对齐数最大就是8

了解的规则的话我们来做几道题目就知道了。

首先是练习一:首先char是占一个字节的,int占四个字节,所以c1放在第一个位置,但是int类型要对齐自己对齐数整数倍的地址,所以i的地址就放在第五个了,当放完i时,c2就应该放在i的后面,而结构体的总大小就是最大对齐数(int类型的对齐数最大为4)的整数倍。所以应该为12。

练习二:由图就可以很完整的看出来因为i的地址是对齐数的整数倍,所以i与c2之间应该相差两个地址,所以结构体大小应该为8

练习三:我们可以看到最大对齐数是double,对齐数为8,所以整个结构体应该是8的倍数,而由图可知,这个位置刚好是8的倍数,所以为16

练习四:

首先我们可以看到结构体里面有个结构体,这个结构体是struct3,它的大小为16个字节,但是它的对齐数是它本身成员里面最大的对齐数,也就是double,对齐数为8。由图就可以看出来,结构体4的最大对齐数是8,而占了刚好为32个字节。

接下来我们来看看答案。

2.1、为什么要存在内存对齐

1、平台原因

不是所有的硬件平台都能访问任意地址上的数据。某些硬件平台只能在某些地址访问某些特定的类型数据,否者就抛出硬件异常。

2、性能原因

数据结构(尤其是栈)应该尽可能地在⾃然边界上对齐。原因在于,为了访问未对⻬的内存,处理器需要
作两次内存访问;而对齐的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节,则地
址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对⻬成8的倍数,那么就可以
⽤⼀个内存操作来读或者写值了。否则,我们可能需要执⾏两次内存访问,因为对象可能被分放在两
个8字节内存块中。
总结来说就是:拿空间去换取时间的做法。
所以要想既节省空间又可以减少时间,就可以将内存小的放在一起,内存大的放一起。

2.2修改默认对齐数

由图可知,这里面的结构体的大小就发生了变化

3、结构体传参

由上图可知我们进行了两种传参,test1是进行地址传参,test2是进行结构体传参,这两种办法都可以输出我们想要的结构,那么两种那个更好用呢?首选第一种,第一种它是进行地址传参,这样可以大大减小内存从占用,如果我们选择第二种的话,它就会在栈中创建一个临时变量来接受结构体,这样会占用空间,所以我们通常选择传地址的方法去进行结构体的传参。

4.结构体实现位段

位段的声明和结构是相似的,由两个不同:

1、位段的成员必须是int,unsigned int,signed int,在c99中位段成员的类型也可以选择其他类型。

2、位段的成员名后由一个冒号和数字。

4.1位段的内存分配

1、位段的成员可以是int,unsigned int ,signed int,char等类型。

2、位段的空间按照需要以4个字节或者一个字节来开辟。

3、位段涉及很多不确定因素,位段是不跨平台的,注重可移植程序应该避免使用位段。

首先我们要了解,成员名后面的数字表示的是什么?是该成员的大小为几个字节。

如图所示,我们可以发现,由于a1的类型是int类型,所以先开辟一个四个字节的空间,也就是32bit位,又该成员a1的2个bit位,a2是三个bit位,a3是十个bit位,所以放不下a4的20个bit位,就需要另外开辟一个int类型的空间,也就是4个字节,32个bit位,所以该位段开辟了两次int类型的空间,也就是8个字节,我们来看下结果。

4.2位段使用时的注意事项

位段几个成员有时是共用一个字节的,这些的成员起始位置并不是某个字节的起始位置,,那么这些位置处是没有地址的。内存中的每个字节分配一个地址,但bit位不会分配地址

所以对位段成员不能使用&符号,也不可以用scanf直接个位段成员输入值,只能先输入一个变量,在赋值给位段的成员。

ok了,家人们,这就是我对结构体的一些理解了

  • 7
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值