首页 > 其他分享 >C语言宏(macro)小技巧

C语言宏(macro)小技巧

时间:2023-06-10 19:33:07浏览次数:44  
标签:__ 技巧 macro MUX C语言 COND text DEF define

目录

字符串化运算符(stringizing operator)

运算符 # 在宏中会将实参直接转化为字符串literal,也就是字符串常量,举个简单的例子:

#define arg2str(p) #p
puts(arg2str(666)); // 宏展开后变成如下
puts("666")

简单却有用的使用场景,计算一个表达式,将表达式和结果都一起输出:

#define calc(exp) printf( #exp " = %f ", exp )
calc( 4 * atan(1.0)); // 宏展开后如下
printf( "4 * atan(1.0) = %f ", 4 * atan(1.0));

记号粘贴运算符(token-pasting operator)

运算符 ## 在宏中会将两个符号连接起来,因此我更喜欢把它叫做连接符,举个简单的例子:

#define text_1 "hello"
#define myconcat() text_ ## 1
puts(myconcat()); // 宏展开如下
puts("hello")

可以看到就是将text_和1作为符号连接起来。再举一个带参数的例子,一般带参数的才有使用场景

#define text_1 "hello"
#define text_2 "world"
#define myconcat2(p) text_ ## p
puts(myconcat2(1));
puts(myconcat2(2));
// 宏展开分别为如下
puts("hello");
puts("world");

至此,我们还需要注意一个关键点,无论是什么样的宏,传入宏的实参只会被当作一个符号,而不会被识别成变量、宏或是别的东西并进行展开,可能不好理解,举个例子说明:

#define EXPAND 1
#define text_1 "hello"
#define myconcat(p) text_ ## p
puts(myconcat(EXPAND));
// 宏展开后报错:未定义标识符text_EXPAND

可以看到当我们将EXPAND作为实参传进去时,EXPAND只是被简单连接在text_的后面,而不是先展开成1再连接。类似地,如果实参EXPAND是一个变量而不是宏,比如

int EXPAND = 1;

那么相信你更可以确定实参只是一个符号而不是被展开成变量存储的值,因为宏展开是在预处理阶段(甚至还没到编译阶段),还没有变量的概念,自然也就无法“展开”这个变量

再进一步地,看一下这个例子

#define EXPAND 1
#define text_1 "hello"
#define myconcat(p) text_ ## p
#define myconcat2(p) myconcat(p)
puts(myconcat2(EXPAND)); // 宏展开如下
puts("hello");

可以看到我们多封装了一层宏,这样就不会报错了。至此,我们可以总结出,实参只是作为一个符号,但形参是根据实际来决定是否展开的,例如myconcat2中,EXPAND传进去后,形参p并没有进行 # 或 ##运算,因此被展开为1并传入myconcat,最终成功得到text_1

二选一选择器宏(mux)

有时候我们要根据宏是否存在从而选择编译哪部分的代码,比如

#define COND 1

#ifdef COND
	printf("hello");
#else
	printf("world");
#endif

上面的代码在预处理过后只保留了第一个printf,这个不必多说

printf("hello");

但是这种使用也有他的局限性,比如我们要根据宏是否定义从而来决定一个变量的值

#define COND 1

#ifdef COND
	int var = val1;
#else
	int var = val2;
#endif

可以看到这种实现方式很麻烦和啰嗦,如果有很多地方都有这种判断,而且不能一次性写在一起的话,代码中就会充斥大量的ifdef else endif。下面要介绍的宏二选一选择器,是我写某个项目时学到的

先看看怎么用这个宏简化上面的var赋值代码

#define COND 1
int var = MUXDEF(COND, val1, val2); // 展开后如下
int var = val1;

MUXDEF就是我们的主角,二选一选择器宏,传入的参数就不多说了,对比上面的“繁琐版”就知道是什么意思,可以看到这个宏实现了make your life easier(

除了二选一选择值,你也可以用来分支代码段

#define COND 1
MUXDEF(COND, printf("hello"), (puts("world1"), puts("world2"))); // 展开后如下
printf("hello");

使用十分简单,但实现却十分巧妙,下面看看这个宏怎么实现的:

#define concat(x, y) x ## y

#define CHOOSE2nd(a, b, ...) b
#define MUX_WITH_COMMA(contain_comma, a, b) CHOOSE2nd(contain_comma a, b)
#define MUX_MACRO_PROPERTY(p, macro, a, b) MUX_WITH_COMMA(concat(p, macro), a, b)

#define __P_DEF_1  X,

#define MUXDEF(macro, X, Y)  MUX_MACRO_PROPERTY(__P_DEF_, macro, X, Y)

下面的宏依赖于上面的宏,我们从上往下看

  • concat只是用来语义化连接操作的
  • CHOOSE2nd很明显,传入两个或以上的参数,并替换为第二个参数
  • MUX_WITH_COMMA是核心,十分重要,但强行解释没意思,下面用例子来解释
  • MUX_MACRO_PROPERTY也很明显,不解释
  • __P_DEF_1MUX_WITH_COMMA结合生效,下面用例子解释
  • MUXDEF我们的主角

直接看例子吧,然后通过一步步宏展开来看,首先是COND=1的情况

#define COND 1
int var = MUXDEF(COND, 1, 2); // 宏展开如下
int var = 1;

将第二行的宏一步步展开(下一行是当前行的展开)

MUXDEF(COND, 1, 2);
MUX_MACRO_PROPERTY(__P_DEF_, COND, 1, 2);
MUX_MACRO_PROPERTY(__P_DEF_, 1, 1, 2);
MUX_WITH_COMMA(concat(__P_DEF_, 1), 1, 2);
MUX_WITH_COMMA(__P_DEF_1, 1, 2);
CHOOSE2nd(__P_DEF_1 1, 2);
CHOOSE2nd(X, 1, 2);
1

然后再看一下COND没有定义的情况

int var = MUXDEF(COND, 1, 2); // 宏展开如下
int var = 1;
MUXDEF(COND, 1, 2);
MUX_MACRO_PROPERTY(__P_DEF_, COND, 1, 2);
// 与上一种情况对比,COND没被展开,因为没有定义COND这个宏
MUX_WITH_COMMA(concat(__P_DEF_, COND), 1, 2);
MUX_WITH_COMMA(__P_DEF_COND, 1, 2);
CHOOSE2nd(__P_DEF_COND 1, 2);
2

通过逐步展开宏事情已经一目了然,即通过是否加上逗号,使得CHOOSE2nd中所选择的第二个参数,变成我们第二个或者第三个实参。注意到需要与__P_DEF_1这个宏的名字匹配,因此我们的COND只能定义成1而不能是别的,否则就会匹配失败,当然,你也可以写成__P_DEF_y,然后#define COND y就可以

标签:__,技巧,macro,MUX,C语言,COND,text,DEF,define
From: https://www.cnblogs.com/nosae/p/17471808.html

相关文章

  • C语言-字符串
    简介C语言没有单独的字符串类型,字符串被当作字符数组,即char类型的数组。比如,字符串“Hello”是当作数组{'H','e','l','l','o'}处理的。编译器会给数组分配一段连续内存,所有字符储存在相邻的内存单元之中。在字符串结尾,C语言会自动添加一个全是二进制0的字节,写作\0字符,表示字......
  • LabVIEW|小技巧:16进制字符串至ASCII字符串转换
    在使用LabVIEW进行设备间的通信的时候,对于16进制的字符串以及ASCII字符串的处理是比较常见的,在这里,提供两种字符串之间的转换一个方式,需要自取。......
  • 构建高效互联网医院系统:源码开发技巧
    目前来看,互联网医院系统源码的构建成为了医院信息化建设的一个重要方向。在构建高效互联网医院系统的过程中,源码开发技巧显得尤为重要。本篇文章,小编将为大家着重讲述一下,希望对您有一定的帮助。一、系统整体架构设计理论上来讲,整体架构的开发就是互联网医院系统的核心之一,在这个过......
  • Java编程技巧-定义集合常量、定义数组常量的最佳方式
    场景Java中定义集合常量的最佳方式在编码中,经常使用到各种集合常量,比如List(列表)常量、Set(集合)常量、Map(映射)常量等。普通方式一般这样写:publicstaticfinalList<Integer>CONST_VALUE_LIST=Arrays.asList(1,2,3);publicstaticfinalSet<Integer>CONST_VALUE......
  • Java开发技巧-数据结构-使用HashSet判断主键是否存在、使用Pair成对结果返回/Triple三
    场景Java中使用HashSet判断主键是否存在HashSet实现Set接口,由哈希表(实际上是HashMap)实现,但不保证set的迭代顺序,并允许使用null元素。HashSet的时间复杂度跟HashMap一致,如果没有哈希冲突则时间复杂度为O(1),如果存在哈希冲突则时间复杂度不超过O(n)。所以,在日常编码中,可以使用HashSe......
  • Python+pandas你可能不知道的排序技巧
    除了支持使用sort_index()方法按索引或列名进行排序,pandas的DataFrame结构还支持sort_values()方法根据值进行排序,本文重点介绍sort_values()方法,其完整语法如下:sort_values(by,axis=0,ascending=True,inplace=False,kind='quicksort',na_position='last')其中常用的参数有:1)参......
  • C语言的指针
    1.初步理解指针C语言的指针指的是一个地址,也是内存单元的编号,它存储的是内存地址.#include<stdio.h>intmain(){//初始化一个变量a的值inta=10;//定义一个指针p指向变量a的地址,int*表示这个变量存放的是int类型的地址int*p=&a;//打印这个指针所指向的变量......
  • 初识C语言
       通过对C语言的基础知识的简单介绍,对C语言有大概的了解1.什么是C语言    首先我们常说的语言是人与人交流的方式,而C语言是人与计算机交流的一种计算机语言。人----------------------人        人------------------计算机  汉语,英语,法语...  ......
  • 2.6万字的软件测试高频面试题(2023全新版),内容包括:面试技巧,HR面试、基础面试、JMeter面
    1.求职面试准备(记得收藏保存转发给你的朋友)1.1面试技巧......
  • C语言编程—强制类型转换
    强制类型转换是把变量从一种类型转换为另一种数据类型。例如,如果您想存储一个long类型的值到一个简单的整型中,您需要把long类型强制转换为int类型。您可以使用强制类型转换运算符来把值显式地从一种类型转换为另一种类型,如下所示:(type_name)expression请看下面的实例,使用强......