【C语言】结构体详解 -《探索C语言的 “小宇宙” 》
目录
- C语言结构体(`struct`)详解
- 结构体概览表
- 1. 结构体的基本概念
- 1.1 结构体定义
- 1.2 结构体变量声明
- 2. 结构体成员的访问
- 2.1 使用点运算符(`.`)访问成员
- 输出
- 2.2 使用箭头运算符(`->`)访问成员
- 输出
- 3. 结构体的初始化
- 3.1 结构体初始化
- 输出
- 3.2 使用指定初始化器
- 输出
- 4. 结构体的大小
- 输出
- 5. 结构体作为函数参数
- 5.1 传递结构体的副本
- 输出
- 5.2 传递结构体指针
- 输出
- 6. 结构体的嵌套
- 输出
- 7. 结构体与数组
- 输出
- 8. 结构体的内存对齐
- 8.1 对齐示例
- 输出
- 8.2 结构体对齐与`#pragma pack`
- 输出
- 9. 类型定义(`typedef`)简化结构体声明
- 输出
- 10. 嵌入式系统中的应用
- 10.1 示例:硬件寄存器配置
- 输出
- 11. 拓展技巧
- 11.1 结构体指针的算术运算
- 输出
- 11.2 结构体与联合体(`union`)的比较
- 示例:结构体与联合体的比较
- 输出
- 7. 结束语
- 相关文章:
C语言结构体(struct
)详解
结构体概览表
功能 | 描述 |
---|---|
定义结构体 | 定义一个结构体类型 |
声明结构体变量 | 声明一个结构体变量 |
访问成员 | 使用点运算符(. )和箭头运算符(-> )访问成员 |
初始化结构体 | 在声明时初始化结构体 |
计算大小 | 使用sizeof 计算结构体的大小 |
作为函数参数 | 传递结构体或结构体指针作为函数参数 |
结构体嵌套 | 结构体中包含其他结构体 |
结构体与数组 | 结构体作为数组元素或包含数组的成员 |
内存对齐 | 结构体的内存对齐和填充 |
类型定义(typedef ) | 使用typedef 简化结构体声明 |
嵌入式应用 | 在嵌入式系统中使用结构体 |
拓展技巧 | 结构体指针运算和联合体比较 |
1. 结构体的基本概念
1.1 结构体定义
结构体通过struct
关键字定义。定义结构体时,需要指定结构体的名称以及结构体内部的成员变量。
struct Person {char name[50];int age;float height;
};
在上面的示例中,定义了一个Person
结构体,其中包含三个成员:name
(字符数组)、age
(整数)和height
(浮点数)。
1.2 结构体变量声明
定义结构体后,可以声明结构体变量来使用它。例如:
struct Person person1;
这里声明了一个Person
结构体类型的变量person1
。
2. 结构体成员的访问
2.1 使用点运算符(.
)访问成员
可以通过点运算符(.
)访问结构体的成员变量。例如:
#include <stdio.h>struct Person {char name[50];int age;float height;
};int main() {struct Person person1;// 初始化结构体成员person1.age = 25;person1.height = 175.5;snprintf(person1.name, sizeof(person1.name), "Alice");// 输出结构体成员printf("Name: %s\n", person1.name);printf("Age: %d\n", person1.age);printf("Height: %.2f\n", person1.height);return 0;
}
输出
Name: Alice
Age: 25
Height: 175.50
2.2 使用箭头运算符(->
)访问成员
如果结构体变量是指针类型,则可以通过箭头运算符(->
)访问其成员。例如:
#include <stdio.h>struct Person {char name[50];int age;float height;
};void printPerson(struct Person *p) {printf("Name: %s\n", p->name);printf("Age: %d\n", p->age);printf("Height: %.2f\n", p->height);
}int main() {struct Person person1 = {"Bob", 30, 180.0};struct Person *ptr = &person1;printPerson(ptr);return 0;
}
输出
Name: Bob
Age: 30
Height: 180.00
3. 结构体的初始化
3.1 结构体初始化
可以在定义结构体变量的同时进行初始化。例如:
#include <stdio.h>struct Person {char name[50];int age;float height;
};int main() {struct Person person2 = {"Alice", 30, 160.0};printf("Name: %s\n", person2.name);printf("Age: %d\n", person2.age);printf("Height: %.2f\n", person2.height);return 0;
}
输出
Name: Alice
Age: 30
Height: 160.00
3.2 使用指定初始化器
C99标准引入了指定初始化器,可以按顺序或指定成员进行初始化。例如:
#include <stdio.h>struct Person {char name[50];int age;float height;
};int main() {struct Person person3 = {.age = 40, .height = 180.0, .name = "Bob"};printf("Name: %s\n", person3.name);printf("Age: %d\n", person3.age);printf("Height: %.2f\n", person3.height);return 0;
}
输出
Name: Bob
Age: 40
Height: 180.00
4. 结构体的大小
结构体的大小取决于其成员的数量和类型,以及内存对齐的规则。可以使用sizeof
运算符来获取结构体的大小。例如:
#include <stdio.h>struct Person {char name[50];int age;float height;
};int main() {printf("Size of Person: %zu bytes\n", sizeof(struct Person));return 0;
}
输出
在不同平台上的输出可能不同,例如:
- 32位系统:
Size of Person: 60 bytes
- 64位系统:
Size of Person: 64 bytes
5. 结构体作为函数参数
5.1 传递结构体的副本
结构体可以作为函数参数传递。如果传递的是结构体的副本,则会创建结构体的一个副本,可能会影响性能。
#include <stdio.h>struct Person {char name[50];int age;float height;
};void printPerson(struct Person p) {printf("Name: %s\n", p.name);printf("Age: %d\n", p.age);printf("Height: %.2f\n", p.height);
}int main() {struct Person person1 = {"Alice", 30, 160.0};printPerson(person1);return 0;
}
输出
Name: Alice
Age: 30
Height: 160.00
5.2 传递结构体指针
为了提高效率,可以将结构体的指针传递给函数,这样只需传递指针而不是整个结构体。
#include <stdio.h>struct Person {char name[50];int age;float height;
};void updateAge(struct Person *p, int newAge) {p->age = newAge;
}int main() {struct Person person1 = {"Bob", 30, 180.0};updateAge(&person1, 35);printf("Updated Age: %d\n", person1.age);return 0;
}
输出
Updated Age: 35
6. 结构体的嵌套
结构体可以嵌套其他结构体。例如:
#include <stdio.h>struct Address {char street[100];char city[50];int postalCode;
};struct Person {char name[50];int age;struct Address address; // 嵌套的结构体
};int main() {struct Person person4 = {"John",28,{"123 Main St", "Metropolis", 12345}};printf("Name: %s\n", person4.name);printf("Address: %s, %s %d\n", person4.address.street, person4.address.city, person4.address.postalCode);return 0;
}
输出
Name: John
Address: 123 Main St, Metropolis 12345
7. 结构体与数组
结构体可以作为数组的元素,也可以包含数组作为成员。例如:
#include <stdio.h>struct Person {char name[50];int age;float height;
};int main() {struct Person people[2] = {{"Alice", 30, 160.0},{"Bob", 40, 180.0}};for (int i = 0; i < 2; i++) {printf("Person %d: %s, %d, %.2f\n", i+1, people[i].name, people[i].age, people[i].height);}return 0;
}
输出
Person 1: Alice, 30, 160.00
Person 2: Bob, 40, 180.00
8. 结构体的内存对齐
结构体的内存对齐与填充是为了提高数据访问的效率。在C语言中,结构体的内存布局可能会受到对齐要求的影响,导致结构体的实际大小可能大于成员变量总和的大小。编译器通常会在成员之间插入填充字节,以确保每个成员的地址对齐。
8.1 对齐示例
#include <stdio.h>struct Example {char c; // 1 byteint i; // 4 bytes, 3 bytes of paddingshort s; // 2 bytes
};int main() {printf("Size of Example: %zu bytes\n", sizeof(struct Example));return 0;
}
输出
Size of Example: 12 bytes
在这个例子中,Example
结构体的大小是12字节。虽然char
占1字节,int
占4字节,short
占2字节,成员变量的总和为7字节,但由于内存对齐要求,Example
结构体实际上占用12字节。
8.2 结构体对齐与#pragma pack
在某些情况下,可以使用#pragma pack
指令来控制结构体的对齐方式,从而减少内存占用。
#include <stdio.h>#pragma pack(1) // 设置结构体对齐为1字节struct PackedExample {char c;int i;short s;
};#pragma pack() // 恢复默认对齐int main() {printf("Size of PackedExample: %zu bytes\n", sizeof(struct PackedExample));return 0;
}
输出
Size of PackedExample: 7 bytes
使用#pragma pack(1)
可以将PackedExample
结构体的对齐方式设置为1字节,从而减少结构体的实际大小为7字节,但可能会影响访问效率。
9. 类型定义(typedef
)简化结构体声明
使用typedef
可以为结构体定义一个新的类型名,使得结构体的声明更加简洁。例如:
#include <stdio.h>typedef struct {char name[50];int age;float height;
} Person;int main() {Person person1 = {"Charlie", 28, 175.0};printf("Name: %s\n", person1.name);printf("Age: %d\n", person1.age);printf("Height: %.2f\n", person1.height);return 0;
}
输出
Name: Charlie
Age: 28
Height: 175.00
10. 嵌入式系统中的应用
在嵌入式系统中,结构体用于管理硬件寄存器、配置参数以及存储设备状态等。结构体能够帮助开发者以更结构化的方式访问硬件资源,提高代码的可读性和维护性。
10.1 示例:硬件寄存器配置
#include <stdio.h>
#include <stdint.h>// 定义硬件寄存器配置结构体
typedef struct {volatile uint32_t CONTROL;volatile uint32_t STATUS;volatile uint32_t DATA;
} UART_RegDef_t;int main() {UART_RegDef_t UART1; // 假设这是一个UART寄存器的实例// 设置UART寄存器UART1.CONTROL = 0x01; // 启用UARTUART1.STATUS = 0x00; // 清除状态UART1.DATA = 0x55; // 发送数据// 打印寄存器的配置printf("UART1 CONTROL: 0x%X\n", UART1.CONTROL);printf("UART1 STATUS: 0x%X\n", UART1.STATUS);printf("UART1 DATA: 0x%X\n", UART1.DATA);return 0;
}
输出
UART1 CONTROL: 0x1
UART1 STATUS: 0x0
UART1 DATA: 0x55
在这个示例中,结构体UART_RegDef_t
用于表示UART寄存器的配置,包含CONTROL
、STATUS
和DATA
寄存器。通过这种方式,可以方便地设置和读取寄存器的值。
11. 拓展技巧
11.1 结构体指针的算术运算
可以对结构体指针进行算术运算,通常用于数组访问。例如:
#include <stdio.h>struct Person {char name[50];int age;float height;
};int main() {struct Person people[3] = {{"Alice", 30, 160.0},{"Bob", 40, 180.0},{"Charlie", 25, 170.0}};struct Person *ptr = people; // 指向结构体数组的指针for (int i = 0; i < 3; i++) {printf("Person %d: %s, %d, %.2f\n", i+1, (ptr+i)->name, (ptr+i)->age, (ptr+i)->height);}return 0;
}
输出
Person 1: Alice, 30, 160.00
Person 2: Bob, 40, 180.00
Person 3: Charlie, 25, 170.00
11.2 结构体与联合体(union
)的比较
结构体和联合体都可以存储多个数据项,但结构体的每个成员都占有独立的内存空间,而联合体的所有成员共享同一块内存。使用结构体时每个成员都可用,而使用联合体时只有一个成员可以使用。
示例:结构体与联合体的比较
#include <stdio.h>typedef union {int intValue;float floatValue;char charValue;
} UnionType;typedef struct {int intValue;float floatValue;char charValue;
} StructType;int main() {UnionType u;StructType s;u.intValue = 10;printf("Union intValue: %d\n", u.intValue);u.floatValue = 5.5; // 修改联合体中的值printf("Union floatValue: %f\n", u.floatValue);// 注意:这时intValue的值是不确定的s.intValue = 10;s.floatValue = 5.5;s.charValue = 'A';printf("Struct intValue: %d\n", s.intValue);printf("Struct floatValue: %f\n", s.floatValue);printf("Struct charValue: %c\n", s.charValue);return 0;
}
输出
Union intValue: 10
Union floatValue: 5.500000
Struct intValue: 10
Struct floatValue: 5.500000
Struct charValue: A
7. 结束语
- 本节内容已经全部介绍完毕,希望通过这篇文章,大家对C语言中的结构体
struct
有了更深入的理解和认识。- 感谢各位的阅读和支持,如果觉得这篇文章对你有帮助,请不要吝惜你的点赞和评论,这对我们非常重要。再次感谢大家的关注和支持!点我关注❤️
相关文章:
- 指针的神秘探险:从入门到精通的奇幻之旅 !
相关文章:

【C语言】结构体详解 -《探索C语言的 “小宇宙” 》
目录 C语言结构体(struct)详解结构体概览表1. 结构体的基本概念1.1 结构体定义1.2 结构体变量声明 2. 结构体成员的访问2.1 使用点运算符(.)访问成员输出 2.2 使用箭头运算符(->)访问成员输出 3. 结构体…...

基于DTW距离的KNN算法实现股票高相似筛选案例
使用DTW算法简单实现曲线的相似度计算-CSDN博客 前文中股票高相关k线筛选问题的延伸。基于github上的代码迁移应用到股票高相关预测上。 这里给出一个相关完整的代码实现案例。 1、数据准备 假设你已经有了一些历史股票的k线数据。如果数据能打标哪些股票趋势是上涨的、下跌…...
GD32 - IIC程序编写
一、初始化 理论知识链接: IIC理论知识 二、代码实现 1、SDA和SCL设置成开漏输出模式 开漏输出的作用: 因为IIC总线是一种双向的通信协议,需要使用开漏输出实现共享总线。开漏输出类似于一种线与的方式,即无论总线上哪个设备…...

将项目部署到docker容器上
通过docker部署前后端项目 前置条件 需要在docker中拉去jdk镜像、nginx镜像 docker pull openjdk:17 #拉取openjdk17镜像 docker pull nginx #拉取nginx镜像部署后端 1.打包后端项目 点击maven插件下面的Lifecycle的package 对后端项目进行打包 等待打包完成即可 2.将打…...

免费【2024】springboot宠物美容机构CRM系统设计与实现
博主介绍:✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 技术范围:SpringBoot、Vue、SSM、HTML、Jsp、PHP、Nodejs、Python、爬虫、数据可视化…...

搞懂数据结构与Java实现
文章链接:搞懂数据结构与Java实现 (qq.com) 代码链接: Java实现数组模拟循环队列代码 (qq.com) Java实现数组模拟栈代码 (qq.com) Java实现链表代码 (qq.com) Java实现哈希表代码 (qq.com) Java实现二叉树代码 (qq.com) Java实现图代码 (qq.com)...

Stable Diffusion 图生图
区别于文生图,所谓的图生图,俗称的垫图,就是比文生图多了一张参考图,由参考一张图来生成图片,影响这个图片的要素不仅只靠提示词了,还有这个垫图的因素,这个区域就上上传垫图的地方,…...

语言转文字
因为工作原因需要将语音转化为文字,经常搜索终于找到一个免费的好用工具,记录下使用方法 安装Whisper 搜索Colaboratory 右上方链接服务 执行 !pip install githttps://github.com/openai/whisper.git !sudo apt update && sudo apt install f…...

ref函数
Vue2 中的ref 首先我们回顾一下 Vue2 中的 ref。 ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例࿱…...
7/30 bom和dom
文档对象mox 浏览器对象模型...
【Golang 面试 - 进阶题】每日 3 题(五)
✍个人博客:Pandaconda-CSDN博客 📣专栏地址:http://t.csdnimg.cn/UWz06 📚专栏简介:在这个专栏中,我将会分享 Golang 面试中常见的面试题给大家~ ❤️如果有收获的话,欢迎点赞👍收藏…...
MySQL,GROUP BY子句的作用是什么?having和where的区别在哪里说一下jdbc的流程
GROUP BY 子句的作用是什么 GROUP BY 字段名 将数据按字段值相同的划为一组,经常配合聚合函数一起使用。 having和where的区别在哪里 where是第一次检索数据时候添加过滤条件,确定结果集。而having是在分组之后添加结果集,用于分组之后的过…...

1._专题1_双指针_C++
双指针 常见的双指针有两种形式,一种是对撞指针,一种是左右指针。对撞指针:一般用于顺序结构中,也称左右指针。 对撞指针从两端向中间移动。一个指针从最左端开始,另一个从最右端开始,然后逐渐往中间逼近…...
Spring集成ES
RestAPI ES官方提供的java语言客户端用以组装DSL语句,再通过http请求发送给ES RestClient初始化 引入依赖 <dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId> </d…...

力扣高频SQL 50题(基础版)第二十六题
文章目录 力扣高频SQL 50题(基础版)第二十六题1667.修复表中的名字题目说明实现过程准备数据实现方式结果截图总结 力扣高频SQL 50题(基础版)第二十六题 1667.修复表中的名字 题目说明 表: Users ----------------…...

WIFI 接收机和发射机同步问题+CFO/SFO频率偏移问题
Synchronization Between Sender and Receiver & CFO Correction 解决同步问题和频率偏移问题是下面论文的关键,接下来结合论文进行详细解读 解读论文:Verification and Redesign of OFDM Backscatter 论文pdf:https://www.usenix.org/s…...

ubuntu安装并配置flameshot截图软件
参考:flameshot key-bindins 安装 sudo apt install flameshot自定义快捷键 Settings->Keyboard->View and Customize Shortcuts->Custom Shortcuts,输入该快捷键名称(自定义),然后输入command(…...

【Linux】CentOS更换国内阿里云yum源(超详细)
目录 1. 前言2. 打开终端3. 确保虚拟机已经联网4. 备份现有yum配置文件5. 下载阿里云yum源6. 清理缓存7. 重新生成缓存8. 测试安装gcc 1. 前言 有些同学在安装完CentOS操作系统后,在系统内安装比如:gcc等软件的时候出现这种情况:(…...

Leetcode49. 字母异位词分组(java实现)
今天我来给大家分享的是leetcode49的解题思路,题目描述如下 如果没有做过leetcode242题目的同学,可以先把它做了,会更好理解异位词的概念。 本道题的大题思路是: 首先遍历strs,然后统计每一个数组元素出现的次数&#…...
OpenJudge | 字符串中最长的连续出现的字符
总时间限制: 1000ms 内存限制: 65536kB 描述 求一个字符串中最长的连续出现的字符,输出该字符及其出现次数,字符串中无空白字符(空格、回车和tab),如果这样的字符不止一个,则输出第一个 输入 首先输入N…...

Docker 离线安装指南
参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性,不同版本的Docker对内核版本有不同要求。例如,Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本,Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...

七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...
Webpack性能优化:构建速度与体积优化策略
一、构建速度优化 1、升级Webpack和Node.js 优化效果:Webpack 4比Webpack 3构建时间降低60%-98%。原因: V8引擎优化(for of替代forEach、Map/Set替代Object)。默认使用更快的md4哈希算法。AST直接从Loa…...

CVPR2025重磅突破:AnomalyAny框架实现单样本生成逼真异常数据,破解视觉检测瓶颈!
本文介绍了一种名为AnomalyAny的创新框架,该方法利用Stable Diffusion的强大生成能力,仅需单个正常样本和文本描述,即可生成逼真且多样化的异常样本,有效解决了视觉异常检测中异常样本稀缺的难题,为工业质检、医疗影像…...
git: early EOF
macOS报错: Initialized empty Git repository in /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/.git/ remote: Enumerating objects: 2691797, done. remote: Counting objects: 100% (1760/1760), done. remote: Compressing objects: 100% (636/636…...

门静脉高压——表现
一、门静脉高压表现 00:01 1. 门静脉构成 00:13 组成结构:由肠系膜上静脉和脾静脉汇合构成,是肝脏血液供应的主要来源。淤血后果:门静脉淤血会同时导致脾静脉和肠系膜上静脉淤血,引发后续系列症状。 2. 脾大和脾功能亢进 00:46 …...