尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
C语言的结构体
在C语言中普通变量只能存单个数据一个数字或者一个字符但是结构体可以把一组相关的、不同类型的数据捆成一个整体。即结构体可以把一个数据元素的各个不同的数据项聚合为一个整体是一种自定义数据类型。核心作用是把多个不同类型的变量打包在一起用来描述一个事物的多个属性。本文主要介绍结构体的定义、引用最后并通过一个实例来测试实际场景中的应用一、声明格式struct 结构体名{成员列表;};成员列表包括成员类型 成员名不同属性成员类型可以相同但是成员名必须要不同例如定义学生这个结构体structStudent{intid;charname[20];//不能指针因为可能野指针intage;floatscore;};structStudentstu1;定义完结构体类型只是声明构造了一种新的类型struct 结构体名还没有创建实际变量未存储实际的数据不能在{}进行赋值、运算等实际使用还需需要进行声明创建具体的变量可真正存数据的对象如上面的struct Student stu1二、引用c语言结构体有两种成员运算符点运算符域运算符和箭头运算符分量运算符点运算符用于结构体普通变量访问成员格式变量.成员名箭头运算符用于结构体指针访问成员格式变量-成员名《》等价于*指针).成员名.的优先级高于*,(*p).成员的括号不能省略1、域运算符对结构体变量的运算必须通过成员域运算符“.”来访问结构体变量的成员方式如下结构体变量名.成员名intmain(){// 2. 定义结构体变量structStudentstu1;// 3. 给成员赋值stu1.id888;strcpy(stu1.name,王五);//字符数组定义的时候可以name[10]wangwu;这里不可以使用。使用strcpy或scanf(%s,stu1.name)stu1.age18;stu1.score99.5;// 4. 访问成员并打印printf(学号:%d\n,stu1.id);printf(姓名:%s\n,stu1.name);printf(年龄:%d\n,stu1.age);printf(成绩:%.1fd\n,stu1.score);return0;}编译运行后的结果如下通过scanf实现字符串的输入#includestdio.h#includewindows.h#includemath.hstructtest_person{/* data */charname[20];intage;intclass;charaddress[20];};intmain(){SetConsoleOutputCP(65001);SetConsoleCP(65001);{/* data */structtest_personperson1;scanf(%s %d %d %s,person1.name,person1.age,person1.class,person1.address);printf(姓名%s\n,person1.name);printf(年龄%d\n,person1.age);printf(年级%d\n,person1.class);printf(地址%s\n,person1.address);};return0;}运行结果2、分量运算符箭头运算符-#includestdio.h#includewindows.h#includemath.hstructtest_person{/* data */charname[20];intage;intclass;charaddress[20];};intmain(){SetConsoleOutputCP(65001);SetConsoleCP(65001);{/* data */structtest_personperson1;structtest_person*pperson1;//struct test_person *pmalloc(sizeof(struct test_person));p-age22;p-class2026;scanf(%s %s,person1.name,person1.address);printf(姓名%s\n,p-name);printf(年龄%d\n,p-age);printf(年级%d\n,p-class);printf(地址%s\n,p-address);};return0;}运行结果3结构体嵌套的引用structtest_birthday{intyear;intmouth;intday;};structtest_person{/* data */charname[20];intage;intclass;structtest_birthdaytime;charaddress[20];};intmain(){SetConsoleOutputCP(65001);SetConsoleCP(65001);{/* data */structtest_personperson1;person1.time.year2008;person1.time.mouth8;person1.time.day8;person1.age18;printf(年龄%d\n,person1.age);printf(出生年%d\n,person1.time.year);printf(出生月份%d\n,person1.time.mouth);};return0;}运行结果核心判断规则每一步看当前是什么如果是结构体变量实体后面跟.如果是结构体指针后面跟-情况写法外层变量内层结构体变量obj.in.a外层变量内层结构体指针obj.pin-a外层指针内层结构体变量p-in.a外层指针内层结构体指针p-pin-astruct 结构体可以放在main函数作用域只限于main函数也可以放到函数外面此时作用域是全局变量。三、结构体的特点和内存分布1、特点允许存在多种类型可以把int、char、float、数组甚至其他结构体打包在一起自定义性强可以根据需求创建任意类型整体管理一个结构体变量能代表一个完整事物不用维护一堆零散变量。2、内存分布计算结构体的大小需要考虑到内存单元的分布结构体在内存中按照单元存储的每个单元多大是跟结构体中最大的基本类型有关。存储方式把第一个成员变量的存储地址看成0把第二个成员变量的存储地址就是它所占的字节数的倍数。。。。整个结构体的大小是最大的基本类型的倍数。1示例一structtest_struct{chara;shortb;shortc;intd;};structtest_person{charname[20];intage;intclass;structtest_structtime;charaddress[20];};intmain(){SetConsoleOutputCP(65001);SetConsoleCP(65001);{printf(test_struct的长度是%d\n,sizeof(structtest_struct));printf(test_person的长度是%d\n,sizeof(structtest_person));};return0;}我们按照上面的存储规则进行计算存储大小test_struct的大小如下存储char a从0开始存储占用1字节short bshort类型是2字节需要从2的倍数开始存储故1#需要填充b占用2#3#两个字节short c:同b正好可以从4#开始int dint占用4字节需要从4的倍数开始存储故6#7#需要填充d占用8-11字节综上一共12字节。test_person含有嵌套结构体char name[20]:占用0-19#int ageint需要从4的倍数开始正好从20#-23int class;同age24-27#struct test_struct time;内部abcd继续按照上面的原则存储a:占28#b填充29#占用30-31c占用32-33dint类型需要从36-3934、35填充char address[20];占用40-59综上占用60字节通过程序运行一下可得到如下结果2示例二#includestdio.h#includewindows.h#includemath.hstructtest_struct{chara;intb;shortc;};structtest_person{chard;structtest_structf;intg;};intmain(){SetConsoleOutputCP(65001);SetConsoleCP(65001);{printf(test_struct的长度是%d\n,sizeof(structtest_struct));printf(test_person的长度是%d\n,sizeof(structtest_person));};return0;}运行结果分析过程test_struct是一个结构体是4字节对齐它的第一个成员变量应该放到最大成员变量所占字节数的倍数地址上。这种数据对齐方式(不限结构体这样存储其他也是)可以提升访问性能访问对应的变量可以尽可能少的存取周期但是造成了空间的浪费。两个相同结构体成员相同但是排列顺序不同其占空间也是不一样的四、实际场景练习1、需求说明使用结构体描述硬件通信数据的485通信485通信的核心固定格式是帧头固定从机地址功能码数据长度数据段校验和帧尾固定1字节1字节1字节1字节n字节1字节1字节2、实现逻辑先定义帧结构体、实现求校验和函数主机声明485结构体模拟主机发送数据从机接收数据。#includestdio.h#includestdint.h// 1. 定义485通信数据帧---结构体类型typedefstruct{uint8_thead;//帧头uint8_taddr;//设备地址uint8_tfunc;//功能码uint8_tdata_len;//数据长度uint8_tdata[10];//数据区最多存10个字节uint8_tcheck_h;//校验和uint8_tcheck_l;//校验和uint8_ttail_h;//帧尾uint8_ttail_l;//帧尾}RS485_Frame;intmain(){// 模拟主机打包485指令RS485_Frame tx_frame;// 定义发送帧变量// 填充指令给从机发地址1读数据数据段2字节tx_frame.head0x3A;// 帧头固定tx_frame.addr0x11;// 目标从机地址0x11tx_frame.func0x05;// 功能码0x05读数据tx_frame.data_len0x01;// 数据段长度0字节tx_frame.data[0]0x10;// 数据1tx_frame.check_h0x27;tx_frame.check_l0x00;tx_frame.tail_h0x0D;// 帧尾固定tx_frame.tail_l0x0A;// 帧尾固定// 主机发送直接把结构体转成字节流通过485总线发出去printf(主机发送485帧十六进制);uint8_t*tx_buf(uint8_t*)tx_frame;// 结构体转字节指针for(inti0;isizeof(RS485_Frame);i){printf(0x%02X ,tx_buf[i]);}printf(\n);// 模拟从机接收485指令硬件从机用结构体解析RS485_Frame rx_frame;// 定义接收帧变量// 假设从机通过485接收到数据直接存入结构体rx_frametx_frame;// 从机解析指令直接访问结构体成员printf(\n从机解析结果\n);printf(帧头0x%02X\n,rx_frame.head);printf(从机地址%d\n,rx_frame.addr);printf(功能码0x%02X读数据\n,rx_frame.func);printf(数据长度%d字节\n,rx_frame.data_len);printf(数据段0x%02X 0x%02X\n,rx_frame.data[0],rx_frame.data[1]);printf(校验和0x%02X数据有效\n,rx_frame.check_h);printf(帧尾0x%02X\n,rx_frame.tail_h);return0;}运行结果如下通过实践可以看出485通信贴合硬件数据格式发送时打包一个结构体接收时直接解析成员不用拼接字节也不用算偏移。硬件通信最怕字节顺序错结构体固定成员顺序可杜绝bug扩展性好后续增加数据或状态位直接在结构体里添加成员就可以。五、注意事项1、不同编译器以及编译选项的属性系统为它分配的存储空间会有所不同在存储该结构时会按照不同的内存对齐规则进行相关处理。32位的操作系统是按照最大不超过4字节对齐的64位的操作系统按最大的基本类型对齐程序员可以自己指定对齐方式在程序的最上面增加#pragma pack(n),n就是最大对齐方式#includestdio.h#includewindows.h#includemath.h#pragmapack(2);structtest_struct{chara;intb;shortc;};#pragmapack()structtest_person{chard;structtest_structf;intg;};intmain(){SetConsoleOutputCP(65001);SetConsoleCP(65001);{printf(test_struct的长度是%d\n,sizeof(structtest_struct));printf(test_person的长度是%d\n,sizeof(structtest_person));};return0;}运行结果分析过程
RELATED

相关推荐

Linux进程管理:从kill命令到pgrep/pkill的精准进程终止指南

Linux进程管理:从kill命令到pgrep/pkill的精准进程终止指南

1. 从“杀进程”说起:一个看似简单却暗藏玄机的操作在Linux世界里,“杀掉一个进程”大概是每个管理员和开发者都绕不开的日常操作。无论是某个失控的Java服务吃光了内存,还是一个前端开发服务器卡死在前台,亦或是清理掉那些早已完…

📅 2026/9/8 21:10:03
自动驾驶开源数据集全景解析:从KITTI到Waymo的选型与实战指南

自动驾驶开源数据集全景解析:从KITTI到Waymo的选型与实战指南

1. 项目概述:为什么你需要一份自动驾驶数据集“藏宝图”如果你正在研究自动驾驶,或者想入门这个领域,那么你肯定遇到过这个最现实的问题:“我的算法模型,该用什么数据来训练和验证?”这就像厨师没有食材&am…

📅 2026/9/15 4:19:04
深入解析大模型基石:Token分词与Embedding向量化的原理与实践

深入解析大模型基石:Token分词与Embedding向量化的原理与实践

1. 项目概述:从符号到空间的认知跃迁最近在跟一些刚入行的朋友聊大模型,发现一个挺有意思的现象:很多人对“Token”和“Embedding”这两个词耳熟能详,但真要问它们到底是什么、怎么工作的、为什么是LLM的基石,往往就语…

📅 2026/9/15 4:03:45
MORE NEWS

更多资讯

📰

Jev模型服务接入指南:从密钥申请到Codex配置实战

最近几天,Jev这个词突然在开发者社群里刷屏了。打开技术群、刷动态,到处是“Jev模型官网”“Jev密钥”“Jev在Codex里怎么用”这类关键词。但问了一圈,发现一个很尴尬的现象:真正能讲清楚Jev是什么的人,没几个。有人以…

📰

TensorFlow实战指南:从安装部署到与PyTorch对比的2024全解析

如果你点进这篇文章,估计你已经受够了那些把 TensorFlow 吹成“万能神器”的教程。作为一个从 TensorFlow 0.x 时代就开始折腾的过来人,我得先说句实话:TensorFlow 早年确实是出了名的难用,动不动就要建图、开会话、跑 Session&am…

📰

AI搜索排名优化实战:让品牌被任务智能体优先引用

1. 变化已经发生:AI搜索正在改写流量规则,越早看懂越主动过去十年做搜索排名优化,大家盯的是 Google、百度、必应的爬虫和排名算法。关键词密度、外链数量、TDK 设置、页面权重这些老一套,在很多行业依然有效,但一个明…

📰

TensorFlow 2024实战指南:从安装到部署全流程解析

TensorFlow 这个词,凡是碰过机器学习的人基本都绕不开。2015 年谷歌把它开源出来,一度几乎是深度学习的代名词,这几年虽然被 PyTorch 抢了不少风头,但真要论工业落地、移动端部署、大规模分布式训练,TensorFlow 依然是…

📰

2800张YOLO手机检测数据集实战:从标注到部署

2800张的规模说大不大,说小也够用。你要是做过目标检测就知道,数据集这个东西,数量只是表象,真正能决定模型上限的,是数据的分布、标注的一致性以及场景的贴近度。蹭着YOLO系列模型的热度,今天就把这份手机…

📰

AI 代码可视化,Code Wiki 还挺好用!用 TaoToken 统一 Key 打通 Gemini 生成代码图谱

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬