C语言零基础第18讲:自定义类型—结构体

目录

1.结构体类型的声明

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

3.匿名结构体类型

4.结构体的自引用

5.结构体内存对齐

[5.1 对齐规则](#5.1 对齐规则)

[5.2 为什么要内存对齐?](#5.2 为什么要内存对齐?)

[5.3 修改默认对齐数](#5.3 修改默认对齐数)

6.结构体传参

7.特殊的结构体:位段

[7.1 什么是位段?](#7.1 什么是位段?)

[7.2 位段的内存分配](#7.2 位段的内存分配)

[7.3 位段的跨平台问题](#7.3 位段的跨平台问题)

[7.4 位段的应用](#7.4 位段的应用)

[7.5 位段使用的注意事项](#7.5 位段使用的注意事项)


正文开始

1.结构体类型的声明

结构体时是一些值的集合,这些值称为成员变量。每个成员可以是不同的类型。

结构体是集合,数组也是集合。结构体有成员,数组也有成员(即元素)。结构体和数组的区别在于,结构体的成员可以是不同的类型,数组的成员必须是同一类型的。

我们来看看结构体的声明:

复制代码
struct tag            //struct是关键字
{                     //tag是类型名,这个名字可以自己取   

    member-list;      //成员列表:可以有1个或多个成员  

}variable-list;       //变量列表,可以省略 

比如,描述一个学生:

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

3.匿名结构体类型

我们再来看一种特殊的声明方式,匿名结构体类型:

我们再看一种情况:

所以,需要注意的是,匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使用一次

4.结构体的自引用

在结构体中,包含一个类型为结构体本身的成员,是否可以呢?请看解析:

如上,在结构体中,不能包含一个类型与该结构体相同的结构体

那么,正确的自引用方式是什么呢?

在介绍自引用之前,我们先简单提一下链表的概念。我们知道,数组中的各个元素,在内存中是连续的。而对于链表,各个数据在物理上并不是连续存储的,那它是怎么存储的呢?每个数据分为两个部分,其中一个部分用来存储数据本身,另一个部分用来存储下一个数据的地址,这样就可以通过一个元素找到下一个元素了。请看图示:

我们来看一下,结构体自引用示例,代码如下:

需要注意的是,在结构体自引用的过程中,如果夹杂了typedef对结构体类型的重命名,结构体内部的指针域,是不能使用重命名后的名字来声明成员的 。请看解析:

5.结构体内存对齐

计算结构体的大小,要涉及到结构体的内存对齐

5.1 对齐规则

我们先通过一个代码来引入这个问题:

如上,S1和S2两个结构体类型,成员变量只有顺序的差异,而计算出的结构体大小却不相同。这是对齐方式不同导致的结果。

在这里,我们先给出对齐规则,然后再对这些规则进行理解:

  1. 结构体的第1个成员,对齐到结构体变量相对于起始位置偏移量为0的地址处,即结构体的首地址处。
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的偏移量处。
  3. 对齐数 :用编译器默认的一个对齐数,与该成员的大小作比较,谁更小,谁就是对齐数。VS中msvc默认为8,Linux中gcc没有默认的数,对齐数就是成员自身的大小。
  4. 结构体中每个成员都有对应的对齐数,取最大的那个对齐数,结构体总大小为这个最大对齐数的整数倍。
  5. 如果嵌套了结构体的情况,嵌套的结构体成员,对齐到自己成员中最大对齐数的整数倍处,结构体的整体大小是所有对齐数(含嵌套结构体中成员对应的对齐数)中最大对齐数的的整数倍。

以S1为例,我们来看看图示:

如上,c1放在偏移量为0处,c2放在偏移量为1处,n放在偏移量为4处。到底对不对呢?

我们可以利用offsetof来进行检验。offsetof是一个宏,可以计算出结构体的成员,相较于结构体起始位置的偏移量,对应的头文件是stddef.h

请看S1的检验:

如上,结果和我们分析的是一样的,S1的大小也确实为8。

我们再看看S2的情况:

使用offsetof检验一下:

如上,结果和我们分析的是一样的,S2的大小也确实为12。

我们再来看看,嵌套结构体的情况:

我们来看看图示:

5.2 为什么要内存对齐?

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

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

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

2.性能原因:

数据结构(尤其是栈),应该尽可能地在自然边界上对齐。原因在于,假设一个处理器,每次总是从内存中取出8个字节,这就意味着,地址一定是8的倍数。在这种情况下,若一个double类型的数据,它的地址是不对齐的:7、8、9、10、11、12、13、14,而处理器一次取出8个字节,就得先取0、1、2、3、4、5、6、7中的数据,再取8、9、10、11、12、13、14、15中的数据,这就需要访问2次内存了。如果地址是对齐的:8、9、10、11、12、13、14、15,处理器一次取8个,刚好仅需访问1次内存即可。

3.总结:

结构体的内存对齐,是拿空间换时间的做法。

4.启示:

那么,在设计结构体的时候,我们应该尽可能地,既满足对齐, 又节省空间。

做法就是,让占用空间小的成员,尽量集中在一起:

5.3 修改默认对齐数

前面提到过,VS的msvc中,默认对齐数是8,成员要和这个默认对齐数去做比较,谁更小,谁就是对齐数。

那么,这个默认的对齐数,可以修改吗?当然是可以的。

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

6.结构体传参

请看第一种传参方式:

如上,结构体中date成员所占空间非常大,那么这个结构体所占的空间也很大了。如果将结构体变量传给函数,那么形参也会申请这样大一块空间,同时数据的拷贝也需要花时间。因此,结构体在传参的时候,选择传结构体变量,在空间和时间上都会很浪费

请看第二种传参方式:

如上,结构体传参的时候,选择传结构体的地址比较好

7.特殊的结构体:位段

接下来,我们来讨论一下,结构体实现位段的能力。

7.1 什么是位段?

我们可以把位段看作一种特殊的结构体

位段的声明,和结构体是类似的,但有2个不同:

  1. 位段的成员必须是int、unsigned int或signed int,在C99中也可以选择其他类型。
  2. 位段的成员名后边,有一个冒号和一个数字。

我们来看一个例子:

上面的A就是一个位段类型。

可知,使用位段是可以节省空间的

上面的结果显示,位段A的大小为8个字节。那么,位段是如何存储的呢?

7.2 位段的内存分配

  1. 常见的位段成员有:int、unsigned int、signed int或char等类型。
  2. 位段的空间是按照需要,以4个字节(int)或1个字节(char)的方式来开辟的。
  3. 位段涉及很多不确定因素,是不跨平台的,注重可移植性的程序应该避免使用位段。

我们来看一个例子:

7.3 位段的跨平台问题

  1. int位段,被当成有符号数还是无符号数,是不确定的。
  2. 位段中最大位的数目不能确定。如16位机器最大16,写成30,就会出现问题。
  3. 位段中的成员在内存中从左向右分配,还是从右向左分配,标准尚未定义。
  4. 对于位段中第n个成员和第n+1个成员,在内存中,如果第二个位段成员比较大, 无法放进第一个位段剩余的位时,是否舍弃剩余的位,是不确定的。

总而言之,跟结构体相比,位段可以达到同样的效果,并且很好地节省空间,但是存在着跨平台的问题

7.4 位段的应用

下图是网络协议中,IP数据报的格式。我们可以看到,其中很多的属性只需要几个bit位就能够描述了。这里使用位段,不仅可以实现想要的效果,也节省了空间。这样,网络所传输的数据报也会较小一些,对网络的畅通是有帮助的

7.5 位段使用的注意事项

我们知道,内存中以字节为单位分配地址,一个字节内部的比特位,是没有地址的。

位段中,有时候可能几个成员是放在同一个字节中的。这样的话,有些成员的起始位置,并不是某个字节的起始位置,这些成员是没有地址的。

所以,不能对位段的成员使用取地址操作符(&),也就不能使用scanf()直接给位段的成员输入值,只能是先将输入放在一个变量中,然后赋值给位段的成员

我们来看一个示例:


完结

相关推荐
GUET_一路向前1 小时前
【C语言防御性编程】if条件常量在前,变量在后
c语言·开发语言·if-else·防御性编程
楼田莉子1 小时前
C++算法题目分享:二叉搜索树相关的习题
数据结构·c++·学习·算法·leetcode·面试
十一10241 小时前
FX10/20 (CYUSB401X)开发笔记5 固件架构
笔记
FakeOccupational2 小时前
【电路笔记 通信】AXI4-Lite协议 FPGA实现 & Valid-Ready Handshake 握手协议
笔记·fpga开发
小明的小名叫小明2 小时前
区块链技术原理(14)-以太坊数据结构
数据结构·区块链
pusue_the_sun2 小时前
数据结构——栈和队列oj练习
c语言·数据结构·算法··队列
Dontla2 小时前
Makefile介绍(Makefile教程)(C/C++编译构建、自动化构建工具)
c语言·c++·自动化
想不明白的过度思考者2 小时前
数据结构(排序篇)——七大排序算法奇幻之旅:从扑克牌到百亿数据的魔法整理术
数据结构·算法·排序算法
一支闲人3 小时前
C语言相关简单数据结构:双向链表
c语言·数据结构·链表·基础知识·适用于新手小白