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

【C语言】结构体新的理解

【C语言】结构体新的理解

  • 一、引言
    • 1 介绍
    • 2 分析
  • 二、怎么定义结构体?
    • 1 直接定义结构体变量
    • 2 定义一个结构体“类型”
    • 3 定义结构体“类型”,且typedef指定别名
  • 三、typedef的用法
    • 1 最基本的用法
    • 2 与define的区别
      • 2.1 原理不同
      • 2.2 功能不同
      • 2.3 作用域不同
      • 2.4 对指针的操作不同
    • 3 typedef为基本数据类型取“别名”
    • 4 typedef为“自定义数据类型”取“别名”
    • 5 为数组起别名
    • 6 typedef为指针取“别名”
    • 四、结构体指针
    • 五、回顾最开始的问题

一、引言

1 介绍

最近在看ESP32的I2C程序时,看到一条语句,不太理解,于是记录一下。

/*** @brief Type of I2C master bus handle*/
typedef struct i2c_master_bus_t *i2c_master_bus_handle_t;
struct i2c_master_bus_t {i2c_bus_t *base;                 // bus base classSemaphoreHandle_t bus_lock_mux;  // semaphore to lock bus processint cmd_idx;                     //record current command index, for master mode_Atomic i2c_master_status_t status;    // record current command status, for master mode
.
.
.
.i2c_transaction_t i2c_trans_pool[];    // I2C transaction pool.
};

应用的时候,配置I2C总线。

#include "driver/i2c_master.h"i2c_master_bus_config_t i2c_mst_config = {.clk_source = I2C_CLK_SRC_DEFAULT,.i2c_port = TEST_I2C_PORT,.scl_io_num = I2C_MASTER_SCL_IO,.sda_io_num = I2C_MASTER_SDA_IO,.glitch_ignore_cnt = 7,.flags.enable_internal_pullup = true,
};i2c_master_bus_handle_t bus_handle;
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_mst_config, &bus_handle));

2 分析

结合上面的程序,i2c_master_bus_handle_t是一个i2c_master_bus_t类型的结构体指针
看下面这个语句

i2c_master_bus_handle_t bus_handle;

应该是用结构体指针,定义一个i2c_master_bus_t类型的结构体变量,结构体变量名是bus_handle。
指针只能等于同类型变量的地址(指针 = &变量)。
问题是:
用了typedef之后,指针可以定义变量吗?
下面梳理一下。

二、怎么定义结构体?

1 直接定义结构体变量

struct{char no[20];        //学号char name[20];      //姓名char sex[5];      //性别int age;          //年龄
}stu1,stu2;    

上面stu1;stu2是两个结构体变量,如果想要再定义一个相同的结构体变量,还需要重新做完整的定义,如下所示。

struct{char no[20];        //学号char name[20];      //姓名char sex[5];      //性别int age;          //年龄
}stu3;    

显而易见,如果每次都这样写,比较麻烦。

2 定义一个结构体“类型”

struct student{char no[20];        //学号char name[20];      //姓名char sex[5];      //性别int age;          //年龄
};    

上面的struct student就是一个类型,相当于int,char
可以用这个结构体类型,定义结构体变量,如下:

struct student stu1;

int,char可以在定义变量的同时,为变量赋初值。
相对应的
可以在定义结构体类型的同时,定义结构体变量(可不可以同时赋初值,待定)
如下面定义一个结构体类型的同时,定义一个stu1的结构体变量:

struct student{char no[20];        //学号char name[20];      //姓名char sex[5];      //性别int age;          //年龄
}stu1;    

后面可以再用struct student这个结构体类型定义其他的变量。

这样看,每次定义变量都需要写struct student这个结构体类型,也比较麻烦。
所以有下面这种方法,用typedef为struct student指定一个别名。

3 定义结构体“类型”,且typedef指定别名

typedef struct student{char no[20];       //学号char name[20];    //姓名char sex[5];    //性别int age;          //年龄
}STUDENT;
STUDENT stu1;

上面STUDENT就等于struct student
可以用STUDENT定义struct student的结构体变量。

STUDENT stu1;

总结:
也就是说:
struct student是一个结构体类型,通过typedef为结构体类型指定别名STUDENT之后。
STUDENT可以定义变量。
那么如果使用typedef为int 或者char指定别名之后,是否可以通过这个“别名”定义变量呢??

三、typedef的用法

typedef最核心的思想就是
为已有的类型起一个别名
自己定义的结构体类型,也可以用typedef起别名。
既然是“为类型起别名”,那么也可以用别名定义该类型的变量

1 最基本的用法

最开始是和define一块认识的,所以最基本的用法就是起别名。

#define unsigned int uint
typedef uint unsigned int

2 与define的区别

在C语言中,typedef和define都是用来起别名的关键字,但它们的应用方式和效果却存在明显差异。typedef用于为已有的数据类型创建新的名称,而define则用于定义预处理宏,在编译时会被替换为指定的文本。

2.1 原理不同

#define是C语言中定义的语法,是预处理指令,在预处理时进行简单而机械的字符串替换,不作正确性检查,只有在编译已被展开的源程序时才会发现可能的错误并报错。

typedef是关键字,在编译时处理,有类型检查功能。它在自己的作用域内给一个已经存在的类型一个别名,但不能在一个函数定义里面使用typedef。
用typedef定义数组、指针、结构体等类型会带来很大的方便,不仅使程序书写简单,也使意义明确,增强可读性。

2.2 功能不同

typedef用来定义类型的别名,起到类型易于记忆的功能。
另一个功能是定义机器无关的类型,也就是前面在“定义结构体”时说的,使用typedef定义一个结构体的类型。

这么看,typedef确实有“定义类型”的作用。
使用typedef定义完类型,就可以用这个类型定义变量。

#define不只是可以为类型取别名,还可以定义常量、变量、编译开关等。

typedef在创建新类型时,只是为已有类型赋予别名。它只在编译器阶段有效,不会引起任何代码替换。使用typedef给基本类型或复杂类型取别名,可以集中管理多个相似的类型,提高代码的可读性和可维护性。

相比之下,define是在预处理阶段进行文本替换。预处理器会根据宏定义中指定的文本,将代码中所有的宏调用替换为对应的文本。这种替换是简单的文本替换,没有类型检查和语法分析。

2.3 作用域不同

#define没有作用域的限制,只要是之前预定义过的宏,在以后的程序中都可以使用。
typedef有自己的作用域。

可以理解为
define的作用域是全局
typedef有限
但具体怎么有限,暂时不清楚。

2.4 对指针的操作不同

这里简单写一个不同。
define和普通用类型定义定义指针一样,只对第一个变量起作用。

int *a,b;

a为int指针变量,b为int变量

#define INT int*
INT a,b;

a为int指针变量,b为int变量
typedef对后面多个变量起作用。

typedef int* INT
INT a,b;

a和b均为int类型的指针。

3 typedef为基本数据类型取“别名”

typedef unsigned int uint;

使用uint就是unsigned int

4 typedef为“自定义数据类型”取“别名”

typedef struct {int x;int y;
} ;typedef enum {RED,GREEN,BLUE
} Color;

可以使用PointColor这两个类型,定义该类型下的变量。

Point point;
Color color;

5 为数组起别名

typedef char arr_name[20];

上面这个语句,好像没有“别名”
typedef A B也就是没有B。

这是一个 C 语言中的类型定义语句,用于定义一个名为arr_name的数组类型,数组元素类型为 char,数组长度为 20。
具体来说,typedef char arr_name[20]; 定义了一个名为 arr_name数组类型,它包含了 20 个 char 类型的元素。
通过这个类型定义,可以使用 arr_name 来声明一个长度为 20 的字符数组,而不必每次都写出完整的数组声明语句。
例如,使用这个类型定义可以这样声明一个长度为 20 的字符数组:

arr_name my_array;

这样就等同于以下完整的数组声明:

char my_array[20];

这种类型定义可以使代码更加简洁和易读,特别是在多处需要声明相同类型的数组时。

这么看来,使用typedef之后,确实可以用后面的“别名”,去定义一个变量。

6 typedef为指针取“别名”

用着的时候再研究

四、结构体指针

自定义一个结构体类型,也可以定义指向某结构体类型的结构体指针。

//定义结构体类型(struct Person)(Per)
typedef struct Person
{char name[10];int age;char job[];int annual_salary;
}Per;int main(void)
{Per Lihua;//用结构体类型定义一个Lihua的结构体//struct Person Lihua;//等效Lihua.age = 18;//访问结构体成员用`.`。Per *Lihua_dad;//定义指向Per类型的结构体指针,名为Lihua_dadLihua_dad = &Lihua;//Lihua_dad和Lihua均为struct Person类型。左边是结构体指针,右边是结构体。将右边取地址,可赋值给左边。Lihua_dad->age = 20;//通过结构体指针,访问结构体的成员(应该是结构体指针的特殊用法)(用的最多)(通过结构体访问时,用`->`)//*(Lihua_dad+1) = 20;//应该是结构体指针的标准用法,符合指针用法(在结构体指针上,很少用)(理论可行)(未验证)//*(Lihua_dad).age = 20;//已验证//Lihua_dad[1] = 20;//指针类似数组的访问方法(理论可行)(未验证)
}

五、回顾最开始的问题

这么综合看下来,typedef核心思想就是“起别名”。
最开始的语句

/*** @brief Type of I2C master bus handle*/
typedef struct i2c_master_bus_t *i2c_master_bus_handle_t;

如果是

typedef struct i2c_master_bus_t i2c_master_bus_handle_t;

含义就是为struct i2c_master_bus_t起了一个别名为i2c_master_bus_handle_t
可以用i2c_master_bus_handle_t来定义struct i2c_master_bus_t类型的结构体。
所以,

typedef struct i2c_master_bus_t *i2c_master_bus_handle_t;

应该改为下面的形式更好理解

typedef struct i2c_master_bus_t* i2c_master_bus_handle_t;

这么看,可以用struct i2c_master_bus_t*类型定义结构体指针变量
起别名之后,就可以用i2c_master_bus_handle_t别名后的类型,定义结构体指针变量

即,

i2c_master_bus_handle_t bus_handle;

就相当于

struct i2c_master_bus_t* bus_handle;

该语句的含义是定义结构体类型的指针变量。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Day05-Unity Time类
  • element el-popover组件 查看示例图片功能实现
  • 【记录】基于Windows系统安装rust环境的过程
  • [报错] nvcc -V 找不到
  • vue3中批量下载文件(压缩包)功能
  • Linux学习之路 -- systemV进程通信 -- 消息队列和信号量(简单介绍)
  • ssrf实现
  • springboot+vue+mybatis计算机毕业设计电子产品交易系统+PPT+论文+讲解+售后
  • iview checkbox单独使用时 如何去掉显示的true和false以及不显示文字
  • VBA技术资料MF180:将某个文件夹中的某类图片导入Word
  • sqlite blob 数据检索(基于sqlite3_get_table的优化)
  • 如何使用Gitee管理自己的项目
  • 【自由能系列(初级)】自由能原理——神经科学的“能量守恒”方程
  • 惠海H6900B升压恒流调光IC芯片3.7V7.4V12V升压18V24V36V 48V 植物灯/电解水
  • 娱乐社交、游戏等行业共探合规前提下,实现产品可持续的增长与营收 | 网易数智x华为云泛娱乐行业沙龙-杭州站邀您前来!
  • php的引用
  • [译] 怎样写一个基础的编译器
  • 2017-08-04 前端日报
  • Angular4 模板式表单用法以及验证
  • bearychat的java client
  • conda常用的命令
  • ES学习笔记(12)--Symbol
  • JS字符串转数字方法总结
  • Promise初体验
  • Redis 懒删除(lazy free)简史
  • Shadow DOM 内部构造及如何构建独立组件
  • spring boot下thymeleaf全局静态变量配置
  • 安卓应用性能调试和优化经验分享
  • 创建一个Struts2项目maven 方式
  • 关于Java中分层中遇到的一些问题
  • 小而合理的前端理论:rscss和rsjs
  • 优秀架构师必须掌握的架构思维
  • ionic异常记录
  • 国内唯一,阿里云入选全球区块链云服务报告,领先AWS、Google ...
  • ​HTTP与HTTPS:网络通信的安全卫士
  • ​低代码平台的核心价值与优势
  • # Panda3d 碰撞检测系统介绍
  • # wps必须要登录激活才能使用吗?
  • $ git push -u origin master 推送到远程库出错
  • $.proxy和$.extend
  • (02)Hive SQL编译成MapReduce任务的过程
  • (4.10~4.16)
  • (arch)linux 转换文件编码格式
  • (delphi11最新学习资料) Object Pascal 学习笔记---第13章第6节 (嵌套的Finally代码块)
  • (不用互三)AI绘画:科技赋能艺术的崭新时代
  • (第61天)多租户架构(CDB/PDB)
  • (二十三)Flask之高频面试点
  • (附源码)python房屋租赁管理系统 毕业设计 745613
  • (四) 虚拟摄像头vivi体验
  • (四)进入MySQL 【事务】
  • (转)Java socket中关闭IO流后,发生什么事?(以关闭输出流为例) .
  • .net 按比例显示图片的缩略图
  • .net 提取注释生成API文档 帮助文档
  • .net 托管代码与非托管代码
  • .NET 指南:抽象化实现的基类