C/C++ 宏定义高级用法

c/c++ 宏定义高级用法
1.ANSI标准五个预定义的宏名

    __LINE__ 表示该行代码的所在行号
    __FILE__ 表示源文件的文件名
    __DATE__ 表示源文件被编译的日期,格式(月/日/年)
    __TIME__ 表示源文件被编译成目标代码的时间,格式(时:分:秒)
    __STDC__ 表示编译器是否标准,标准时表示常量1,非标准则表示其它数字

测试用例

#include <stdio.h>
#include <stdlib.h>
 
 
#define INFO(msg) info_debug(__FILE__, __LINE__, __DATE__, __TIME__, msg)
 
void info_debug(const char* filename, int line, const char* date, const char* time, const char* msg)
{
	printf_s("info_debug %s:%d (%s-%s):%s", filename, line, date, time, msg);
}
 
int main()
{
	INFO("Hello world!\n");
	system("pause");
	return 0;
}

定义函数或这类:

#define Function(name) void Func##name(void)

//使用
Function(mytest)
{
    ....
}

//编译器宏替换后
void Funcmytest(void)
{
    ...
}

2.不定参数的宏定义用法

__VA_ARGS__ 表示后面剩余的所有参数

具体用法

#define LOG(fmt, ...) log(__FILE__, __LINE__, fmt, __VA_ARGS__)

"..."表示后面省略的参数,__VA_ARGS__正好对应"..."的作用

测试用例
 

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
 
 
#define INFO(msg) info_debug(__FILE__, __LINE__,  msg)
#define LOG(fmt, ...) log(__FILE__, __LINE__, fmt, __VA_ARGS__)
 
void info_debug(const char* filename, int line, const char* msg)
{
	printf_s("info_debug %s:%d :%s", filename, line, msg);
}
 
void log(const char* filename, int line, const char* fmt, ...)
{
	int size;
	char * msg = NULL;
	va_list args;
	va_start(args, fmt);
	size = vsnprintf(NULL, 0, fmt, args) + 1;
	msg = (char *)malloc(size);
	vsnprintf(msg, size, fmt, args);
	info_debug(filename, line, msg);
	free(msg);
	va_end(args);
}
 
int main()
{
	INFO("Hello world!\n");
	LOG("%s %d\n", "debug", 14);
	system("pause");
	return 0;
}

3.# 与 ## 在宏定义的含义

#的功能是将其后面的宏参数进行字符串化操作,简单说就是在对它所引用的宏变量通过替换后在其左右各加上一个双引号

##被称为连接符,用来将两个Token连接为一个Token,##符是把传递过来的参数当成字符串进行替代

测试用例

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
 
 
#define INFO(msg) info_debug(__FILE__, __LINE__,  msg)
#define LOG(fmt, ...) log(__FILE__, __LINE__, fmt, __VA_ARGS__)
 
#define VERSION 1.0.0
#define __STR__(x) (#x)  
#define TO_STR(x) __STR__(x)
 
#define LINK(x, y) x##y
 
 
void info_debug(const char* filename, int line, const char* msg)
{
	printf_s("info_debug %s:%d :%s", filename, line, msg);
}
 
void log(const char* filename, int line, const char* fmt, ...)
{
	int size;
	char * msg = NULL;
	va_list args;
	va_start(args, fmt);
	size = vsnprintf(NULL, 0, fmt, args) + 1;
	msg = (char *)malloc(size);
	vsnprintf(msg, size, fmt, args);
	info_debug(filename, line, msg);
	free(msg);
	va_end(args);
}
 
int main()
{
	INFO("Hello world!\n");
	LOG("%s %d\n", "debug", 14);
	printf_s("#把一个宏定义变成字符串:version = %s\n", TO_STR(VERSION));
	int n = LINK(12, 34);
	printf_s("##把两个参数拼接一起:%d\n", n);
	system("pause");
	return 0;
}

4、多行宏定义并在宏定义中进行函数调用

示例:

#define  DSPI0_RX  0
#define  DSPI1_RX  1
#define  DSPI2_RX  2
#define  DSPI3_RX  3
#define  DSPI4_RX  4

void  Spi_LLD_IsrRxDma_DSPI();

#define SPI_LLD_ISRRXDMA_DSPI_N_SRV(MCU_DMA_CHANNEL_n_SOURCE) \
if(MCU_DMA_CHANNEL_n_SOURCE == DSPI0_RX)\
{\
    Spi_LLD_IsrRxDma_DSPI();\
}\
else if(MCU_DMA_CHANNEL_n_SOURCE == DSPI1_RX)\
{\
    Spi_LLD_IsrRxDma_DSPI(); \
}\
else if(MCU_DMA_CHANNEL_n_SOURCE == DSPI2_RX)\
{\
    Spi_LLD_IsrRxDma_DSPI();\
}\
else if(MCU_DMA_CHANNEL_n_SOURCE == DSPI3_RX)\
{\
    Spi_LLD_IsrRxDma_DSPI();\
}\
else if(MCU_DMA_CHANNEL_n_SOURCE == DSPI4_RX)\
{\
    Spi_LLD_IsrRxDma_DSPI();\
}

根据MCU_DMA_CHANNEL_n_SOURCE值的不同调用不同的函数。

5、空指针定义

#define NULL_PTR ((void *)0)

6、变量或返回值宏定义指明类型和对应模块的使用技巧

举例说明,常变量

typedef usigned char uint8;

typedef uint8 Adc_ChannelType; 

#define CONST(consttype, memclass) const consttype

STATIC CONST(Adc_ChannelType, ADC_CONST) AdcGroup_10_Assignment_PB_2_1[4] =
{//AdcGroup_10
    0U,
    1U,
    2U,
    3U,
};

//Adc_ChannelType表明数组元素为ADC通道类型,属于ADC模块的常变量。

再例如:普通变量声明

 #define VAR(vartype, memclass) vartype

  VAR(Adc_ChannelType, ADC_VAR) chId;

7、宏定义实现断言

举例说明:

#define  ASSERT(condition, message) {\

  if(!(condition)){\

  logError("Assertion failed:",#condition,message);\

  exit(EXIT_FAILURE);\

}\

}

8、在嵌入式开发中,利用#define 和 typedef联合使用,将地址常量变为结构体变量,并使用“.”运算符访问结构体变量成员。

使用举例

typedef volatile union
{
 struct
 {
  unsigned int ECCENLDRAM     : 1;
  unsigned int ECCENDTAG      : 1;
  unsigned int ECCENSPRAM     : 1;
  unsigned int ECCENPTAG      : 1;
  unsigned int ECCENPMU       : 1;
  unsigned int ECCENPRAM      : 1;
  unsigned int ECCENCMEM      : 1;
  unsigned int ECCENCAN       : 1;
  unsigned int ECCENERAY      : 1;
  unsigned int                : 23;
 } B;
 int I;
 unsigned int U;

} SCU_ECCCON_type;


SCU_ECCCON_type SCU_ECCCON_Test;

#define SCU_ECCCON    (*( SCU_ECCCON_type *) 0xffff10000)

#define SCU_ECCCON_Test_ECCENERAY    (SCU_ECCCON.B.ECCENERAY)

//那么直接对SCU_ECCCON_Test_ECCENERAY宏进行操作就是对结构体变量SCU_ECCCON的B成员的ECCENERAY位进行操作。

//注意该案例中的结构体为变量定义的方法。

 

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
宏定义是C/C++语言的一个特性,它可以将一些常用的代码片段或者常量定义为一个宏,从而方便代码编写和维护。除了常规的宏定义,还有一些高级用法,以下是一些常见的高级用法: 1. 带参数的宏定义 可以使用宏定义来定义带有参数的函数。例如: ``` #define MAX(a,b) ((a) > (b) ? (a) : (b)) ``` 这个宏定义定义了一个函数,用于获取两个数的最大值。在代码使用该宏的方式如下: ``` int a = 10; int b = 20; int max = MAX(a, b); ``` 在编译时,编译器会将 MAX(a, b) 替换为 ((a) > (b) ? (a) : (b)),相当于直接调用了 MAX 函数。 2. 条件编译 可以使用宏定义来进行条件编译。例如: ``` #define DEBUG #ifdef DEBUG printf("debugging\n"); #endif ``` 在编译时,如果定义了 DEBUG 宏,编译器会将 #ifdef 和 #endif 之间的代码包含进去;否则,这段代码就会被忽略掉。 3. 字符串连接 可以使用宏定义来进行字符串连接。例如: ``` #define CONCAT(a,b) a##b ``` 在代码使用该宏的方式如下: ``` int ab = CONCAT(a, b); ``` 在编译时,编译器会将 CONCAT(a, b) 替换为 ab,相当于直接将 a 和 b 连接起来。 4. 复杂的宏定义 可以使用宏定义来进行复杂的操作,例如: ``` #define SQUARE(x) ((x) * (x)) #define MAX(a,b) ({ __typeof__(a) _a = (a); __typeof__(b) _b = (b); _a > _b ? _a : _b; }) #define PRINTF(fmt, ...) printf(fmt, ##__VA_ARGS__) ``` 这些宏定义分别用于计算平方、获取两个数的最大值、格式化输出字符串。其,MAX 宏使用了 GCC 的扩展语法,可以返回两个参数的最大值;PRINTF 宏使用了可变参数语法,可以传入不定数量的参数。 总之,宏定义是一个非常强大的工具,可以帮助我们编写出更加简洁、高效的代码。但是,在使用宏定义时需要注意避免一些潜在的问题,如宏定义可能存在的副作用、宏定义可能存在的类型错误等,需要谨慎使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值