宏及可变参数

【宏定义】

Macro属于编译期,而非运行期

'#'的功能是阻止后面宏的展开,直接将后面的宏参数进行字符串替换(字符串化,stringizing) 如:

#define WARN_IF(EXP) do {						\
	if (EXP)									\
		fprintf(stderr, "warning: "#EXP"\n");	\
} while(0)

执行过程中,

WARN_IF(divider == 0)

会被替换成

do {
	if (divider == 0)
		fprintf(stderr, "warning: ""divider == 0""\n");
} while(0)

'##'的作用是字符串连接

具体的区别如下代码所示:

#include <stdio.h>

#define SHOW_VALUE(instance, member) do {								\
	printf("%s\'s %s is %s\n", #instance, #member, instance.member);	\
} while(0)

#define SHOW_VALUE_ANOTHER(which) do {			\
	printf("%s\n", which##_name);				\
} while(0)

int main(int argc, char *argv[])
{
	struct person {
		char first_name[20];
		char last_name[20];
	};

	struct person a = {"Kobe", "Brant"};
	char *first_name = "Tom";
	char *last_name = "Jerry";

	SHOW_VALUE(a, first_name);
	SHOW_VALUE(a, last_name);

	SHOW_VALUE_ANOTHER(first);
	SHOW_VALUE_ANOTHER(last);

	return 0;
}

输出为:

a's first_name is Kobe
a's last_name is Brant
Tom
Jerry

【可变参数宏】

宏可以接收可变数据的参数,如:

#define LOG(format, ...) printf(format, __VA_ARGS__)

其中'...'表示参数可变,VA_ARGS 在预处理中由实际的参数集所替换

假设稍后调用该宏:

LOG("%s\n", "Howdy");
LOG("weight = %d, shipping = $%.2f\n", 1, 2.0);

对于第二次调用,VA_ARGS 展开为2个参数:wt和sp。 展开后的代码为:

printf("Howdy");
printf("weight = %d, shipping = $%.2f\n", 1, 2.0");

在GCC中,同时支持如下的形式:

#define LOG(format, args...) printf(format, args)

用法与上面的一致。

但上面这种写法会有个问题:可变参数必须存在。若没有可变参数,编译就会报错。 这里可以使用"##"连接符,用以对toke进行连接,因此做如下修改:

#define LOG(format, args...) printf(format, ##args);

在调试环境下,通常会定义一个打印调试信息的宏,如:

#define __ENABLE_DBG__

#ifdef __ENABLE_DBG__
#define DBG_PRINTF(dbg_level, fmt, args...) do { \
	if (dbg_level <= get_debug_level()) \
		printf("\t [module] %s(%d). " fmt "\n", __FUNCTION__, __LINE__, ##args); \
} while (0)
#else
#define DBG_PRINTF(lvl, fmt, args...)
#endif

【可变参数: stdarg.h】 前面提到了变参宏,可以接收可变数量的参数。stdarg.h头文件为函数提供了一个类似的功能。 其用法较为复杂,必须按照如下步骤进行:

1. 提供函数原型;
2. 在函数定义中创建一个va_list类型的变量;
3. 用宏把该变量初始化为一个参数列表;
4. 用宏访问参数列表;
5. 用宏完成清理工作。
  1. 函数原型的形参列表中,至少有一个形参和一个省略号。其中,最右边的形参(省略号左边第一个) 表示省略号部分的参数数量(使用parmN来代表该形参)

  2. 接下来定义一个va_list类型的变量,

    va_list ap;

用于储存形参列表中省略号部分的数据对象(这里只是声明,还未存放)

  1. 将参数列表拷贝到va_list类型的变量中

    va_start(ap, parmN);

该宏有两个参数,分别为va_list类型的变量和parmN形参

  1. 下一步是访问参数列表的内容,使用宏va_arg()。该宏接收两个参数:一个va_list类型变量和一个类型名。 类型名指定了返回值的类型。第一次调用va_arg()时,它返回参数列表的第一项;第二次调用va_arg()时,它返回参数列表的第二项,以此类推。

注意:传入的参数类型必须与宏参数的类型相匹配。

  1. 最后,要使用va_end()宏完成清理工作。如,释放动态分配用于存储参数的内存。 该宏接收一个va_list类型的变量。 在调用va_end()后,只有重新调用va_start初始化ap后,才能使用变量ap。

转载于:https://my.oschina.net/u/2561528/blog/1580048

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
c语言中的宏函数是一种宏定义的形式,可以在编译预处理阶段通过替换来实现类似函数调用的功能。它不同于普通的函数调用,其主要特点是在宏定义时使用一些特殊的宏形参,这些宏形参可以包含可变参数可变参数是指宏函数在调用时可以传入不定数量的参数。在宏定义中,我们可以使用"..."来表示可变参数,且该参数必须放置在宏参数列表的最后。 通过宏定义中的可变参数,我们可以通过宏函数实现一些功能较为复杂的操作。比如,我们可以编写一个求和的宏函数,可以传入任意数量的参数,并对它们进行求和计算。例如: ``` #define SUM(...) sum(__VA_ARGS__) int sum(int count, ...) { int result = 0; va_list arg; va_start(arg, count); for(int i = 0; i < count; i++) { result += va_arg(arg, int); } va_end(arg); return result; } ``` 在使用可变参数的宏函数时,我们需要使用宏定义中的特殊宏形参,包括`va_list`、`va_start`、`va_arg`和`va_end`。`va_list`是一个用于存储可变参数的类型,`va_start`用于初始化可变参数的访问,`va_arg`用于按顺序获取可变参数的值,`va_end`用于结束对可变参数的访问。 总之,宏函数和可变参数是C语言中非常有用的功能,可以通过它们来实现一些复杂的操作和功能。但需要注意的是,在使用可变参数时要小心,保证参数的正确性和合法性,以免出现错误。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值