把一个va_list变量放在......变量参数列表(!)中

Rém*_*yre 6 c variadic-functions

我想设计一个函数,它接受可变数量的参数,其中一个参数本身就是一个va_list; 但我的代码出了问题,我不明白...

警告 - 我的问题不是设计代码做我想做的事情(我找到了绕过问题的方法),而只是关于理解我做错了什么......

为了解释我的问题,让我们从一个简单的例子开始:即一个函数ffprintf,它的作用类似于fprintf,但将其内容写入多个字符串,其编号由第一个参数表示的字符串,ffprintf其身份由下一个参数给出(这些参数的数量可能因调用而异,因此您必须使用变量参数列表).这样的函数将使用如下:

FILE *stream0, *stream1, *stream2;
int a, b;
ffprintf (3, stream0, stream1, stream2, "%d divided by %d worths %f", a, b, (double)a / b);
Run Code Online (Sandbox Code Playgroud)

它的代码是:

void ffprintf (int z, ...)
 {va_list vlist, auxvlist;
  FILE **streams = malloc (z * sizeof(FILE *));
  va_start (vlist, z);
  for (int i = 0; i < z; ++i)
   {streams[i] = va_arg (vlist, FILE *); // Getting the next stream argument
   }
  char const *format = va_arg (vlist, char const *); // Getting the format argument
  for (int i = 0; i < z; ++i)
   {va_copy (auxvlist, vlist); // You have to work on a copy "auxvlist" of "vlist", for otherwise "vlist" would be altered by the next line
    vfprintf (streams[i], format, auxvlist);
    va_end (auxvlist);
   }
  va_end (vlist);
  free (streams);
 }
Run Code Online (Sandbox Code Playgroud)

这很好.现在,还有标准函数vfprintf,其原型是vfprintf (FILE *stream, char const* format, va_list vlist);,并且您像这样使用它来创建具有可变参数列表的另一个函数:

void fprintf_variant (FILE *stream, char const* format, ...)
 {
  va_list vlist;
  va_start (vlist, format);
  vfprintf (stream, format, vlist);
  va_end (vlist);
 }
Run Code Online (Sandbox Code Playgroud)

这也很好.现在,我的目标是将两个想法结合起来创建一个我会调用的函数vffprintf,你会像这样使用它:

FILE *stream0, *stream1, *stream2;
void fprintf_onto_streams012 (char const *format, ...)
 {va_list vlist;
  va_start (vlist, format);
  vffprintf (3, stream0, stream1, stream2, format, vlist);
  va_end (vlist);
 }
Run Code Online (Sandbox Code Playgroud)

我设计了以下代码:

void vffprintf (int z, ...)
 {va_list vlist, auxvlist, auxauxvlist;
  va_start (vlist, z);
  FILE **streams = malloc (z * sizeof(FILE *));
  for (int i = 0; i < z; ++i)
   {streams[i] = va_arg (vlist, FILE *);
   }
  char const *format = va_arg (vlist, char const *);
  va_copy (auxvlist, va_arg (vlist, va_list)); // Here I get the next argument of "vlist", knowing that this argument is of "va_list" type
  for (int i = 0; i < z; ++i)
   {va_copy (auxauxvlist, auxvlist);
    vfprintf (streams[i], format, auxvlist);
    va_end (auxauxvlist);
   }
  va_end (auxvlist);
  va_end (vlist);
  free (streams);
 }
Run Code Online (Sandbox Code Playgroud)

这段代码编译顺利,但它无法正常工作......例如,如果我编写以下完整代码:

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>

void vffprintf (int z, ...)
 {va_list vlist, auxvlist, auxauxvlist;
  FILE **streams = malloc (z * sizeof(FILE *));
  va_start (vlist, z);
  for (int i = 0; i < z; ++i)
   {streams[i] = va_arg (vlist, FILE *);
   }
  char const *format = va_arg (vlist, char const *);
  va_copy (auxvlist, va_arg (vlist, va_list));
  for (int i = 0; i < z; ++i)
   {va_copy (auxauxvlist, auxvlist);
    vfprintf (streams[i], format, auxauxvlist);
    va_end (auxauxvlist);
   }
  va_end (auxvlist);
  va_end (vlist);
  free (streams);
 }

void printf_variant (char const *format, ...)
 {va_list vlist;
  va_start (vlist, format);
  vffprintf (1, stdout, format, vlist);
  va_end (vlist);
 }

int main (void)
 {printf_variant ("Ramanujan's number is %d.\n", 1729);
  return 0;
 }
Run Code Online (Sandbox Code Playgroud)

我得到了段错!...为什么?!

P.-S:很抱歉这个很长的问题; 但我希望它非常清楚,因为它技术性很强......

P.-S.2:我故意使用两个标记"VA-名单"和"variableargumentlists"对于这个问题,因为这我感兴趣的是va_list,被视为一个类型,一个(其他)变量参数列表,里面视为一个列表. ..所以这里真的是两个不同的概念.

cre*_*mno 2

va_argC11(N1570)最终草案中的描述包含(类型是第二个参数):

\n\n
\n

如果类型与实际下一个参数的类型不兼容(根据默认参数升级进行升级),则行为未定义

\n
\n\n

va_list允许是数组类型(标准要求它是所谓的 \xe2\x80\x9c 完整对象类型\xe2\x80\x9d),并且您的实现似乎利用了这种可能性。您可能知道,在 C 中,数组不能作为参数传递,因为它们会退化为指针,并且此类指针的类型与原始数组类型不兼容。

\n\n

例如:int *与 不兼容int [1]。因此,如果您确实需要传递数组或va_list可移植的 a,则可以struct使用va_list成员定义 a 并传递它(请参阅为什么我们不能按值将数组传递给函数?)。

\n