C语言 结构体

结构体声明

在我们平时使用数据的时候会给这个数据做一个声明,声明也是一种定义,定义这个值是什么类型的,这个数据也可以是一种形象的代指,代指着某一个现实的事物,如我们使用的ASCII码表

我们将各种字符转换成数字交给计算机处理。

但是在日常中很多事物很复杂,无法用简单的数字去表示,所以C语言中有结构体,结构体是多个值的集合,能让我们使用多个值去表示一个事物。

如图,这是一个结构体的完整声明,这个结构体里面包含一个整形的age和一个char[10]类型的class。结构体的类型在定义之后才会成型,此结构体的类型st就是struct students关键字是ruct,students这个是可以随着创建的结构体所指代的事物而更改的名字,在声明结构体时在类型前加上typedef可以给这个结构体类型进行重命名让我们调用此结构体时更方便,stu就是这次给这个结构体类型重新定义的名字,以后使用只需要直接结构体类型stu加上创建的变量名字如:stu s  即可。

特殊声明

上述的声明是正常的声明,除此结构体还有一种特殊的声明方式。

这种结构体声明是一种不完全声明它的类型就是struct若是我们程序有两个没有重命名的不完全声明结构体的话编译器是无法辨认两个结构体而出错。

我们也可以给这个结构体进行重命名而正常使用,不过还是建议每个结构体都要正常声明。

结构体初始化和赋值

因为结构体声明本质只是定义了这些个数据的类型而已,本身不会在内存中开辟空间,当我们使用时需要定义一个结构体的对象

如图,p就是我们定义的这个结构体类型的对象,这时才会真正分配空间。图上我们在定义的同时也给结构体内的变量进行了赋值,初始化时赋值可以使用{}直接赋值用,隔开每个值。

若在定义结构体对象时没有进行赋值也可以如下图进行赋值。

结构体成员访问符

对结构体的成员访问有两种方式一个时使用符号‘.’这个访问符能让我们直接用结构体名字直接访问内部成员变量,还有一种时通过符号‘->’的方式访问 如图

这三种方式其实都是等价的,图中我们定义了一个指向结构体s的指针,在对指针进行解引用在使用‘.’的方式去访问这个结构体成员变量过于麻烦,所以C语言中提供了‘->’符能让我们直接使用指针访问结构体内部成员变量。

结构体自引用

在数据结构的创建中我们不可避免的需要使用到不连续空间存储数据,但是又需要这个数据要具有连续性所以链表就必不可少,链表的创建就是使用的结构体自引用。

结构体的自引用并不是想递归一样对自身的调用,而是对同一结构体类型的调用。

图中我们在结构体中定义了一个同类型结构体的指针,这个指针就可以指向任何相同类型的结构体也可以指针自身,这样我们就可以使用结构体内的成员变量去调用自己。

若我们创建了多个同类型的结构体我们还可以将这个指针指向另一个结构体,使多个结构体进行逻辑上相连接的形式,那我们只需要调用其中一个结构体遍能遍历所有相连的结构体了。

结构体的内存空间对齐

结构体是多个值的集合,那么结构体的创建的时候是否按照变量的大小去分配空间的呢。如图

int的大小是4个字节,char的大小是1个字节,指针的大小是8个字节(64位)如果是相加的话理应是13个字节但实际这个结构体有16个字节,这是结构体的内存对齐。如下图

实际情况是结构体的内存创建与使用考虑到不同计算机对数据传输方式不同而设计了一套内存对齐规则,并不是所有计算机都能随意访问所有地址空间,受限硬件的约束有些计算机会以2的倍数,4的倍数或者8的倍数去连续读取多个字节,若是上图中的指针紧接着变量a存入地址5—12中的话那需要进行两次读取并且需要将数据进行拼接,所以设计了如下规则:

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

2. 其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。 对⻬数 = 编译器默认的⼀个对⻬数 与 该成员变量⼤⼩的较⼩值。

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

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

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

对默认对齐数的修改

系统提供的默认对齐数是可以使用预处理指令修改的,预处理指令由于在预处理阶进行就能在结构体创建之前修改。

#pragma pack(1)  括号中的数字便是修改后的默认对齐数。

结构体传参

当我们使用函数需要传结构体时最好的方式时传指针。

函数的传参我们知道若是直接传结构体那么函数得到的是一个结构体的一份临时拷贝,同时这份临时拷贝是需要在函数栈上进行压栈的,内存开销大的同时若我们还需要对结构体内的值进行修改的话我们修改的只是那份临时拷贝,并没有修改到原本的结构体。传指针就不需要担心,指针都是固定大小同时也可以通过指针访问原本的结构体进行修改。

结构体实现位段

位段的使用是为了让结构体节省一定空间,但是这个泛用性不强,在跨平台的计算机上很容易出现错误。

如上图,我们创建了三个整形变量,但是整个结构体的大小只有4个字节。结构体中数字表示的是这个变量只会占3个bit位。

位段会创建一个共用的空间,这个空间会依据你的变量类型创建,当这个空间足够放下所有的变量时就不会继续开辟空间了。

但是当这个空间不够放下下一个变量大小的时候就会继续创建变量。

这个图中我们创建了一个整数数始化为0,然后我们按照位段的存储方式分别传入三个数,得到的结果时只有第一段的的空间是有数值的,并数值是乱的,这是由于位段传输是按照bit位传输的整形共有32个bit位所以a和b共占数组第一个整形的位置

所以实际上数组第一个值是图中左边这个二进制数,这是a与b拼接出来的结果,由于我这是小端电脑,所以在栈中处于小地址位的变量b拼接到了高位就是后面接的是占30个bit位的变量a(数值1)。

由于位段是对Bit位进行操作所以不确定因素太多,电脑大小端,cpu的读取方式都有影响所以实用型太差,最后还是不建议使用结构体实现位段。位段是更多用于网络传输中的技术。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值