我想用这个原型制作一个函数:
char *combine(const char *string, ...)
组合从命令行给出的参数,并将结果返回到main
。
我的功能目前看起来像这样:
char *combine(const char *mj, ...)
{
va_list pl;
va_start(pl, mj);
while(mj != NULL) {
printf("%s",mj);
mj = va_arg(pl, char *);
}
va_end(pl);
return pl;
}
例如,如果用户使用以下命令运行此程序:
./a.out one two three four five six seven
输出应为:
Parameter: onetwothreefourfivesixseven (length of parameter: 27)
现在如果我用它运行我的程序,输出主要是随机元素,参数只是空的。
出了什么问题以及我需要修复什么才能显示正确的内容?
该程序需要使用以下main
代码:
int main(int argc, char *argv[]) {
int i = 0;
char *mj = malloc(1);
mj[0] = '\0';
for(i = 1; (i+3) <= argc; i += 3)
{
char *tmp = mj;
mj = combine(tmp, argv[i], argv[i+1], argv[i+2], NULL);
free(tmp);
}
if((i+2) == argc)
{
char *tmp = mj;
mj = combine(mj, argv[i], argv[i+1], NULL);
free(tmp);
}
else if((i+1) == argc)
{
char *tmp = mj;
mj = combine(mj, argv[i], NULL);
free(tmp);
}
printf("Parameter: %s (length: %lu)\n", mj, (unsigned long)strlen(mj));
free(mj);
return 0;
}
答案 0 :(得分:0)
将va_list
视为一种不透明的类型,就像“从不介意它是什么”一样。 “函数”va_start()
,va_arg()
和va_end()
实际上不是函数,而是宏。同样,它们实际上是什么以及它们实际如何工作起初并不重要。 (“不要在窗帘后面注意那个人!”)你现在需要知道的是如何使用。
假设您正在编写自己的函数:
char *combine(const char *mj, ...)
有一个参数const char *mj
,然后......可能是其他一些参数。那么如果没有标识符,你如何引用这些参数,如果你甚至不知道它们有多少?这就是va_list
的用途。可以把它想象成一个可以滚动浏览这个参数列表的游标,并可以某种方式告诉你下一个参数是什么。
首先,您必须使用va_start
:
va_start(pl, mj);
这意味着使用va_list pl
设置mj
作为最后一个已知的显式参数。
第一次将va_arg()
与pl
一起使用时,它会在 mj
之后为您提供第一个参数。您还需要在va_arg()
中指定您希望下一个参数的预期类型,因此在您的情况下,调用将是va_arg(pl, char *)
。每次使用va_arg()
时,它都会前进此“光标”,以便下一次调用va_arg()
将为您提供下一个参数。因此,每次使用va_arg()
时,您都应该使用此值执行某些操作,因为您不会再次使用此值。 (除非,也许你重新开始。)
那你怎么知道你是否在列表的最后?好吧,你只需知道某种程度。 va_arg()
不会告诉你你在列表的末尾,如果你继续使用它,它会很乐意继续走到尽头。通常,您将使用最后一个已知参数(在您的情况下为mj
)以某种方式指定有多少参数,或者您可以在参数列表中查找一些sentinel值。
当您决定完成后,请务必使用
va_end(pl);
在此之后,va_list pl
无效,不应再次引用。 (除非你想从另一个va_start()
开始。)
说明如何使用它的一个例子是经典printf()
,它使用变量参数。从man pages开始,我们知道printf()
被声明为
int printf(const char *format, ...);
考虑一下,例如函数调用
printf("The value of %s is %ld\n", lbl, x);
在内部,printf()
会使用一些va_list
,我们称之为vl
。 format
是最后一个已知的参数,因此它将在va_start(vl, format)
中使用。 printf()
将解析此format
字符串,找到%s
并期望接下来有char *
个参数。然后它将调用va_arg(vl, char *)
,以某种方式使用此值,并继续解析format
字符串。然后它找到%ld
并期望一些long int
,因此它会调用va_arg(vl, long int)
并在打印时使用此值。在解析了format
之后,它会到达格式字符串的末尾,然后停止,最终使用va_end(vl)
。 (如果您正在关注,或许现在您可以弄清楚为什么始终在printf
格式字符串中使用正确的长度修饰符和转换说明符非常重要。)
这就是如何使用 va_list
。现在回到您的代码:请记住,va_start(pl, mj);
不会<{1}}分配任何内容,因为您可能会想到。它仅使用mj
作为最后一个已知的显式参数来设置va_list pl
。由于某种原因,您也在重新分配mj
(该部分没有意义)。您根本不想尝试连接任何字符串。看起来你只是想打印它们。从动态分配的某个大小的缓冲区开始,逐个追加字符串,并根据需要重新分配。 (这并不难,但是第一次这样做时看起来并不重要。如何实现它超出了这个问题的范围。)
mj
没有“组合”任何东西,它只能一次为你提供一个函数的参数(更像是它将事情分开)。你必须自己进行组合,va_list
就是如何搞定。