【C语音进阶】分享一个宏定义的小小窍门

架构师李肯 发表于 2022/11/30 23:37:19 2022/11/30
【摘要】 这个小窍门,学过C语言的你,了解过吗?

【C语音进阶】分享一个宏定义的小小窍门

这个小窍门,学过C语言的你,了解过吗?

1 问题描述

事情是这样的,前两天有个同事,突然微信截了个代码给我看,说他对这个代码非常差异,简直超出了他的想象,是关于C语言宏定义的,你也可以看下,你知道其中的奥秘吗?

image-20221203222246706

同事跟我说:“ 这代码难道不会编译报错吗?

2 问题分析

为了验证他这个疑问,我决定亲自写一段示例代码验证下,如下:

#include <stdio.h>
#include <string.h>

#define TEST_MACRO(vvv)   test_func(argc, argv, vvv)

static int test_func(int argc, char *argv[], char *value)
{
	int i = 0;

	for (i = 0; i < argc; i++) {
		if (strstr(argv[i], value)) {
			return 1;
		}	
	}
		
	return 0;
}

int main(int argc, char *argv[])
{
	if (TEST_MACRO("xxx")) {
		printf("OK\r\n");
	} else {
		printf("FAIL\r\n");
	}
}

我们使用gcc编译器编译运行下看看:

编译没有报错 gcc -o test test.c
运行一个失败的测试用例没有问题 ./test abc
FAIL
运行第二个成功的测试用了依然没有 ./test xxx
OK

那我们分析一下,为何这段代码是没有任何语法问题的呢。

我们主要看 TEST_MACRO宏定义的写法:

#define TEST_MACRO(vvv) test_func(argc, argv, vvv)

当我们调用这个宏定义的时候,根据宏定义的展开规则,我们知道它会被展开为:

if (TEST_MACRO(“xxx”)) {

展开之后变成:

if (test_func(argc, argv, “xxx”)) {

也就是说,main函数这变成了

int main(int argc, char *argv[])
{
	if (test_func(argc, argv, "xxx")) { //test_func 函数在上面定义了,自然是能够找到这个函数的
		printf("OK\r\n");
	} else {
		printf("FAIL\r\n");
	}
}

这么一看,你说他能有语法问题吗?完全没有啊!

3 知识点总结

  • 宏定义仅仅是 字符替换,不会对语句进行任何的语法规则检验;记住这一句话,你可以解决大部分的宏定义问题;
  • 带参数的宏定义与带参数的函数,一定要区分开来;两者有个非常大的不同就是:
    • 带参数的宏定义仅仅是在预处理阶段展开;无任何数据类型校验;
    • 带参数的函数时需要运行时,经过传参执行的;需要有严格的数据类型校验;

4 扩展延伸

思考两个问题:

  • 为何原工程中要这么子写,这不是增加了代码阅读的难度吗?
  • 如果是复杂的宏定义,我如何在编译阶段就知道宏定义被展开之后的样子,以便于更快解决语法报错的代码?

先回答第一个问题:

由于我们只看到了代码片段,并没有一览全局代码,也许他的代码需要支持N个cli命令行输入;如果每一个命令都使用xxx_contain这种写法,会显得比较呆板;而当它换成宏定义的时候,原本3个传参的xxx_contain函数就变成了仅有一个参数的宏定义,这个输入参数仅需要关注你需要判断的字符串标签;这样看起来整个代码是比较整洁的。

再回答第二个问题:

由于宏定义的展开是在预编译阶段处理的,使用gcc编译器的话,可以通过 -save-temps=obj 参数,让编译器在生成最终的可执行文件的时候也同时生成预处理后的文件。如上面我写的示例代码,通过这种方法得到的预处理后的代码为:

static int test_func(int argc, char *argv[], char *value)
{
int i = 0;

for (i = 0; i < argc; i++) {
if (strstr(argv[i], value)) {
return 1;
}
}

return 0;
}

int main(int argc, char *argv[])
{
if (test_func(argc, argv, "xxx")) {
printf("OK\r\n");
} else {
printf("FAIL\r\n");
}
}

看吧,与我们的猜想分析是一毛一样的。

5 更多分享

架构师李肯

架构师李肯全网同名),一个专注于嵌入式IoT领域的架构师。有着近10年的嵌入式一线开发经验,深耕IoT领域多年,熟知IoT领域的业务发展,深度掌握IoT领域的相关技术栈,包括但不限于主流RTOS内核的实现及其移植、硬件驱动移植开发、网络通讯协议开发、编译构建原理及其实现、底层汇编及编译原理、编译优化及代码重构、主流IoT云平台的对接、嵌入式IoT系统的架构设计等等。拥有多项IoT领域的发明专利,热衷于技术分享,有多年撰写技术博客的经验积累,连续多月获得RT-Thread官方技术社区原创技术博文优秀奖,荣获CSDN博客专家CSDN物联网领域优质创作者2021年度CSDN&RT-Thread技术社区之星2022年RT-Thread全球技术大会讲师RT-Thread官方嵌入式开源社区认证专家RT-Thread 2021年度论坛之星TOP4华为云云享专家(嵌入式物联网架构设计师)等荣誉。坚信【知识改变命运,技术改变世界】!

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区),文章链接,文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至:cloudbbs@huaweicloud.com进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。