LwIP系列(1):C语言宏定义相关基础知识(##、include 文件、宏函数、预编译)

news/2024/11/7 18:33:36/

前言

对于嵌入式物联网技术来说,TCP/IP 协议几乎是不能绕过的,常见socket、tcp、udp、mqtt、coap、modbus-tcp、mdns、广播、组播等等,均是基于TCP/IP协议实现,无处不在。而目前在嵌入式领域,使用最多的TCP/IP协议栈就是LwIp,所以本系列尝试着从LwIP的详细分析,来入门学习TCP/IP协议。
在LwIP中,使用了很多高级的C语言用法,如果不了解这些高级用法,我们很难清楚的了解其实现原理,我们先从宏定义的高级用法来入手,因为在LwIP中的枚举定义、内存分配、协议配置等等,均通过宏定义协助实现。

函数宏

函数宏,也叫类函数宏,其宏定义有相关的模板,一般是类似函数的形式,通过( ) 括号内增加参数来表示,需要特别说明的是,函数宏中的参数就是简单的替换。我们先看几个实例:

示例1

#include <stdio.h>#define max(a, b)   ((a > b) ? (a) : (b))#define func(a, b)  a + bint main(void)
{int a, b;a = 2;b = 3;printf("max:%d\n", max(3, 2));printf("func * 3 = %d\n", func(2, 3) * 3);return 0;
}

运行结果:

max:3
func * 3 = 11

注意: 从上述计算结果可知,宏定义就是替换,所以 func 3 结果是11,即 2 + 33 = 11,而不是我们(2 + 3)* 3, 如果想要这样的效果,则需要添加(), 即,我们在使用函数宏时,不要吝啬( ) 括号的使用。

‘##’ 拼接宏

## 宏是将两边的参数拼接成1个参数,## 两边可以有空格,
示例:

#define t(x,y,z) x ## y ## z
int j[] = { t(1,2,3), t(,4,5), t(6,,7), t(8,9,),
t(10,,), t(,11,), t(,,12), t(,,) };

经过预编译后,结果如下:

int j[] = {123, 45, 67, 89,10, 11, 12,  };

在lwip中,为了实现可配置,比如我们只使用UDP、TCP,通过宏配置就能够实现,比如下面的代码:

typedef enum {
#define LWIP_MEMPOOL(name,num,size,desc)  MEMP_##name,
#include "lwip/priv/memp_std.h"MEMP_MAX
} memp_t;

这里面有3种宏用法,分别是:
(1)函数宏,LWIP_MEMPOOL(name,num,size,desc), 编译器在预编译时,将LWIP_MEMPOOL(name,num,size,desc) 替换为MEMP_##name,其中name为参数。
(2)## 拼接宏,编译器在预编译时,将MEMP和 参数name 拼接成1个新的宏。
(3) #include xxxx, include 引用,编译器在编译时,会将lwip/priv/memp_std.h 的内容拷贝到当前位置.
所以, 经过预编译,会将memp_std.h 拷贝到当前枚举定义位置,然后将其中的LWIP_MEMPOOL 函数替换为 MMEP_name,最终如下所示:

typedef enum { 
/* #line 1 "..\\..\\Middlewares\\lwip\\src\\include\\lwip/priv/memp_std.h" */
MEMP_UDP_PCB,
MEMP_TCP_PCB, MEMP_TCP_PCB_LISTEN, MEMP_TCP_SEG,
MEMP_REASSDATA,
MEMP_FRAG_PBUF,
/* #line 92 "..\\..\\Middlewares\\lwip\\src\\include\\lwip/priv/memp_std.h" */
MEMP_SYS_TIMEOUT,
/* #line 111 "..\\..\\Middlewares\\lwip\\src\\include\\lwip/priv/memp_std.h"  */
MEMP_PBUF, MEMP_PBUF_POOL,
/* #line 55 "..\\..\\Middlewares\\lwip\\src\\include\\lwip/memp.h"  */
MEMP_MAX 
} memp_t;

这样就实现了,通过宏定义,实现了自动变化 宏定义枚举定义。

通过Keil输出预编译文件,辅助读lwip源码

在lwip中,有很多地方都会上面提到的宏方法来定义变量、函数。我们在开始看代码的时候,会觉得懵逼,如果上来就陷到解析代码中,效率会非常低,所以我们可以通过借助IDE输出预编译文件来查看宏替换后的文件结果。以Keil为例,通过设置:
Options for Target / List / C Preprocessor Listing 即可将预编译结果输出到指定目录,如下图所示:
在这里插入图片描述
输出文件格式为.i*, 我们可以直接使用文本编辑器查看。


http://www.ppmy.cn/news/99913.html

相关文章

C++ 中基础的几种变量作用域,类作用域(C++复习向p5)

文章目录 三种变量变量作用域初始化变量类作用域 三种变量 局部变量&#xff1a;函数/代码块中的变量形式参数&#xff1a;函数参数中定义的变量&#xff0c;在函数体中有效全局变量&#xff1a;所有函数外部声明的变量 变量作用域 局部作用域&#xff1a;局部变量在函数执行…

C++程序设计基础【一】

C程序设计基础【一】 一、一个程序的开发步骤1.编辑程序2.编译程序3.链接程序4.执行程序5.测试 2.基础代码解读1.预处理指令(#include <iostream>)2.块注释(/* */)3.行注释(//)4.using namespace std5.int main()6.{}7.std::cin、std::cout、std::endl8.return 0 二、变量…

哈希表(Hash Table)原理和代码

哈希表&#xff08;Hash Table&#xff09;是一种高效的数据结构&#xff0c;用于存储键-值对&#xff08;Key-Value pairs&#xff09;。它通过将键映射到数组的索引位置来实现快速的插入、查找和删除操作。哈希表的核心原理是使用哈希函数将键转换为对应的数组索引&#xff0…

数据分析之Pandas--数据检索

数据分析之Pandas&#xff08;03&#xff09;--数据检索 pandas的数据检索功能是其最基础也是最重要的功能之一。 pandas中最常用的几种数据过滤方式如下&#xff1a; 1. 行列过滤&#xff1a;选取指定的行或者列 2. 条件过滤&#xff1a;对列的数据设置过滤条件 3. 函数过…

KVM虚拟化技术学习-KVM管理

二&#xff0c;KVM管理 1.升级配置 1.创建一个空磁盘卷 [rootlocalhost ~]# qemu-img create -f qcow2 /kvm/images/disk2.qcow2 5G Formatting disk2.qcow2, fmtqcow2 size5368709120 encryptionoff cluster_size65536 lazy_refcountsoff 2.修改配置文件 <disk typefi…

如何使用 service account 获取 keycloak 的用户信息

Keycloak 是一个开源的权限管理和认证系统。使用 Keycloak 可以让开发者专注于解决业务的核心问题。获取用户信息是权限管理和认证系统需要的基本功能。Service Account 是OAuth 2.0推荐的系统服务使用的账户&#xff0c;开发者可以通过 Keycloak 的 Service Account 来让自己的…

ChatGPT自动生成思维导图

&#x1f34f;&#x1f350;&#x1f34a;&#x1f351;&#x1f352;&#x1f353;&#x1fad0;&#x1f951;&#x1f34b;&#x1f349; ChatGPT自动生成思维导图 文章目录 &#x1f350;问题引入&#x1f350;具体操作markmapXmind &#x1f433;结语 &#x1f…

idea不识别yml文件了

添加上这两个就好了