在里面放一个 va_list 变量...一个变量参数列表(!)
Put a va_list variable inside... a variable argument list (!)
我想设计一个函数,它接受可变数量的参数,其中一个参数本身就是一个 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);
其代码为:
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);
}
效果很好。现在,还有标准函数 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);
}
这也很好用。现在,我的目标是结合这两种想法来创建一个我称之为 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);
}
我设计了如下代码:
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);
}
这段代码可以顺利编译,但不能正常工作...例如,如果我编写以下完整代码:
#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;
}
我遇到段错误!...为什么?!
P.-S.:很抱歉这个很长的问题;但我希望它非常清楚,因为它是相当技术性的...
P.-S.2:这个问题我特意用了"va-list"和"variableargumentlists"两个标签,因为我感兴趣的是va_list
,被看作一个类型,在(其他)变量参数列表中,被视为一个列表...所以这实际上是两个不同的概念。
void vffprintf (int z, ...)
{
//...
va_copy (auxvlist, va_arg (vlist, va_list));//this line has the problem
//...
}
像这样一个快速而巧妙的方法,它会奏效。
void vffprintf (int z, ...)
{
//...
va_copy (auxvlist, va_arg (vlist, void*));
//...
}
这里有一些关于var_arg
和va_list
的参考资料,应该已经提供了详细透彻的解释。
1) Pass va_list or pointer to va_list?
2) Is GCC mishandling a pointer to a va_list passed to a function?
3) What is the format of the x86_64 va_list structure?
希望对您有所帮助。
C11终稿(N1570)中对va_arg
的描述包含(类型为第二个参数):
if type is not compatible with the type of the actual next argument (as promoted according to the default argument promotions), the behavior is undefined
va_list
可以是数组类型(标准要求它是所谓的“完整对象类型”),您的实现似乎利用了这种可能性。您可能知道,在 C 中,数组不能作为参数传递,因为它们会退化为指针,并且此类指针的类型与原始数组类型不兼容。
例如:int *
与 int [1]
不兼容。因此,如果您确实需要可移植地传递数组或 va_list
,则定义一个带有 va_list
成员的 struct
并传递它(参见 Why can't we pass arrays to function by value?)。
如果要使用 va_arg():
检索它,您可能需要将类型 va_list 包装到结构中
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
typedef struct
{
va_list list ;
} my_list ;
void vffprintf (int z, ...)
{my_list vlist, auxvlist, auxauxvlist;
FILE **streams = malloc (z * sizeof(FILE *));
va_start (vlist.list, z);
for (int i = 0; i < z; ++i)
{streams[i] = va_arg (vlist.list, FILE *);
}
char const *format = va_arg (vlist.list, char const *);
my_list parent = va_arg (vlist.list, my_list) ;
va_copy (auxvlist.list, parent.list );
for (int i = 0; i < z; ++i)
{va_copy (auxauxvlist.list, auxvlist.list);
vfprintf (streams[i], format, auxauxvlist.list);
va_end (auxauxvlist.list);
}
va_end (auxvlist.list);
va_end (vlist.list);
free (streams);
}
void printf_variant (char const *format, ...)
{my_list vlist;
va_start (vlist.list, format);
vffprintf (1, stdout , format, vlist);
va_end (vlist.list);
}
int main (void)
{printf_variant ("Ramanujan's number is %d.\n", 1729 );
return 0;
}
问题源于数组和同类型的指针不兼容,va_list定义为数组。然后你尝试得到那个类型:
va_arg (vlist, va_list)
所以你告诉 va_arg
你得到了一个数组,但如果事实上传递的 va_list
已经衰减为一个指针。你应该使用 va_list
的指针版本,但你不知道 va_list
的真正定义,所以你无法获得它的指针版本。
解决方案是将 va_list
包装到您控制的类型中,一个结构。
我想设计一个函数,它接受可变数量的参数,其中一个参数本身就是一个 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);
其代码为:
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);
}
效果很好。现在,还有标准函数 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);
}
这也很好用。现在,我的目标是结合这两种想法来创建一个我称之为 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);
}
我设计了如下代码:
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);
}
这段代码可以顺利编译,但不能正常工作...例如,如果我编写以下完整代码:
#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;
}
我遇到段错误!...为什么?!
P.-S.:很抱歉这个很长的问题;但我希望它非常清楚,因为它是相当技术性的...
P.-S.2:这个问题我特意用了"va-list"和"variableargumentlists"两个标签,因为我感兴趣的是va_list
,被看作一个类型,在(其他)变量参数列表中,被视为一个列表...所以这实际上是两个不同的概念。
void vffprintf (int z, ...)
{
//...
va_copy (auxvlist, va_arg (vlist, va_list));//this line has the problem
//...
}
像这样一个快速而巧妙的方法,它会奏效。
void vffprintf (int z, ...)
{
//...
va_copy (auxvlist, va_arg (vlist, void*));
//...
}
这里有一些关于var_arg
和va_list
的参考资料,应该已经提供了详细透彻的解释。
1) Pass va_list or pointer to va_list?
2) Is GCC mishandling a pointer to a va_list passed to a function?
3) What is the format of the x86_64 va_list structure?
希望对您有所帮助。
C11终稿(N1570)中对va_arg
的描述包含(类型为第二个参数):
if type is not compatible with the type of the actual next argument (as promoted according to the default argument promotions), the behavior is undefined
va_list
可以是数组类型(标准要求它是所谓的“完整对象类型”),您的实现似乎利用了这种可能性。您可能知道,在 C 中,数组不能作为参数传递,因为它们会退化为指针,并且此类指针的类型与原始数组类型不兼容。
例如:int *
与 int [1]
不兼容。因此,如果您确实需要可移植地传递数组或 va_list
,则定义一个带有 va_list
成员的 struct
并传递它(参见 Why can't we pass arrays to function by value?)。
如果要使用 va_arg():
检索它,您可能需要将类型 va_list 包装到结构中#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
typedef struct
{
va_list list ;
} my_list ;
void vffprintf (int z, ...)
{my_list vlist, auxvlist, auxauxvlist;
FILE **streams = malloc (z * sizeof(FILE *));
va_start (vlist.list, z);
for (int i = 0; i < z; ++i)
{streams[i] = va_arg (vlist.list, FILE *);
}
char const *format = va_arg (vlist.list, char const *);
my_list parent = va_arg (vlist.list, my_list) ;
va_copy (auxvlist.list, parent.list );
for (int i = 0; i < z; ++i)
{va_copy (auxauxvlist.list, auxvlist.list);
vfprintf (streams[i], format, auxauxvlist.list);
va_end (auxauxvlist.list);
}
va_end (auxvlist.list);
va_end (vlist.list);
free (streams);
}
void printf_variant (char const *format, ...)
{my_list vlist;
va_start (vlist.list, format);
vffprintf (1, stdout , format, vlist);
va_end (vlist.list);
}
int main (void)
{printf_variant ("Ramanujan's number is %d.\n", 1729 );
return 0;
}
问题源于数组和同类型的指针不兼容,va_list定义为数组。然后你尝试得到那个类型:
va_arg (vlist, va_list)
所以你告诉 va_arg
你得到了一个数组,但如果事实上传递的 va_list
已经衰减为一个指针。你应该使用 va_list
的指针版本,但你不知道 va_list
的真正定义,所以你无法获得它的指针版本。
解决方案是将 va_list
包装到您控制的类型中,一个结构。