uncategorized

c语言可变参数宏定义

这篇记录是大学的时候写的, 今天看到了, 现在我有了自己的博客平台, 把这篇文章迁移过
来, 关于c语言中可变参数宏定义, 先来看print函数的声明:

1
int printf(const char* str, ...);

你可以看到printf的第二个参数是三个., 这就是c语言中的可变参数使用。那么我们如
何定义我们自己的可变函数了, 首先你要包 含#include<stdarg.h>头文件来使用里面四
个内置的宏, va_list, va_start, va_arg, va_end, 下面定义定义一个我们自己
的可变参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdarg.h>
#include <stdio.h>

int add(int len, ...)
{

int i = 0;
int sum = 0;
va_list va;
va_start(va, len);
for(i=0; i<len; i++)
{
sum += va_arg(va, int);
}

return sum;
}

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

printf("add(5) = %d\n", add(5, 1, 2, 3, 4, 5));
printf("add(3) = %d\n", add(3, 1, 2, 3));

return 0;
}

查看运行结果:

上面的函数add函数是一个可变参数函数,第一个参数是固定,这个定义一个可变参数的
的必须条件,用于得到虚参在栈里面的首地址,函数的功能实现指定个整数的和,第一个参
数指定数的个数。下面我们分析add函数,首先va_list声明一个变量

`va_list`可能是这样一个`#define va_list void*`宏定义

`va_start`可能是这样一个`#define va_start(va, len) (va = (void*)((char*)&len + sizeof(len)))`, `va_start`宏是通过第一个固定参数`len`来给找到第一个可变参数(c语言中函数入栈是从右向左的, 既`va`变量里面存放的就是我们第一个可变参数的首地址
`va_arg`可能是这样一个`#define va_arg(va, int) *((int*)(va = (void*)((int*)va + 1)) - 1)`宏定义,用于得到其中的某个变参的值
`va_end`可能是这样一个`#define va_end(va) va = NULL`

经过我们上面的分析我们可以去掉我们第一次包含的#include <stdarg.h>头文件,所用我们自定义的宏,例如下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>
#define va_list void*
#define va_start(va, len) (va = (void*)((char*)&len + sizeof(len)))
#define va_arg(va, int) *((int*)(va = (void*)((int*)va + 1)) - 1)
#define va_end(va) va = NULL;

int add(int len, ...)
{

int i = 0;
int sum = 0;
va_list va;
va_start(va, len);
for(i=0; i<len; i++)
{
sum += va_arg(va, int);
}

return sum;
}

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

printf("add(5) = %d\n", add(5, 1, 2, 3, 4, 5));
printf("add(3) = %d\n", add(3, 1, 2, 3));

return 0;
}

这次我们没有包含#include <stdarg.h>头文件,看看运行结果和上面的运行结果一致.

下面我们在来看看c语言中可变参数宏定义,先来看看一个定义

1
2
3
4
// __FILE__和__LINE__是c语言中内置的宏,用于显示当前文件名和行号,这里我们使用了arg这个名字,如果在定义的时候没有写名 字,例如像这样
#define LOG(arg...) printf("%s:%d:%s", __FILE__, __LINE__, arg)
// 我们可以使用__VA_ARGS__这个宏来代替
#define LOG(...) printf("%s:%d:%s", __FILE__, __LINE__, __VA_ARGS__)

这里还有一个#号的用法,放在__VA_ARGS__的前面,例如像这样

1
2
//当我们以LOG(); 调用时,会自动去掉__LINE__后面的逗号,下面是一个测试例子
#define LOG(...) printf("%s:%d:%s", __FILE__, ___LINE__, #__VA_ARGS__)

下面看一个使用例子:

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>

#define LOG(arg...) printf("%s:%d:%s\n", __FILE__, __LINE__, arg)

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

LOG("hello world!");

return 0;
}

上面的运行结果是:

在来看一个例子:

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>

#define LOG(...) printf("%s:%d:%s\n", __FILE__, __LINE__, #__VA_ARGS__)

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

LOG();

return 0;
}

上面的运行结果是:

上面这些是我个人对可变参数的理解,错误请大家指正。