【问题标题】:va_list in C: Creating a function that doesn't need a argument count like 'printf关于 C 中 va_list 的问题
【发布时间】:2022-10-18 22:15:10
【问题描述】:

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

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

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

【问题讨论】:

  • 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? 这么说吧,然后呢?所以写那个函数。
  • sprintfprintf 是相同的,除了 printf 打印到 stdoutsprintf 打印到作为第一个参数传递的 char*
  • 另请参阅C FAQ list 中的question 15.4Question 15.5section 15 的其余部分也可能对您感兴趣。
  • 如果你通缉由也提供答案的人提供,您应该考虑单击空心复选标记以表明您已接受该答案。我还建议单击您认为有帮助的任何其他答案。
  • 与其在问题中加上“谢谢”,不如accept an answer

标签: c standard-library


【解决方案1】:

假设我想创建一个接受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 %s
", "Hello", "World");
    CountingPrintf("%d + %d == %d
", 2, 3, 2+3);
    
    return 0;
}

输出:

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

【讨论】:

    【解决方案2】:
    1. printf()scanf() 这样的函数有格式字符串参数,告诉它们函数调用必须提供的参数的数量和类型。

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

    2. 只要被调用的函数需要va_list,您就可以这样做。有一些注意事项(请参阅 C11 §7.16 Variable arguments),但只需稍加努力即可管理。

      一个非常常见的习惯用法是您有一个函数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 宏只需要知道哪个参数是省略号之前的参数——函数定义中, ... 之前的参数名称。


      comment 中,您说:

      这是我想写的,但没有成功。

      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;
      }
      

      正如abelenkycomment 中所指出的,您需要使用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()

    【讨论】:

    • 这是我正在尝试写的,没有工作。 string format(const char* text, ...) { // temporary string used for formatting string formattedString; initializeString(&amp;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; }
    • 您需要拨打vsprintf 而不是sprintfv 前缀表示“接受可变数量的参数。”
    • @KianFakheriAghdam - 该评论将是您帖子的一个很好的补充,从而解释您尝试了什么,并询问为什么它不起作用的指示。请考虑编辑您的帖子以添加该内容。请务必包含您正在使用的编译器以及哪些库。看起来您可能正在研究 cs50,因为 string 不是 C 中的原生类型。 C 当然会使用char * 代替string
    • 我会投票给你们所有的答案。我是新来的,真的不知道事情是如何运作的。 string 实际上是我自己制作的结构,我正在创建一个 C 库,它有很多不错的功能,并且该字符串是运行时 malloc 字符串。
    【解决方案3】:

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

    例如,在这样的呼叫中:

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

    printf 能够检查第一个参数,格式化字符串,并看到它包含一个%d、一个%c和一个%s,所以它可以假设还有三个额外的参数,第一个是signed int,第二个是char,第三个是 char*,它可能假定它是一个指向以空字符结尾的字符串的指针。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-13
      • 2011-07-23
      • 2011-04-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多