关注

C语言宏中“#”和“##”的用法


前言

使用#把宏参数变为一个字符串,用##把两个宏参数贴合在一起。只有充分了解到其中的用法,才能更好的看懂别人的代码以及自己手到擒来的使用。

#和##都属于预处理标记,即编译器会在预处理阶段进行相关的替换或者处理

一、(#) 字符串化操作符

"#"的功能是将其后面的宏参数进行字符串化操作,等同于把后面的宏变量加上双引号。
宏定义函数的参数与预处理标记 ‘#’ 之间出现的每一个空格都会被删除,并删除第一个预处理标记之前和最后一个预处理标记之后的空白字符,但是宏定义函数参数中的空格会保留。
空参数转化为空,即宏定义函数入参为空,那么展开的时候也为空。

#include <stdio.h>

#define MAKE_STR(s) (#s)

int main(void)
{
    printf("hello\n");
    printf(MAKE_STR(hello\n));

    return 0;
}

输出结果:
hello
hello

它具体是怎么实现的呢?其实很简单就是在其宏变量被替换,在其左右两侧加上双引号。为了探究这个过程,我们在用gcc编译的时候加上-E选项来看下编译器的预处理过程:

int main(void)
{
    printf("hello\n");
    printf(("hello\n"));

    return 0;
}

C语言会自动将相邻的两个字符串合并:

#include <stdio.h>

#define MAKE_STR(s) (#s"\n")

int main(void)
{
    printf("hello\n");
    printf(MAKE_STR(hello));

    return 0;
}

参数详解

宏定义函数的参数与预处理标记 ‘#’ 之间出现的每一个空格都会被删除,并删除第一个预处理标记之前和最后一个预处理标记之后的空白字符,但是宏定义函数参数中的空格会保留。

#include <stdio.h>

#define MAKE_STR(s) ("321" # s "654\n")

int main(void)
{
    printf("hello world\n");
    printf(MAKE_STR(hello world));

    return 0;
}

输出结果:
hello world
321hello world654

#和宏定义参数s之间的空格被删除掉了。字符串"321"和字符串"654"和# s之间的空格也被删除掉了。
但是宏参数hello world之间的空格并没有被删除。
注意:'#'只能用于传入参数的宏定义中,且必须置于宏定义体中的参数名前。

对于#的参数,即便是另一个宏,也不展开,仍然作为字符串字面信息输出。

#include<stdio.h>

#define dprint(expr) printf(#expr"=%d\n",expr); 

int main() 
{  
    int a=20,b=10;
    dprint(a/b);
    return 0; 
}

/*
   输出:a/b=2
 */

二、(##)符号连接操作符

作用:"##"被称为预处理拼接标记,宏定义展开的时候,用来将其左右两边两个token连接为一个token。注意这里连接的对象是token就行,不一定是宏的变量。

#include <stdio.h>

#define CONS(a,b)  int(a##e##b)
int main()
{
   printf("%d\n", CONS(2,3));  // 2e3 输出:2000
   return 0;
}

输出结果:
2000

常见用法1:在结构体定义中的妙用

比较多开源代码中惯用的做法,相比常规的结构体定义法,确实省去很多重复的代码。

#include <stdio.h>

#define DF_STRUCT(name) typedef struct tag##name name;\
                         struct tag##name
DF_STRUCT(DevManage)
{
    int index;   //索引 
    int Access;  //权限
                 //...  
};

int main(int argc, char *argv[]) {

    DevManage stDevManage;

    stDevManage.index  = 1;
    stDevManage.Access = 666;

    printf("Dev Index :%d\n",stDevManage.index );
    printf("Dev Access:%d\n",stDevManage.Access );

    return 1;
}

常见用法2:统一宏替换

拼接标识符意味着符号的粒度更高,而这碎片化的符号进行有效的管理,就可以使得符号更加具有通用性和灵活性。
其实这种思想跟我们代码模块话是同样的道理。

#include <stdio.h>
#include <stdlib.h>

//假如这是stm32库中的宏 
#define GPIO_Pin_0                 ((int)0x0001)  /*!< Pin 0 selected */
#define GPIO_Pin_1                 ((int)0x0002)  /*!< Pin 1 selected */
#define GPIO_Pin_2                 ((int)0x0004)  /*!< Pin 2 selected */
#define GPIO_Pin_3                 ((int)0x0008)  /*!< Pin 3 selected */

#define USART1              ((int *) 0x1000)
#define USART2              ((int *) 0x2000)

//拼接变量 
#define UARTX 1

//最终的组合标识符 
#define UART1_CORE  USART1
#define UART1_RX    GPIO_Pin_0
#define UART1_TX    GPIO_Pin_1

#define UART2_CORE  USART2
#define UART2_RX    GPIO_Pin_2
#define UART2_TX    GPIO_Pin_3

//拼接过程 
#define _UARTX_CORE(uartx)   UART##uartx##_CORE 
#define UARTX_CORE(uartx)    _UARTX_CORE(uartx)

#define _UARTX_RX(uartx)   UART##uartx##_RX
#define UARTX_RX(uartx)    _UARTX_RX(uartx) 

#define _UARTX_TX(uartx)   UART##uartx##_TX
#define UARTX_TX(uartx)    _UARTX_TX(uartx)

int main(int argc, char *argv[]) {

    //组合标识符的使用 
    printf("0x%x\n",UARTX_CORE(UARTX));
    printf("0x%x\n",UARTX_RX(UARTX));
    printf("0x%x\n",UARTX_TX(UARTX));

    return 1;
}

转载自CSDN-专业IT技术社区

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/qq_41908302/article/details/141278218

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--