⾃定义类型:结构体

本文详细介绍了C语言中结构体的声明、变量创建与初始化方法,结构成员的访问操作,以及结构体的内存对齐规则,讨论了结构体传参的性能影响和位段的特性及其跨平台问题。
摘要由CSDN通过智能技术生成

一.结构体类型的声明

1.1结构体的引入

C语言提供了内置类型如:char、short、int、long、float、double等,这些通常用来修饰变量,当我们想用多个内置类型来修饰一个变量时,这些远远不够,假如我想描述一个学生,这时需要他的学号(字符串)、姓名(字符串)、年龄(整形)等,这些数据类型不同但又要结合到一起,这时我们就需要引入一个⾃定义类型----------结构体。结构是⼀些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量如:标量、数组、指针,甚⾄是其他结构体。

1.2结构体的声明

这个声明描述了一个由字符串表示的名字、整形表示的年龄、字符串表示的学号组成的结构体,注意声明结构体时并没有创建空间,只是描述了一个结构体,所以又称为模板。只有结构体变量才分配地址,而结构体的定义是不分配空间的。

下面介绍一下结构体:

1.struct是关键字,表示是一个结构体。

2.student是结构体标签名,struct student就是结构体类型。

3.接下来就是一个括号,括起了结构体成员列表,及就是每个成员变量,使用的都是其自己的声明方式来描述,用分号来结束描述

1.3结构体变量的创建与初始化

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

之前结构体类型的声明相当与只是告诉编译器一个结构体,并没有创建空间,这时我们需要创建结构体变量才能使用结构体,创建一个结构体变量;struct student s;s就是结构体变量,这里的struct student就和int等基本数据类型一样。

这里有两种结构体变量创建的方法,结果是一样的。

1.3.2结构体变量的初始化

1.3.2.1按照结构体成员的顺序初始化

这种方法与基本数据类型和数组类型的初始化相似,也是使用花括号括起来,用逗号分隔的初始化好项目列表,注意每个初始化项目必须要和要初始化的结构体成员类型想匹配。

struct Student s = { "张三", 20, "20230818001" };

 1.3.2.2按照指定的顺序初始化

这种方法可以随意按顺序初始化,方式比较简单 , 点操作符(.)+成员名即可

struct Student s2 = { .age = 18, .name = "lisi", .student card = "20230818002"}

1.4结构成员访问操作符

1.4.1结构成员的直接访问

结构体成员的直接访问是通过点操作符(.)访问的。点操作符接受两个操作数,使⽤⽅式:结构体变量.成员名

1.4.2结构体成员的间接访问

有时候我们得到的不是⼀个结构体变量,⽽是得到了⼀个指向结构体的指针。

使⽤⽅式:结构体指针->成员名

1.5结构体的特殊声明

在声明结构的时候,可以不完全的声明,又称为匿名结构体

上⾯的一个结构在声明的时候省略掉了结构体标签(tag)

//注意这里不再是定义声明结构体类型,而是直接创建结构体变量了,这个编译器会分配内存的;

//这样的确可以省略结构体标签也就是结构体名,但是只能使用一次;

1.6结构体的自引用

在结构中包含⼀个类型为该结构本⾝的成员是否可以呢?

当结构体内包含一个结构体时,sizeof(struct Node)是否能算出来呢?答案是不能,仔细想想一个结构体内包含一个结构体时,一直这样那他的大小是无尽的。

那我们如何成功的自引用呢?这时候就要运用到指针。

二.结构体的内对齐

结构体学到现在我们怎么计算结构体的内存呢?

2.1结构体对齐规则

1. 结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处

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

对⻬数 = 编译器默认的⼀个对⻬数 与 该成员变量⼤⼩的较⼩值。

VS 中默认的值为 8 。- Linux中 gcc 没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩

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

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

例题举例

结构体第一个成员在偏移量为0处占一个字节空间,第二个成员的对齐数为4所以放置的地方为对齐数的整数倍,即是偏移量为4的地方,占用4个字节,第三个成员对齐数为1,则放在偏移量为8的地方,结构体总⼤⼩为最⼤对⻬数的整数倍,所以该结构体的大小为12个字节

注意虽然其中有的空间没放字节,但也属于结构体的内存。

2.2为什么存在内存对⻬?

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

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

2. 性能原因:

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

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

那在设计结构体的时候,我们既要满⾜对⻬,⼜要节省空间,如何做到:

让占⽤空间⼩的成员尽量集中在⼀起

2.3修改默认对⻬数

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

结构体在对⻬⽅式不合适的时候,我们可以⾃⼰更改默认对⻬数。

三.结构体传参

有以种两种传参方式,⾸选print2函数

原因:

函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。

如果传递⼀个结构体对象的时候,结构体过⼤,参数压栈的的系统开销⽐较⼤,所以会导致性能的下降。

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

四. 结构体实现位段

4.1 什么是位段?

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

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

2. 位段的成员名后边有⼀个冒号和⼀个数字(作用是表示该成员的二进制保存多少位)。

如int  a=2 对应的二进制为0000 0000 0000 0010.     int a:4;即二进制保留为   0010

4.2 位段的内存分配

1. 位段的成员可以是 int unsigned int signed int 或者是 char 等类型

2. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的⽅式来开辟的。

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

4.3位段的跨平台问题

1. int 位段被当成有符号数还是⽆符号数是不确定的。

2. 位段中最⼤位的数⽬不能确定。(16位机器最⼤16,32位机器最⼤32,写成27,在16位机器会 出问题。

3. 位段中的成员在内存中从左向右分配,还是从右向左分配,标准尚未定义。

4. 当⼀个结构包含两个位段,第⼆个位段成员⽐较⼤,⽆法容纳于第⼀个位段剩余的位时,是舍弃 剩余的位还是利⽤,这是不确定的。

总结: 跟结构相⽐,位段可以达到同样的效果,并且可以很好的节省空间,但是有跨平台的问题存在。

  • 46
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值