C语言的结构体

在C语言中,普通变量只能存单个数据(一个数字或者一个字符),但是结构体可以把一组相关的、不同类型的数据捆成一个整体。

即:结构体可以把一个数据元素的各个不同的数据项聚合为一个整体 ,是一种自定义数据类型。核心作用是把多个不同类型的变量打包在一起,用来描述一个事物的多个属性。

本文主要介绍结构体的定义、引用,最后并通过一个实例来测试实际场景中的应用

一、声明格式

struct 结构体名{

成员表列;

};

例如定义学生这个结构体:

c 复制代码
struct Student {
    int id;
    char name[20];
    int age;
    float score;
};
struct Student stu1;

定义完结构体类型后,还没有创建实际变量,不占内存

实际使用还需需要进行声明(创建具体的变量,可真正存数据的对象),如上面的struct Student stu1

二、引用

对结构体变量的运算必须通过成员运算符"."来访问结构体变量的成员,方式如下
结构体变量名.成员名

c 复制代码
int main() {
    // 2. 定义结构体变量
    struct Student stu1;
    // 3. 给成员赋值
    stu1.id = 888;
    strcpy(stu1.name,"王五");
    stu1.age = 18;
    stu1.score = 99.5;

    // 4. 访问成员并打印
    printf("学号:%d\n",stu1.id);
    printf("姓名:%s\n",stu1.name);
    printf("年龄:%d\n",stu1.age);
    printf("成绩:%.1fd\n",stu1.score);

    return 0;
}

编译运行后的结果如下:

三、结构体的特点
允许存在多种类型 :可以把int、char、float、数组甚至其他结构体打包在一起;
自定义性强 :可以根据需求创建任意类型
整体管理:一个结构体变量能代表一个完整事物,不用维护一堆零散变量。

四、实际场景练习

1、需求说明

使用结构体描述硬件通信数据的485通信

485通信的核心固定格式是:

zhen tou

帧头(固定) 从机地址 功能码 数据长度 数据段 校验和 帧尾(固定)
1字节 1字节 1字节 1字节 n字节 1字节 1字节

2、实现逻辑

先定义帧结构体、实现求校验和函数,主机声明485结构体,模拟主机发送数据,从机接收数据。

c 复制代码
#include <stdio.h>
#include <stdint.h>

// 1. 定义485通信数据帧---结构体类型
typedef struct {
    uint8_t head;     //帧头
    uint8_t addr;     //设备地址
    uint8_t func;     //功能码
    uint8_t data_len; //数据长度
    uint8_t data[10]; //数据区:最多存10个字节
    uint8_t check_h;    //校验和
    uint8_t check_l;    //校验和
    uint8_t tail_h;     //帧尾
    uint8_t tail_l;     //帧尾
}RS485_Frame;


int main() {
    // ===================== 模拟主机:打包485指令()=====================
    RS485_Frame tx_frame;  // 定义发送帧变量
    // 填充指令(给从机发:地址1,读数据,数据段2字节)
    tx_frame.head = 0x3A;      // 帧头固定
    tx_frame.addr = 0x11;      // 目标从机地址=0x11
    tx_frame.func = 0x05;      // 功能码=0x05(读数据)
    tx_frame.data_len = 0x01;  // 数据段长度=0字节
    tx_frame.data[0] = 0x10;   // 数据1
    tx_frame.check_h = 0x27;
    tx_frame.check_l = 0x00;
    tx_frame.tail_h = 0x0D;      // 帧尾固定
    tx_frame.tail_l = 0x0A;      // 帧尾固定
    // 主机发送:直接把结构体转成字节流,通过485总线发出去
    printf("主机发送485帧(十六进制):");
    uint8_t *tx_buf = (uint8_t*)&tx_frame;  // 结构体转字节指针
    for(int i=0; i<sizeof(RS485_Frame); i++) {
        printf("0x%02X ", tx_buf[i]);
    }
    printf("\n");

    // ===================== 模拟从机:接收485指令(硬件从机用结构体解析)=====================
    RS485_Frame rx_frame;  // 定义接收帧变量
    // 假设从机通过485接收到数据,直接存入结构体
    rx_frame = tx_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);

    return 0;
}

运行结果如下:

通过实践可以看出:

485通信贴合硬件数据格式,发送时打包一个结构体,接收时直接解析成员,不用拼接字节也不用算偏移。

硬件通信最怕字节顺序错,结构体固定成员顺序,可杜绝bug;

扩展性好,后续增加数据或状态位,直接在结构体里添加成员就可以。

五、注意事项

1、结构体数据的空间中,可能产生填充信息

原因:大多数处理器,访问按照字或半字对齐的数据速度更快,在定义结构体时,编译器为了性能优化,可能会将他们按照半字或字对齐。

所以会出现:两个相同结构体,成员相同,但是排列顺序不同,其占空间也是不一样的

2、不同编译器以及编译选项的属性,系统为它分配的存储空间会有所不同

在存储该结构时会按照不同的内存对齐规则进行相关处理。

相关推荐
计算机安禾3 小时前
【数据结构与算法】第18篇:数组的压缩存储:对称矩阵、三角矩阵与稀疏矩阵
c语言·开发语言·数据结构·c++·线性代数·算法·矩阵
maverick_1113 小时前
【FPGA】关于两个数相加的“坑”
c语言·matlab·fpga开发
计算机安禾3 小时前
【数据结构与算法】第17篇:串(String)的高级模式匹配:KMP算法
c语言·数据结构·学习·算法·visual studio code·visual studio·myeclipse
水饺编程4 小时前
第4章,[标签 Win32] :SysMets3 程序讲解02,iVertPos
c语言·c++·windows·visual studio
code_whiter5 小时前
C\C++5(内存管理)
c语言·c++
HABuo5 小时前
【linux线程(二)】线程互斥、线程同步、条件变量详细剖析
linux·运维·服务器·c语言·c++·ubuntu·centos
Rabitebla5 小时前
归并排序(MergeSort)完全指南 —— 从原理到非递归实现
c语言·数据结构·c++·算法·排序算法
寒秋花开曾相惜5 小时前
(学习笔记)3.9 异质的数据结构(3.9.1 结构)
c语言·网络·数据结构·数据库·笔记·学习
福楠5 小时前
现代C++ | C++14甜点特性
linux·c语言·开发语言·c++