va_list in C:创建一个不需要参数计数的函数,如“printf”

va_list in C: Creating a function that doesn't need a argument count like 'printf'

提问人:KianFakheriAghdam 提问时间:10/7/2022 最后编辑:Peter MortensenKianFakheriAghdam 更新时间:10/29/2022 访问量:385

问:

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

  1. 要开始使用 ,您需要使用一个宏,该宏需要知道有多少个参数,但是 & ...使用不需要参数计数。我怎样才能创建一个不需要参数计数的函数,比如?va_listva_startprintfva_listprintf

  2. 假设我想创建一个函数,该函数接受 ?(所以在伪代码中,它会像va_listva_listvoid printfRipOff(const char* format, ...) {printf(format, ...);})

C 标准库

评论

2赞 KamilCuk 10/7/2022
va_start macro that needs to know how many arguments there va_start宏不需要知道有多少个参数。 “知道”参数计数 - 它计算格式字符串中不跟另一个字符串的数量。 这么说吧,还有?所以写那个函数。How can I create a function that doesn't need the argument count like printf?printf%%say I want to create a function that takes a va_list & instead of using it, passes it to another function that requires a va_list?
2赞 Willis Hershey 10/7/2022
sprintf并且是相同的,只是打印到和打印到作为第一个参数传递的参数printfprintfstdoutsprintfchar*
2赞 Steve Summit 10/7/2022
另请参阅C常见问题列表中的问题15.4。您可能也会对问题 15.5第 15 节的其余部分感兴趣。
2赞 ryyker 10/7/2022
如果你想要的东西是由一个也提供答案的人提供的,你应该考虑点击空心复选标记,表示你已经接受了答案。我还建议您向上单击您认为有用的任何其他答案。
2赞 dbush 10/7/2022
与其在问题中加上“谢谢”,不如接受答案

答:

2赞 Jonathan Leffler 10/7/2022 #1
  1. 函数喜欢 和 具有格式字符串参数,该参数告诉它们函数调用必须提供的参数的数量和类型。printf()scanf()

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

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

一个非常常见的习语是,你有一个函数和第二个函数,你把第一个实现为对第二个的简单调用: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;
}

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

评论

0赞 KianFakheriAghdam 10/7/2022
这就是我想写的,没有用。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; }
2赞 abelenky 10/7/2022
您需要调用而不是 .前缀的意思是“接受 V个可计算的参数”。vsprintfsprintfv
2赞 ryyker 10/7/2022
@KianFakheriAghdam - 该评论将是对您帖子的一个很好的补充,从而解释您尝试过的内容,并询问为什么它不起作用。请考虑编辑您的帖子以添加该内容。请务必包括您用于编译器的确切内容以及库。看起来您可能正在学习,因为不是 中的母语类型。 当然会用 代替 .cs50stringCCchar *string
0赞 KianFakheriAghdam 10/7/2022
我会为你们的答案投赞成票。我是新来的,真的不知道事情是如何运作的。而且它实际上是我自己制作的结构,我正在创建一个 C 库,它有很多不错的功能,并且该字符串是运行时字符串。stringmalloc
2赞 Willis Hershey 10/7/2022 #2

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

例如,在像这样的通话中:

printf("%d %c %s\n", num, c, "Hello");

printf能够检查第一个参数,即格式字符串,并看到它按该顺序包含 a、a 和 a,因此它可以假设有三个附加参数,其中第一个是 a,第二个是 a,第三个是 a,它可能假定它是指向以 null 结尾的字符串的指针。%d%c%ssigned intcharchar*

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

3赞 abelenky 10/7/2022 #3

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

查看函数,了解将 a 作为输入参数的函数的一个示例。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 %s\n", "Hello", "World");
    CountingPrintf("%d + %d == %d\n", 2, 3, 2+3);
    
    return 0;
}

输出:

Line # 1: Hello World
Line # 2: 2 + 3 == 5