深度剖析 —— 结构体

深度剖析 —— 结构体

系列文章目录

深度剖析:数据
深度剖析:递归
深度剖析:结构体
深度剖析:动态内存管理
深度剖析:文件操作
深度剖析:预处理


文章目录

  • 系列文章目录
  • 前言
  • 一、示例问题:创建通讯录
    • 1.通讯录的需求
  • 二、结构体
    • 1.建立结构声明
    • 2.创建结构变量
    • 3.初始化结构体
    • 4.访问结构成员
  • 三、结构体数组
    • 1.创建结构数组
    • 2.数组的初始化
    • 3.数组访问成员
  • 四、嵌套结构
    • 1.结构体的嵌套
    • 2.嵌套的初始化
    • 3.嵌套访问成员
  • 五、结构体指针
    • 1.创建结构指针
    • 2.指针的初始化
    • 3.指针访问成员
  • 六、结构体的计算
    • 1.空间换取时间
    • 2.结构体的存储
  • 总结

前言

在设计程序时,最重要的步骤之一是选择表示数据的方法。许多情况下数组已经部能很好的满足我们的需求,而结构变量通过创造新的形式提高了我们表示数据的能力。


一、示例问题:创建通讯录

1.通讯录的需求

通讯录就需要一种能包含数字的数据形式,而切要保持各信息的独立

信息

  1. 名字
  2. 年龄
  3. 性别
  4. 电话
  5. 地址

功能

  1. 增加
  2. 删除
  3. 查找
  4. 修改
  5. 排序

二、结构体

1.建立结构声明

结构声明(structure declaration)描述了一个结构组织布局

structPeoInfo{charName[8];charSex[4];intAge;charTelephone[12];charAddress[12];};

该声明描述了一个由四个字符数组和一个整型变量组成的结构。该声明并未创建实际的数据,只是描述了该对象由什么组成。
从本质上看,结构声明创建了一个名为struct PeoInfo的新类型(int double等类型等价) 。

struct

  1. 它表明其后的是一个结构
  2. struct PeoInfo则是这个结构的名称

2.创建结构变量

结构变量定义了一个struct PeoInfo类型的变量

structPeoInfoMail;

此时此刻才向堆栈申请了一块sizeof(struct PeoInfo)大小的空间(这里是为了便于理解所以恰好设置了40个字节的大小布局,后面会有结构体大小的计算详解)。

3.初始化结构体

struct PeoInfo类型也可以直接进行初始化

structPeoInfoMail={"小明","男",18,"11111111111","某小区"};

struct PeoInfoint等其他类型一样,可以直接进行赋值,它的内容需要由花括号括起来,各初始化项由逗号进行分隔。

4.访问结构成员

结构体成员的基本访问(对象.访问成员指针->访问成员)

printf("%s",Mail.Name);printf("%s",Mail.Sex);printf("%d",Mail.Age);printf("%s",Mail.Telephone);printf("%s",Mail.Address);

本质上Name、Sex、Age、Telephone、Address都是struct PeoInfo的下标,就像数组的下标0、1、2、3一样,数组是用[ ]来访问所在下标位置的元素, 而结构体则是用.->来访问成员。

三、结构体数组

1.创建结构数组

创建结构数组创建明其他类型的数组类似

structPeoInfoMail[100];

与其他类型一样,数组的每一个元素又是struct PeoInfo类型的元素。

2.数组的初始化

与其他数组一样只能在创建的时被直接初始化

structPeoInfoMail[20]={{"小明","男",18,"11111111111","某小区"},{"小红","女",18,"11111111111","某小区"}};

下面的这种写法是错误的!

Mail[0]={"小明","男",18,"11111111111","某小区"};//错误示例

3.数组访问成员

找到结构数组的某一个元素的下标进行访问

printf("%s",Mail[0].Name);printf("%s",Mail[0].Sex);printf("%d",Mail[0].Age);printf("%s",Mail[0].Telephone);printf("%s",Mail[0].Address);

四、嵌套结构

1.结构体的嵌套

当我们需求更丰富时就需要在结构体中嵌套一层结构体

//成员信息structPeoInfo{charName[8];charSex[4];intAge;charTelephone[11];charAddress[13];};//通讯录structContact{structPeoInfoData[MAX];//存放增加进来的人的信息(此处为结构体的嵌套)intcount;//记录当前通讯录中有效信息的个数}Contact;

2.嵌套的初始化

嵌套式结构体建议一个一个进行赋值而非直接初始化

//方法一strcpy(Mail.Data[0].Name,"小明");strcpy(Mail.Data[0].Sex,"男");Mail.Data[0].Age=18;strcpy(Mail.Data[0].Telephone,"11111111111");strcpy(Mail.Data[0].Address,"某小区");//方法二scanf("%s",Mail.Data[0].Name);scanf("%s",Mail.Data[0].Sex);scanf("%d",&Mail.Data[0].Age);scanf("%s",Mail.Data[0].Telephone);scanf("%s",Mail.Data[0].Address);

当结构体过于复杂时,不建议像下面代码这样直接初始化,当嵌套更加复杂时,直接初始化会非常困难。

structContactMail={{{"小明","男",18,"11111111111","某小区"},{"小红","女",18,"11111111111","某小区"}},20};

3.嵌套访问成员

嵌套成员依然是一层一层的去访问(与前面一模一样)

printf("%s",Mail.Data[0].Name);printf("%s",Mail.Data[0].Sex);printf("%d",Mail.Data[0].Age);printf("%s",Mail.Data[0].Telephone);printf("%s",Mail.Data[0].Address);

五、结构体指针

1.创建结构指针

指向结构体的指针

structContact*PMail;

2.指针的初始化

当结构体过于复杂时建议一个一个进行赋值

//方法一 (结构体过于复杂时不推荐该方法)structContactMail={{{"小明","男",18,"11111111111","某某小区"},{"小红","女",18,"11111111111","某某小区"}},20};structContact*PMail=&Mail;//方法二strcpy(PMail->Data[0].Name,"小明");strcpy(PMail->Data[0].Sex,"男");PMail->Data[0].Age=18;strcpy(PMail->Data[0].Telephone,"11111111111");strcpy(PMail->Data[0].Address,"某小区");//方法三scanf("%s",PMail->Data[0].Name);scanf("%s",PMail->Data[0].Sex);scanf("%d",&PMail->Data[0].Age);scanf("%s",PMail->Data[0].Telephone);scanf("%s",PMail->Data[0].Address);

3.指针访问成员

访问时可以*指针.->直接访问

printf("%s",PMail->Data[0].Name);printf("%s",PMail->Data[0].Sex);printf("%d",PMail->Data[0].Age);printf("%s",PMail->Data[0].Telephone);printf("%s",PMail->Data[0].Address);

六、结构体的计算

1.空间换取时间

效率至上:在尽可能不浪费空间的情况下,尽可能的提高运行的效率来达到我们想要的结果。所以在结构体的存储方面我们选择牺牲较少的空间,来降低计算机计算的时间复杂度,便于更快的运行程序。

2.结构体的存储

存储法则
  1. 第一个成员在与结构体变量偏移量为0的地址处。
  1. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。(对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值)。
  1. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
  1. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处。
  1. 结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
  1. 计算结构体的偏移量:offsetof (type, 成员)
  1. 修改默认最大对齐数8:#pragma pack(最大对齐数)(一般设置为2的n次方)
//计算下面代码的大小structBook{chartitle[3];intcount;shortvalue;};

题解
int的对齐数是4,而char占了3个字节,显然不是4的整数倍,于是浪费1个空白字节从而凑出4个字节恰好为int对齐数4的整数倍,此时此刻占了8个字节。short的对齐数是2个字节,8恰好是2的整数倍,所以short紧跟在int后面占2个字节,此时占用10个字节,但10并不是最大对齐数4(默认对齐数和int的对齐数取较小值)的整数倍,所以还要再浪费2个空白字节,最终结构体占用12个字节的大小。

总结

结构体提供了再相同的数据对象中存储多个不同类型数据项的方法。可以创建一个具体的结构模板,并声明相应的变量。通过.运算符和->运算符可以访问结构中的各个成员。它使得我们在处理问题时有更佳灵活的方式去解决。