自定义类型——结构体类型

本篇将讲解结构体的基本知识,及其的注意点和基本使用


目录

结构体

 结构体定义

结构体变量的定义和初始化

结构访问操作符

结构体内存对齐(计算结构体大小)

内存对齐存在原因

修改默认对齐数


结构体

结构体是一种自定义类型的数据,数组也是一种自定义类型,可以用数组作类比理解结构体,不过数组里的成员只能定义个数和类型,而结构体内可以定义任意类型的成员,也就是结构体内可以装任意类型的数据,并且这些数据可以是不同类型的

结构体的关键字是 struct


 结构体定义

struct tag

{

member-list;

}variable-list;

tag 是自己写的一个标签(用来表示这个结构体是关于什么的)结构体类型就为 strcut tag

tmamber-list 是成员列表,括号里就是结构体里包含哪些类型的数据(注意:这里只是在定义结构体是怎么样的,只需要声明它有哪些类型的数据,不需要给这些数据创建变量

variable-list 是在定义结构体时顺便也创建类型为这个结构体的变量,可以一次性创建多个,在这里创建是则创建的一个全局变量,当然也可以在其他地方创建

注意:结构体定义最后面的 ' ; ' 号不可以省略

特殊的声明的结构体:省略掉标签(tag),此为匿名结构体,若没有对此结构体类型重命名的话基本上只能使用一次

例:

struct student  - 定义一个关于学生信息的结构体,信息包含学生的年龄、名字、学号
{
    int age;
    char name[50];
    int ID;
}a,b,c;  - 并创建三个类型为 struct student 的变量

int main()
{
    struct student d;  - 再创建一个局部变量 d
}


结构体变量的定义和初始化

像上面所说, 定义中 variable-lsit 就是变量的创建,且为全局变量

其他变量定义:struct tag(类型) s1(变量名) = {};放函数里即为局部变量,s1 即是一个变量,类型为 struct tag

变量的初始化分两种:默认顺序初始化 和 指定顺序初始化

例:

struct student 
{
    int age;
    char name[50];
    int ID;
}a = {17, "zhangsan", 101},b,c;  - 创建全局结构体变量 a 的同时并将它按默认顺序初始化  

int main()
{
    struct student d = { .ID = 103, .age = 20, .name = "lisi"};  - 按我们自己指定的顺序初始化
}

结构访问操作符

 点操作符:.

箭头操作符:-> (由一个减号(-) 和 大于符号(>) 组成)

作用:

 直接访问:结构体成员的直接访问是通过点操作符(.)访问

点操作符接受两个操作数 —— 结构体变量 . 成员名

间接访问:结构体指针 -> 成员名

例:

struct student  
{
    int age;
    char name[50];
    int ID;
}a,b,c;  

int main()
{
    struct student d; 
    stuct student* pd = &d;  - 创建指针变量 pd,并取出 d 的地址赋值给 pd
    pd->age = 17;  - 指针 pd 通过箭头操作符访问其指向的结构体变量的成员 age,并赋值 17
    d.name = "头发化码";  - 变量 a 通过点操作符访问其成员 name,并赋值 头发化码
}

通过上面的例子,你一定对结构访问操作符有一定理解了,不过其实这个例子里面有个地方是错误的,这也是使用结构访问操作符需要注意的一个小细节,具体如下

 在之前变量的定义和初始化中,我举了一个这样的例子:
struct student d = { .ID = 103, .age = 20, .name = "lisi"};  - 这段代码是没问题的

刚刚上面的例子中又有这样一段代码:
d.name = "头发化码";  - 其实这段代码是错误的

注意 .name = "lisi" 和 d.name,为什么前面一个是对的,而后面一个是错误的呢,这里并不是因为 . 前面有差异而导致错误的,而是两段代码所处位置导致含义的不同,前者是在创建结构体变量的同时将结构体的成员初始化,这时这些结构体成员里的值就是这些初始值,而后者是通过点操作符访问到 name 这个成员,而 name 是一个数组,它在这里表示的是首元素地址,首先它是指针常量,是不可以赋值的,其次就算能赋值,也不能达成我们想要的效果,因为他只是一个地址,而不是我们想要的地址内的空间

当然,这个注意点只针对数组成员,因为其他类型数据的变量名在不同地方表示的含义是相同的,只有数组的变量名是要分情况确定它的含义,想具体了解的小伙伴可以点击我看”数组名“的内容


结构体内存对齐(计算结构体大小)

 结构体是存在一个叫内存对齐的规则的,它规定了结构体内的成员是如何分配空间的,并决定了整个结构体的大小

在讲解对齐规则前先补充一个对齐数的知识

对齐数 = 编译器默认的一个对齐数与该成员变量大小的较小值(VS 中默认为 8,Linux 中 gcc 没有默认对齐数,对齐数就是成员自身大小),比如一个 int a,它的大小为 4(字节),在 VS 当中默认对齐数为 8,4 < 8,所以成员 a 的对齐数为 4

 对齐规则:

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

2.其他成员对齐到其对齐数的整数倍的地址处

3.结构体总大小为最大对齐数(这个结构体所有成员中最大的)的整数倍

4.若嵌套了结构体,这个嵌套的结构体成员的对齐数为其自己成员中的最大对齐数

这里我们举两个例子:

                                                                例一

                                                                例二  


内存对齐存在原因

1.平台原因(移植原因):不是所有硬件平台都能访问任意地址上的任意数据的,某些硬件平台只能在某些特定地址处取某些特定类型的数据。例:只能从 4 的倍数的地址上取整数

2.性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐,因为对于未对齐的内存,处理器需要进行两次内存访问。例:假设一个处理器总是从内存中取 8 个字节,也就是处理器是按 8 个字节的内存一块一块处理的,若一个数据分布在两个块中,处理器则需要取出这两个块才能取出这个数据

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


修改默认对齐数 

预处理命令:#pragma pack()

修改默认对齐数为括号里的数,若不填则还原为默认对齐数


最后,这里再附上其他几个自定义数据类型 :

联合体类型与枚举类型

数组

  • 22
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值