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

【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 class
    SemaphoreHandle_t bus_lock_mux;  // semaphore to lock bus process
    int 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_dad
	Lihua_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;

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


http://www.kler.cn/a/288913.html

相关文章:

  • 基于STM32的智能家居安防系统设计
  • [Linux网络编程]10-http协议,分别使用epoll和libevent两种方式实现B/S服务器
  • A3超级计算机虚拟机,为大型语言模型LLM和AIGC提供强大算力支持
  • linux上海康SDK安装并设置环境变量
  • Dockerfile的使用
  • Gartner发布安全平台创新洞察:安全平台需具备的11项常见服务
  • css重置样式表 reset.css 格式化默认css样式
  • JavaWeb基础 -- SpringMVC请求和响应
  • Unity 3D学习资料集合
  • 山东大学OLED透明展示柜案例:科技赋能,创新展示新体验
  • 使用HTTP代理注意的点
  • shell脚本发送随机请求
  • 计算机视觉之 GSoP 注意力模块
  • 《第二十六章 IO 流 - 字节流》
  • 在项目中使用 redis存储 数据,提高 项目运行速度
  • 【Linux】 理解 Linux 中的 `dup2` 函数
  • Spring框架中的@EventListener注解浅谈
  • 【C++ Primer Plus习题】8.2
  • 直播路由器的原理是什么
  • Linux CentOS 7.39 安装mysql8
  • rabbitmq发送的消息接收不到
  • 告别文档处理烦恼,PDF Guru Anki一键搞定所有
  • 多目标应用:基于双存档模型的多模态多目标进化算法(MMOHEA)的移动机器人路径规划研究(提供MATLAB代码)
  • C语言之猜数字小游戏
  • 【苍穹外卖】Day3 菜品接口
  • dinput8.dll错误应该如何修复呢?五种快速修复dinput8.dll错误的问题