C语言结构体详解

       

目录

       

        前言

        结构体

        结构体的声明

        结构体的匿名声明

        结构自引用

        结构体的定义和初始化

        结构体内存对齐(重要)

        为什么存在内存对齐?

        修改默认对齐数

        结构体传参


        前言

        本篇文章会介绍C语言中结构体的详细内容,以及结构体中最重要的部分(内存对齐)

        结构体

        结构体的声明

        结构体是一些的值的集合,这些值被称为成员变量。结构体的每个成员可以是不同类型的变量。(数组是一些相同类型的元素集合)

        如下图:结构体的声明:tag是结构体类型名称

        结构体的匿名声明

        但在匿名声明后,就不能在其他地方创建结构体变量了,只能在当前结构体花括号外面分号里面创建结构体变量,创建的这个结构体变量是全局变量。如下图所示:

        结构自引用

        结构体的自引用,就是链式访问的一种表现形式,开辟一个空间,一半存数据,一半存下一个数据的地址,这样我们就可以通过上一个数据来找到下一个数据的地址,从而形成链式访问。如下图所示:

        结构体的定义和初始化

        结构体的定义与初始化如下图所示:

上述情况是顺序初始化,还可以乱序初始化,只需要在创建的时候加个  .成员=内容。

        结构体内存对齐(重要)

        我们通过一个例题来了解结构体内存对齐。

        通过上述情况,我们可以发现,结构体内存并不是按照顺序,在内存中连续存放的,而是有一定的对齐规则。(偏移量我们能通过offsetof宏来找到,其头文件为:#include<stddef>使用方法为:offsetof (type,member),括号内是类型名,结构体成员)

对齐规则:

  1. 结构体第一个成员,用于放在相较于结构体变量起始位置偏移量为0的位置(即结构体首地址)
  2. 从第二个开始,往后每个成员都要对齐到某个对齐数的整倍数。对齐数:结构体成员自身大小和默认对齐数的较小值(vs上默认对齐数为8,gcc没有默认对齐数),对齐数就是结构体成员的自身大小。
  3. 结构体的总大小,必须是最大对齐数的整数倍(最大对齐数,是所以成员的对齐数的最大值,即结构体内最大的类型内存)
  4. 如果嵌套了结构体类型,嵌套的结构体对齐到自身最大对齐数的整数倍处,结构体的整体大小就是所以最大对齐数的整数倍。

        通过上述这个方法,我们来看第一个12是怎么得到的

        方法1:首先先存第一个成员char,他存储在偏移量为0的位置,接着我们要存int类型,所以我们要向后找到内被4整除的偏移量,最近的就是4,在4的偏移量处存下int的4个字节,所以是1+3+4=8,然后存char,由于char只占一个字节在偏移量为9的内存处,9又能被1整除,所以char能直接存储进去。最后由于结构体总大小要被最大对齐数整除,所以必须要加到12才能被4整除。

        方法2:我们可以另类来解读,首先char是一个字节,接着int类型是4个字节,1+4=5,这时由于第二点,5不是4的整数倍,要把5加到8,这三个空间就会加在char和int之间。最后还有和char类型,8+1=9,9能被1整除,不考虑第二点,但是结构体结束了,9又不能被最大对齐数4整除,所以又要向上加到12,才能被4整除,所以最终答案是12。

        具体内存情况如下图所示:

S1内存分布:

S2内存分布:

        对于对齐规则的第四点,我们可以看一个案例,如下图所示。

        当结构体内嵌套了结构体,依次往下计算,

        先算S3:8+1=9能被1整除,继续9+4=13不能被4整除,加到16,S3的最大对齐数是8,16能被8整除,所以S3的就占16个字节。

        接着看S4:1+16=17,根据第四条规则,由于S3的最大对齐数是8,所以我们要把17上升到能被8整除的数字,升成了24,再继续加上double,24+8=32,由于S4的最大对齐数是8,32也能被8整除,所以32就是S4所占的内存字节。

        为什么存在内存对齐?

  1. 平台原因(移植原因)

        不是所以硬件平台都能访问地址上的任意数据的;某些硬件平台只能在某些地址处取在某些特定类型的数据,否则会有硬件异常、

  1. 性能原因

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

        假如没有对齐内存,当我们要读取结构体的中间的某个变量时,由于32位机器一次只能读取4个,当int存在char后面时,读取第一次得到char的内容和int的前三个字节的内容,要读取第2次才能读取到i的全部内容,这样会消耗更多的时间。

         总的来说,内存对齐就是拿空间来换取时间的做法

        那在设计结构体变量时,我们如何做到满足对齐,又要尽量节约空间呢?我们只需要让空间小的成员尽量集中在一起。

        修改默认对齐数

        我们通过使用#pragma pack(内存数)来修改默认对齐数。如果我们把默认字节设置成1,#pragma pack(1),就是做到不对齐的效果

        结构体传参

        结构体传参也分为传值调用和传址调用,一般情况为了节约空间,我们都会选择传址调用,(传值调用会额外开辟空间,传址不会)具体使用方法如下,传址调用是传结构体变量的地址,形参用指针接收,想要打印成员时,用结构体形参的指针->成员变量。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值