va_list C 语言:创建一个不需要像 'printf' 这样的参数计数的函数



使用<stdarg.h>标头,可以创建一个具有可变参数数量的函数,但是:

  1. 要开始使用va_list,您需要使用一个va_start宏,该宏需要知道那里有多少参数,但是printf和...使用va_list不需要参数计数。如何创建一个不需要像printf那样的参数计数的函数?

  2. 假设我想创建一个接受va_list的函数,而不是使用它,而是将其传递给另一个需要va_list的函数?(所以在伪代码中,它会像void printfRipOff(const char* format, ...) {printf(format, ...);})

假设我想创建一个接受va_list的函数,而不是使用它,而是将其传递给另一个需要va_list的函数?

查看函数vprintf( const char * format, va_list arg );,了解将va_list作为输入参数的函数示例。

它基本上是这样使用的:

#include <stdio.h>
#include <stdarg.h>
void CountingPrintf(const char* format, ...)
{
static int counter = 1;
printf("Line # %d: ", counter++); // Print a Counter up front
va_list args;
va_start(args, format); // Get the args
vprintf(format, args);  // Pass the "args" through to another printf function.
va_end(args);
}
int main(void) {
CountingPrintf("%s %sn", "Hello", "World");
CountingPrintf("%d + %d == %dn", 2, 3, 2+3);

return 0;
}

输出:

Line # 1: Hello World
Line # 2: 2 + 3 == 5
  1. printf()scanf()这样的函数具有格式字符串参数,该参数告诉它们函数调用必须提供的参数的数量和类型。

    如果你正在编写自己的函数,你必须有某种方法来知道提供了多少参数及其类型。 可能是它们都是同一类型。 它们可能都是指针,您可以使用空指针来指示参数的结尾。 否则,您可能必须包括计数。

  2. 只要
  3. 被调用的函数需要va_list,您就可以这样做。 有一些警告(参见 C11 §7.16 变量参数),但只需稍加努力即可管理。

一个非常常见的习语是,你有一个函数int sometask(const char *fmt, ...)和第二个函数int vsometask(const char *fmt, va_list args),你实现第一个函数作为对第二个函数的简单调用:

int sometask(const char *fmt, ...)
{
va_list args;
va_start(fmt, args);
int rc = vsometask(fmt, args);
va_end(args);
return rc;
}

当然,第二个函数执行实际工作,或者调用其他函数来完成实际工作。


在问题中,你说:

va_start需要知道多少参数的宏...

不需要;va_start宏只需要知道哪个参数是省略号之前的参数 — 函数定义中, ...之前的参数名称。


在评论中,您说:

这就是我想写的,但它没有用。

string format(const char* text, ...)
{
// temporary string used for formatting
string formattedString;
initializeString(&formattedString, text);
// start the va_list
va_list args;
va_start(text, args);
// format
sprintf(formattedString.array, text, args);
// end the va_list
va_end(args);
return formattedString;
}

正如 abelenky 在评论中指出的那样,您需要使用vsprintf()

string format(const char* text, ...)
{
string formattedString;
initializeString(&formattedString, text);
va_list args;
va_start(text, args);
vsprintf(formattedString.array, text, args);
va_end(args);
return formattedString;
}

这假定formattedString在数组中有足够的空间来存储格式化结果。 它是如何组织的并不明显,但大概你知道它是如何工作的,它是如何安全的。 如果可以确定formattedString中的可用空间,请考虑使用vsnprintf()

printfscanf系列函数不需要显式传递va_list的长度,因为它们能够通过格式字符串推断参数的数量。

例如,在这样的调用中:

printf("%d %c %sn", num, c, "Hello");

printf能够检查第一个参数,即格式字符串,并看到它包含%d%c%s,因此它可以假定有三个附加参数,第一个是signed int,第二个是char,第三个是它可能假定是指向以 null 结尾的字符串的指针的char*

要编写一个不需要明确告诉正在传递多少参数的类似函数,您必须找到一种方法来巧妙地为它提供一些信息,使其能够推断va_list中参数的数量和类型。

最新更新