自定义类型之结构体,枚举,联合(上)

简介: 自定义类型之结构体,枚举,联合

一、结构体


在初识结构体一文中,我们对结构体已经有所了解.


1.结构体的声明,


2.结构体变量的定义与初始化,


3.结构体传参.


其实结构体是一个很重要的内容,在数据结构中,应用十分广泛.所以学好结构体也是十分重要的!


1.1 匿名结构体


匿名结构体是一种省略struct后面的类型名的一种结构体类型.


这种情况好少见,是我们需要使用这个结构体一次时,后面不会再用这个结构体时使用.(个人感觉用处不大).


是一种不完全结构体的声明.


struct
{
  int age;//年龄
  char name[10];//姓名
  float grade;//成绩
}s1 = { 18,"初阶牛",80.0f };
int main()
{
  printf("%d %s %f\n", s1.age,s1.name,s1.grade);
  return 0;
}


注意匿名结构体只有在声明结构体时的后面定义变量,否则对于一个没有名字的结构体,下次想使用就很难找到它了.


补充知识:


两个拥有相同成员变量的结构体,他们是同一类型的结构体吗?


对于两个拥有相同成员变量的结构体,编译器并不会将他们视作同一结构体类型.


//匿名结构体类型
struct s1
{
  int age;//年龄
  char name[10];//姓名
  double grade;//成绩
}s1 = { 18,"初阶牛",50 };
struct s2
{
  int age;//年龄
  char name[10];//姓名
  double grade;//成绩
}  *p;
int main()
{
  p = &s1;
  printf("%d %s %lf\n", p->age, p->name, p->grade);
  return 0;
}


运行结果:


18 初阶牛 50.000000


这里之所以能打印出来是因为,在c语言中会发生强制转换,p会指向s1的地址,刚好偏移量又一样,所以才会打印出来,但是这并不意味着这两个结构体类型相同.


1.2 结构体的自引用


之前我们见过结构体的嵌套定义.


如:


#include <stdio.h>
typedef struct teacher
{
  char name[20];//名字
  char subject[20];//科目
}ter;//将struct teacher重命名为ter,使得类型名更加简洁
typedef struct student
{
  char name[20];//名字
  int age;//年龄
  char sex[5];//性别
  char id[20];//学号
  ter t1;//嵌套定义,一个结构体中包含令一个结构体.
}stu;//同理struct student重命名为stu
int main()
{
  //嵌套结构体的初始化
  stu s1 = { "初阶牛",20,"男","20216666",{"pengge","c语言"} };
  //嵌套结构体的打印
  printf("%-6s %-2d %s %s %s %s\n", s1.name, s1.age, s1.sex, s1.id, s1.t1.name, s1.t1.subject);
  return 0;
}


嵌套定义示例图解:



那么可以自己引用自己(自引用)吗?


我们试着尝试写一下代码:


错误示例1:


typedef struct student
{
  char name[20];//名字
  int age;//年龄
  char sex[5];//性别
  stu s1;
}stu;


原因:


虽然我们对struct student类型进行了重命名为stu,但是在结构体内部还没有生效,因为这个结构体类型的定义还没结束,结构体本身并不认识stu,只有声明结束后才可以使用.


typedef struct student
{
  char name[20];//名字
  int age;//年龄
  char sex[5];//性别
  struct student s1;
}stu;


原因:


虽然我们这里正确的使用了结构体的类型名,但是,如果一个结构体中引用了自己,那么这个结构体的大小是多少?这不就无限递归了吗?


这样?



正确的自引用方法是:


typedef struct student
{
  char name[20];//名字
  int age;//年龄
  char sex[5];//性别
  struct student* next;//一个结构体指针,用于指向与自己类型相同的结构体
}stu;


这种结构体自引用的情况在数据结构的链表中就有应用.


我们可以简单了解一下,后续在数据结构中会详细讲解.


这只是申请了局部变量作为链表的成员,仅仅只是为了介绍结构体自引用的应用.


不需要过分研究.


typedef struct student
{
  char name[20];//名字
  int age;//年龄
  char sex[5];//性别
  struct student* next;
}stu;
int main()
{ 
  stu s1 = { "学生1",18,"男",NULL };//暂时将最后一个成员结构体指针置空
  stu s2 = { "学生2",19,"女",NULL };
  stu s3 = { "学生3",20,"男",NULL };
  //将这些结构体都连接起来
  s1.next = &s2;
  s2.next = &s3;
  //创建一个头指针来访问他们
  stu* head = &s1;
  //打印
  while(head != NULL)//最后一个结构体的next是NULL
  {
    printf("%-8s %-5d%s\n", head->name, head->age, head->sex);
    head = head->next;//往后找下一个结构体
  }
  return 0;
}


运行结果:


学生1 18 男


学生2 19 女


学生3 20 男


链表的简单了解:


抽象图:



内存中的存储图解:



1.3 结构体内存大小的计算


结构体大小的计算是一个重要知识点.


试着猜一下结构体stu的大小是多少?


示例1:


struct student
{
  int a;
  char b;
  int c;
}stu;
int main()
{
  printf("%zd", sizeof(stu));
  return 0;
}


12


不知道友友,们有没有听过内存对齐.


其实结构体可是一个纨绔子弟,"富哥"都是很奢侈的,他经常浪费内存!!!


结构体大小计算方法:


内存对齐规则:


  1. 第一个成员在与结构体变量偏移量为0的地址处。从偏移量为0的地址处向后使用.


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


对齐数 =编译器的默认对齐数与变量成员大小中的较小值.(在VS中默认对齐数是8)


  1. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。


  1. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的成员的对齐数)的整数倍。



还是举例子更好理解对吧?


🌰栗子


示例1:普通结构体


示例1答案:


首先我们将首地址作为偏移量为0的地址.


第一个元素是整形,占4个字节,默认对齐数是8字节,较少者是4字节,则对齐数就是4,故偏移量0-3分配给a变量.


第二个元素是char类型,占1个字节,min(1,8),则对齐数是1,故偏移量4位置分配给b变量.


第三个元素是int型,同理,默认对齐数是4,则偏移量5-7都不能使用,是的你没有听错,这三个字节都被浪费掉了,从偏移量为8开始,8-11偏移的地址分配给变量c.


最后,此时占用了12个字节,最大的成员对齐数是4,刚好12是4的倍数,所以整个结构体的大小就是占12个字节.


图解:



示例2:包含double类型成员的结构体


struct S4
{
  double d;
  char c;
};


运行结果:


16


原因:


double占八个字节,则0-7的偏移量分配给d


char占一个字节,则偏移量8的位置分配给c


总字节数为9,但是成员最大对齐数是8,9不是8 的倍数,所以需要内存对齐,故最后占16字节.


图解:



将结构体改成如下结构,一样是占用16字节.


struct S4
{
  double d;//0-7
  char c;//8
  int i;//12-15
};


示例3:嵌套结构体的内存大小计算


struct S3
{
  int a;
  char c1;
  int i;
};
struct S4
{
  char c1;
  struct S3 s3;
  int d;
};
int main()
{
  printf("%d\n", sizeof(struct S4));
  return 0;
}


运行结果:


20


图解:



示例四:包含数组的结构体


#include <stdio.h>
typedef struct student
{
  char a;
  int arr[6];
  char b;
}stu;
int main()
{
  printf("%d", sizeof(stu));
  return 0;
}


数组就将其看成该元素类型的多个成员变量即可,即对齐数是4,就是6个int型的变量。


图解:



练习题1:


#include <stdio.h>
struct S2
{
  char c1;    //0
  int i;      //4-7
  char c2;    //8
};
struct S3
{
  char c1;    //0
  char c2;    //1
  int i;      //4-7
};
int main()
{
  struct S2 a = { 'a',1,'b' };
  struct S3 b = { 'a','b',1};
  printf("%d\n", sizeof(struct S2));  //12
  printf("%d\n", sizeof(struct S3));  //8
  return 0;
}


当我们拿捏不定时,我们可以通过调试,在内存窗口观察其中的值.


补充知识:cc是系统分配空间时初始化的值,我们就理解为未知值(未被使用)


字符a的ASCII码值是97(十进制)---->61(16进制).


字符b的ASCII码值是98(十进制)---->62(16进制).


s2在内存中:



s3在内存中



练习2:


struct S3
{
  int a;//0-3
  char c1;//4
  int i;//8-11
  double b;//16-23
};
struct S4
{
  char c1;//1
  struct S3 s3;//8-31
  int d;//32-35
};
int main()
{
  printf("%d\n", sizeof(struct S4));
  return 0;
}


答案:40

目录
相关文章
|
存储 编译器 数据库
【C/C++ 数据结构 】线索二叉树全解析:从数学原理到C++实现
【C/C++ 数据结构 】线索二叉树全解析:从数学原理到C++实现
884 0
|
8月前
|
人工智能 JavaScript 机器人
OpenClaw(Clawdbot)阿里云轻量服务器部署+Discord机器人搭建|零代码命令速通+常见问题全解
2026年,OpenClaw(原Clawdbot)凭借轻量化部署、多平台接入与灵活的Skill扩展能力,成为个人与团队搭建专属AI助手的首选工具。对于新手而言,阿里云轻量服务器是部署OpenClaw的最优选择——成本低、稳定性强、7×24小时在线,搭配Discord可快速搭建能聊天、执行指令、自动处理任务的AI机器人。
670 13
|
10月前
|
机器学习/深度学习 存储 缓存
阿里云服务器最新租用价格:2核4G、2核8G、4核8G、4核16G不同实例与带宽活动价格参考
阿里云服务器2核4G配置中,通用算力型u1实例年费199元起,经济型e实例和u2a实例价格分别为599.93元/年、504.60元/年起;4核16G配置u2a实例年费1291.80元起。不同实例类型适用场景各异:经济型e实例适合轻负载场景,通用算力型u2a实例性价比突出,九代实例(c9i/g9i/r9i)专为高性能场景设计。用户需结合预算、业务类型及性能需求选择,通过活动优惠、优惠券达到成本最优化。
1376 6
|
存储 运维 数据挖掘
革新智能驾驶数据挖掘检索效率!某国内新能源汽车未来出行领导者选择阿里云Milvus构建多模态检索引擎
在智能驾驶技术快速发展中,数据成为驱动算法进步的核心。某新能源汽车领军企业基于阿里云Milvus向量数据库构建智能驾驶数据挖掘平台,利用其高性能、可扩展的相似性检索服务,解决了大规模向量数据检索瓶颈问题,显著降低20%以上成本,缩短模型迭代周期,实现从数据采集到场景挖掘的智能化闭环,加速智能驾驶落地应用。
1719 3
革新智能驾驶数据挖掘检索效率!某国内新能源汽车未来出行领导者选择阿里云Milvus构建多模态检索引擎
|
9月前
|
人工智能 安全 网络安全
2026年OpenClaw(Clawdbot)及skills零基础部署攻略:从极速上手到企业级落地
2026年,AI自动化工具迎来全民普及浪潮,OpenClaw(原Clawdbot、Moltbot)凭借“自然语言驱动+任务自动化执行”的核心优势,成为个人与轻量团队搭建专属AI助手的首选方案。这款开源AI代理与自动化平台,彻底打破了传统AI“只说不做”的局限,能够直接执行系统命令、操作文件、控制浏览器、管理邮件,真正成为7×24小时不间断工作的“数字员工”。
682 1
|
10月前
|
Linux 开发者 iOS开发
IntelliJ IDEA 的「闪电操作」:这些 Quick 技巧让你编码快如疾风
本文详解 IntelliJ IDEA 六大高效“Quick”技巧:Quick Fix(Alt+Enter)、Quick Doc(Ctrl+Q)、Quick Evaluate(Alt+F8)、Quick Switch Scheme(Ctrl+`)、Quick Definition(Ctrl+Shift+I)及 Live Templates。助你减少鼠标操作,提升编码流畅度与思维连贯性。(239字)
660 9
|
存储 Shell Linux
八、Linux Shell 脚本:变量与字符串
Shell脚本里的变量就像一个个贴着标签的“箱子”。装东西(赋值)时,=两边千万不能有空格。用单引号''装进去的东西会原封不动,用双引号""则会让里面的$变量先“变身”再装箱。默认箱子只能在当前“房间”(Shell进程)用,想让隔壁房间(子进程)也能看到,就得给箱子盖个export的“出口”戳。此外,Shell还自带了$?(上条命令的成绩单)和$1(别人递进来的第一个包裹)等许多特殊箱子,非常有用。
1177 2
|
关系型数据库 MySQL 数据库
【赵渝强老师】MySQL的事务隔离级别
数据库并发访问时易引发数据不一致问题。如客户端读取到未提交的事务数据,可能导致“脏读”。MySQL通过四种事务隔离级别(读未提交、读已提交、可重复读、可序列化)控制并发行为,默认为“可重复读”,以平衡性能与数据一致性。
620 0
|
存储 安全 数据中心
数据中心概述
数据中心是包含计算机系统、通信设备及配套环境控制、安全装置的复杂设施,用于集中存储、处理和传输数据。根据用途可分为企业、运营商和互联网数据中心。其基础设施涵盖机房、供电、散热、网络设备及管理软件。发展历程从大型机时代逐步演进至云计算时代,现以大规模、资源池化为特征。
1513 0