C语言 使用va_args而不传递num(如printf)



下面是我能想到的将变长参数传递给函数的最基本的例子:

int printme(int num, ...)
{
va_list ap;
va_start(ap, num);
for (int i = 0; i < num; ++i) {
char *arg = va_arg(ap, char *);
printf("%d. %sn", i + 1, arg);
}
va_end(ap);
return 1;
}
int main(void)
{
printme(2, "X", "YY");
}
但是,请注意,我将长度作为第一个参数(或作为任何参数)传入。是否有可能将这些va_宏与类似printf的函数一起使用?例如,我可以做这样的事情(不传递参数的数量)吗?
print2("Hello something %s %s", "Arg 1", "Arg 2");
print2("Hello something %s %s %s", "Arg 1", "Arg 2", "Arg 3");

如果是,接收的函数是什么样子的?如果不可能,那么printf是如何实现的呢?

如果您只想传递字符串,可以很容易地通过编写一个函数来解析第一个参数,如我在这里写的countargs(char*)。它返回参数的个数:

#include <stdarg.h>
#include <stdio.h>
int printme(char* fmt, ...)
{
va_list ap;
int num = countargs(fmt);
va_start(ap, num);
for (int i=0; i < num; ++i) {
char* arg = va_arg(ap, char*);
printf("%d. %sn", i+1, arg);
}
va_end(ap);
return 1;
}
int countargs(char* fmt)
{
int i, num = 0;
if(strlen(fmt) < 2) 
{ 
return 0;
}
for(i = 0; fmt[i+1] != ''; i++)
{
if (fmt[i] == '%' && fmt[i+1] == 's')
{
num++;
}
}
return num;
}
int main(void)
{
printme("%s%s", "Stack", "Overflow");
}

下面是一个从解析字符串中检测参数数量的基本示例。它没有考虑任何特殊情况,也没有实际格式化/解析字符串,而是展示了一个基本的工作程序,以显示如何从字符串解析计数:

int printmy(char * format, ...)
{
// num args
int num = 0;
for(char idx=0, c; c=format[idx]; idx++) {
// ignore escape char + double-percent
if (c=='\' || (c=='%' && format[idx+1]=='%'))
idx++;
else if (c=='%')
num++;
}
// print variable args
va_list ap;
va_start(ap, format);   // need to give it the last argument before the "..."
for (int i=0; i < num; ++i) {
char* arg = va_arg(ap, char*);
printf("%d. %sn", i+1, arg);
}
va_end(ap);

// return num args received
return num;
}
int main(void)
{
int num_args = printmy("Hello something % \% %% %s %s %s", "Arg 1", "Arg 2", "Arg 3");    
printf("Num Args Parsed: %dn", num_args);
}

得到:

1. Arg 1
2. Arg 2
3. Arg 3
4.
Num Args Parsed: 4

您可以使用macroprocessor来计算参数。

#include <stdarg.h>
#include <stdio.h>
int printme(int num, ...)
{
va_list ap;
va_start(ap, num);
for (int i=0; i < num; ++i) {
char* arg = va_arg(ap, char*);
printf("%d. %sn", i+1, arg);
}
va_end(ap);
return 1;
}
#define TENTH(p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,...) p10
#define NARGS(...) TENTH(__VA_ARGS__,9,8,7,6,5,4,3,2,1,0)
#define printme(...) printme(NARGS(__VA_ARGS__), __VA_ARGS__)
int main(void)
{
printme("X", "YY");
}

工作原理:

TENTH只是返回第10个参数例子:

TENTH(a,b,c,d,e,f,g,h,i,j,k)

展开为j.

可用于获取多个实参:

| tenth argument
v
TENTH("X", "XY", 9,8,7,6,5,4,3,2,1,0)

展开为2,因为2TENTH的第十个实参。

相似的,

| tenth argument
v
TENTH("A", "B", "C", "D", 9,8,7,6,5,4,3,2,1,0)

展开为4

NARGS将参数放在序列9,8,...之前,移动序列以获得参数的数量。

我使用printme来避免污染命名空间。当宏被展开时,getdisabled以避免无限递归。它不会进一步扩展为对原始printme()的调用。第一个参数num是用NARGS宏计算的。所有剩余的参数都在num参数之后传递。

仍然有一些缺点:

  • 最多工作10个参数,但这个限制可以很容易地取消
  • 需要至少一个参数(这是可能的解决方案,但它是复杂的)

相关内容

  • 没有找到相关文章