当前位置: 首页 > news >正文

C语言:结构体(二)

8) 结构体嵌套
结构体的成员类型可以是基本类型、构造类型(数组、结构体等)、指针类型
例如:

其运行结果为

注意:结构体成员可以是其他结构体类型的变量,但是不能是本结构体类型的变量
例如:
struct Point
{
int x;
int y;
struct Point pos; // 错误,如果包含自己,里面无限嵌套
};

虽然结构体成员不能是本结构体类型的变量,但是可以是本结构体类型的指针变量
例如:
struct Point
{
int x;
int y;
struct Point *pos; // 正确

9) 定义并初始化结构体变量
结构体变量的初始化用{}

struct data
{
int year;
int month;
int day;
};

struct student
{
int num;
char name[32];
struct data birthday;
float score;
};

1、按结构体类型定义时的顺序依次初始化各成员变量,用逗号分开。 重点:用逗号分开
struct student s1 = {1, "caixunkun", {2026, 2, 8}, 2.5};

2、不按结构体类型定义时的顺序来初始化各成员变量,在初始化时要加上 .成员变量名 = 值。
struct student s2 = {
.score = 2.5,
.name = "caixunkun",
.birthday = {
.day = 8,
.year = 2025,
.month = 2
},
.num = 1
};

3、结构体数组初始化
语法形式:
结构体类型名 数组名[元素个数] = {{第一个元素的初值}, {第二个元素的初值}, ..., {第n个元素的初值}};
a. 按数组元素的顺序依次初始化,用逗号分开。
struct student class[3] = {
// class[0], class[1], class[2]是一个结构体,可以按照上面的两种方式来初始化结构体变量
{1, "caixunkun", {2026, 2, 8}, 2.5},
{2, "shangsan", {2026, 2, 9}, 99.9},
{3, "李四", {2026, 2, 10}, 100.0}
};

b. 不按数组元素的顺序来初始化,在初始化时要加上 [下标] = 初值。
struct student class[3] = {
// class[0], class[1], class[2]是一个结构体,可以按照上面的两种方式来初始化结构体变量
[2] = {3, "李四", {2026, 2, 10}, 100.0},
[1] = {2, "shangsan", {2026, 2, 9}, 99.9},
[0] = {1, "caixukun", {2026, 2, 8}, 2.5}
};

3、 共用体(联合体) union
union 也是用来定义数据类型的,它能在同一块内存中存储不同的数据类型(不是同时存储)
例如:
假设有8个字节的内存空间
可以往里面存放一个double类型的数据
也可以选择存放两个int类型的数据
也可以选择存放一个long类型的数据
也可以选择存放八个char类型的数据
...

共用体(联合体) 的定义方式
union 数据类型名
{
成员类型1 成员变量名1;
成员类型2 成员变量名2;
...
成员类型n 成员变量名n;
};

共用体与结构体最大的区别:
结构体定义的变量所占的内存大小是各成员变量之和(可能含有填充);
共用体定义的变量所占的内存大小是各成员变量中最大的那一个所占的内存大小。

共用体的存储空间是各成员变量之间共同使用的;
存储空间中的值同一时刻只能解释为某一个成员变量的值,为了节省内存才提出使用共用体。

例如:
union Test
{
double a;
int b;
short c;
char d[8];
};
union Test 这种类型定义的变量只占8个字节的空间
成员变量a, b, c, d共用这8个字节,可以给这些成员变量赋值,但是同一时刻只能使用或解释为其中一种方式。
union Test t;
t.a = 3.14;
t.b = 100;
t.c = 10;
t.d[0] = 'a';
t.d[1] = 'b';
...

共用体典型用途之一是用来判断系统的大小端模式。

例如:

其运行结果为:

CPU内部寄存器是按bit位来存储信息,但是内部存储器的数量是有限的,所以我们经常需要把寄存器中的数据存储到内存中。
但是内存不是按bit位寻址,而是按字节编号(地址)来寻址。
如果我们要把寄存器中的数据存储到内存中,我们要分情况:
寄存器中的数据是存放在内存的高地址还是低地址,还是随便乱放?

我们的计算机实际上有两种存储模式:大端模式、小端模式
大端模式(Big_Endian):是指数据的高字节(高位)保存在内存的低地址中,而数据的低字节(低位)保存在内存的高地址中。
具体,如图 <大端模式.png>


这种存储方式有点类似把数据当做字符串顺序处理,地址由小到大增加,而数据从高位往低位放,这种和我们的阅读习惯是一致的。

小端模式(Little_Endian):是指数据的高字节(高位)保存在内存的高地址中,而数据的低字节(低位)保存在内存的低地址中。
具体,如图 <小端模式.png>


这种存储方式将地址的高低和数据的位权有效的结合起来了,高地址部分位权高,低地址部分位权低,与我们的阅读习惯相反。

4、 枚举 enum
使用关键字enum,可以定义新的数据类型,并把该类型所有可能的值一一列举出来。
这些一一列举出来的值,其本质就是整型常量,枚举类型声明符号名称来表示这些整型常量
枚举可用于提高代码的可读性和可维护性。

定义语法:
enum 数据类型名
{
枚举元素的列表
};

例如:
类型的定义可以这样写:
enum Bool
{
False, Ture
};

也可以这样写:

其运行结果为:

枚举变量的初始化:
枚举变量的赋值(或初始化)最好是用定义的枚举元素之一。
枚举元素就是整型常量,默认情况下,枚举元素列表中的常量元素被赋予整型常量值:0, 1, 2...
例如: False 0
Ture 1
mon 0
tue 1
wed 2
thu 3
fir 4
sat 5
sun 6
也可以在声明枚举类型时,指定枚举元素的数值,如果给某个枚举元素指定了值,则后面未指定的枚举元素的值
自动在它前一个枚举元素值的基础上加1(数值是可以重复的,但是不建议重复)
例如: enum week {mon, tue = 100, wed, thu, fir = 0, sat, sun};
0 100 101 102 0 1 2

5、 typedef 用来起别名(重命名)的关键字
typedef 是一个关键字,作用是为一种数据类型定义一个新的名字,同时方便代码的移植,其可以为基本类型定义新的名字,也可以
为自定义类型(数组、结构体、共用体等)起别名。使用typedef关键字可以提高代码的可读性 和 可移植性。

语法:
typedef 现有类型名 新类型名
==> 新类型名 和 现有类型名 的 类型一致

使用typedef目的一般有两个:
a. 给旧类型一个容易记住且意义明确的名字,如:
unsigned int c = 500;
typedef unsigned int uint_32;
typedef unsigned short int uint_16;
uint_32 d = 100; <==> unsigned int d = 100;
uint_16 a = 200; <==> unsigned short int a = 200;

b. 简化一些比较复杂的类型声明,如:
typedef struct student STU;
STU s; <==> struct student s;

给结构体、共用体、枚举起别名还可以在其类型定义的同时就完成操作,具体如下:
typedef struct student struct student
{ {
int num; int num;
char name[32]; char name[32];
struct data birthday; struct data birthday;
float score; float score;
} student, *STU; } s1, *s2;
student s1; struct student s1;
STU s2; <==> struct student *s2; struct student *s2;
// 以上代码在定义类型的 // 以上代码在定义类型的
// 同时还起了别名 // 同时还定义了变量

int num[100]; // 定义了一个数组,数组名叫num,同时定义了 int[100] 这样的数组类型
现在让你去定义一个和 num 一样类型的变量 a
typeof(num) a;
==> int[100] a; // 关键字不能和(), []直接写在一起 ==> int a[100];
typedef int[100] NUM_T;
==> typedef int NUM_T[100]; // 这里不是在定义数组变量,而是给 int[100] 起了一个别名 NUM_T,NUM_T是一个新的类型名
NUM_T a; <==> int[100] a; <==> int a[100];

void (*p)(int, int); // 定义了一个函数指针,指针名为p
现在让你去定义一个和 p 一样类型的变量 p1
typeof(p) p1;
==> void (int, int) *p1; ==> void (*p1)(int, int);
typedef void (int, int)* func_2t; ==> typedef void (*func_2t)(int, int);
// 这里不是在定义函数指针变量,而是给void (int, int)* 起了一个别名func_2t,func_2t是一个新的类型名
func_2t p2; <==> void (*p2)(int, int);

总结:
所以typedef加在定义变量的前面,变成定义一个新的类型名
int a; // a 是一个变量名
typedef int a; // a 是一个新的类型名,这个类型就是int,a就是int的别名

int b[10]; // b 是一个数组变量名
typedef int b[10]; // b 是一个新的类型名,这个类型就是int[10],b就是int[10]的别名

void (*p)(void); // p 是一个函数指针变量名
typedef void (*p)(void); // p 是一个新的类型名,这个类型就是void (*)(void),p就是void (*)(void)的别名
// 用来定义一个 指向一个没有参数没有返回值的函数 的函数指针

http://www.jsqmd.com/news/954419/

相关文章:

  • 850nm 红外补光 + 深度传感,一文看懂 Windows Hello 硬件架构
  • 从攻击到防御:基于快马ai生成dvwa文件上传漏洞的完整实战案例剖析
  • 别再只懂两两导通了!手把手带你搞懂无刷电机三三导通,为啥它不常用?
  • 黄金回收警惕三大细节,嘉兴桐乡市今日金价行情与合理报价区间 - 上门黄金回收
  • 从Vivado回到ISE:老项目调试时,ILA和VIO的这几个差异点你得知道
  • Mythos模型如何重构AI安全与软件漏洞发现范式
  • 企业即时通讯技术架构怎么理解?从服务端、多端同步到私有化部署边界看落地能力 - 小天互连即时通讯
  • Basys 3双板无线钢琴系统:即载即用的发射/接收bit文件包
  • 从100万PPS到10万PPS:一次高性能网关性能雪崩的根因分析与架构重构
  • FPGA上跑通USB转串口的Verilog工程,带全套Quartus编译中间文件
  • 2026花都区专利代理TOP3测评|专利补贴新政全解析、汽车零部件皮具美妆智造资助标准、空港经济科创扶持、高企专精特新申报加分、全年申报批次流程、专利避坑指南与本土制造企业落地案例大全 - 资讯速览
  • 政务系统中的可预测ID模式与IDOR漏洞实战分析
  • Altium Designer绿色报错别头疼,这几个隐藏快捷键和设置项才是关键
  • 你的品牌在AI搜索中排第几?用GEO评估工具测一测
  • 如何将大视频文件缩小90%:终极免费压缩工具完整指南
  • 2026 诸城防水补漏哪家好?住建实地测评权威榜单 TOP5|南部马耳山低山丘陵 / 中部缓岗坡地 / 北部潍河冲积平原、诸城经开区渗漏修缮白皮书(6 月专项调研 - 苏易修缮
  • 航空运维大模型人工智能AI系统软件平台设计方案
  • 新手福音,用快马平台AI生成代码学习ok影视配置接口开发
  • 别再手动画图了!用QGIS 3.28把Excel里的气象站点数据一键变成专业色斑图
  • Whisper语音识别轻量化微调与跨平台部署工具集(Android/Windows/服务端全支持)
  • 手机拍照为什么四角会发暗?深入聊聊ISP里的LSC模块与模组一致性校准
  • GNSS信号频点命名的秘密:从L波段到‘无线电窗口’,一次讲清导航信号为什么选这个频率
  • MuleSoft+LangChain企业级AI编排实战:数据集成与大模型协同
  • Arthas 最常用命令速查表
  • 2026快手怎么去水印?快手官方去水印途径与合规方法汇总
  • 给TMS320F28379D新手:手把手教你配置外部GPIO中断(附代码避坑)
  • MATLAB版DTLZ多目标测试函数全集(含9个标准函数+8种前沿形态变体)
  • Java后端做RAG:从4步入门到文档入库实战
  • 2026实测豆包即梦图片水印去除方法!即梦水印能去掉吗合规去除教程
  • 从H.264宏块到H.265 CTU:视频编码的“乐高积木”进化史