如何使用不确定数量的参数和 va_list 来组合参数?
How to make function to combine parameters using indeterminate number of parameters and va_list?
我想用这个原型做一个函数:
char *combine(const char *string, ...)
将命令行给出的参数和 returns 结果合并为 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] = '[=15=]';
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;
}
将 va_list
视为不透明类型,如 "never mind what it really is"。 "functions"、va_start()
、va_arg()
、va_end()
其实不是函数,而是宏。同样,一开始它们到底是什么以及它们实际如何工作并不那么重要。 ("Pay no attention to that man behind the curtain!")您现在需要知道的是如何使用它们。
假设您正在编写自己的函数:
char *combine(const char *mj, ...)
有一个参数 const char *mj
,然后...可能还有其他一些。那么,如果这些参数没有标识符,而且您甚至不知道它们有多少,您如何引用这些参数呢?这就是 va_list
的用途。可以把它想象成一个游标,可以滚动浏览这个参数列表,并且可以以某种方式告诉你下一个参数是什么。
首先,你必须设置 va_start
:
va_start(pl, mj);
这意味着使用 mj
设置 va_list pl
作为 最后已知的显式参数。
第一次使用 va_arg()
和 pl
时,它会给你第一个参数 after mj
。您还需要在 va_arg()
中指定下一个参数的预期类型,因此在您的情况下,调用将是 va_arg(pl, char *)
。每次您使用 va_arg()
时,它都会推进此 "cursor",以便下一次调用 va_arg()
时会为您提供下一个参数。所以每次你使用 va_arg()
时,你都应该用这个值做一些事情,因为你不会再得到它了。 (除非,也许你重新开始。)
那么你怎么知道你是否在列表的末尾?好吧,您只需要以某种方式知道即可。 va_arg()
不会告诉您您在列表的末尾,如果您继续使用它,它会很乐意继续越过末尾。通常,您会使用最后一个已知参数(在您的情况下 mj
)以某种方式指定有多少个参数,或者您可以在参数列表中查找一些标记值。
当您决定完成后,请务必使用
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);
确实 而不是 将任何内容分配给 mj
,正如您可能认为的那样。它仅使用 mj
设置 va_list pl
作为最后一个已知的显式参数。您还出于某种原因重新分配 mj
(该部分没有意义)。您甚至根本没有尝试连接任何字符串。看起来你只是想打印它们。从某个大小的动态分配的缓冲区开始,将字符串一个一个地附加到它,然后根据需要重新分配。 (这并不难,但第一次做时会显得不平凡。如何实现它超出了本题的范围。)
va_list
没有 "combine" 任何东西,它一次只给你一个函数的参数(它更像是把东西分开)。你必须自己进行组合,va_list
就是如何得到碎片。
我想用这个原型做一个函数:
char *combine(const char *string, ...)
将命令行给出的参数和 returns 结果合并为 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] = '[=15=]';
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;
}
将 va_list
视为不透明类型,如 "never mind what it really is"。 "functions"、va_start()
、va_arg()
、va_end()
其实不是函数,而是宏。同样,一开始它们到底是什么以及它们实际如何工作并不那么重要。 ("Pay no attention to that man behind the curtain!")您现在需要知道的是如何使用它们。
假设您正在编写自己的函数:
char *combine(const char *mj, ...)
有一个参数 const char *mj
,然后...可能还有其他一些。那么,如果这些参数没有标识符,而且您甚至不知道它们有多少,您如何引用这些参数呢?这就是 va_list
的用途。可以把它想象成一个游标,可以滚动浏览这个参数列表,并且可以以某种方式告诉你下一个参数是什么。
首先,你必须设置 va_start
:
va_start(pl, mj);
这意味着使用 mj
设置 va_list pl
作为 最后已知的显式参数。
第一次使用 va_arg()
和 pl
时,它会给你第一个参数 after mj
。您还需要在 va_arg()
中指定下一个参数的预期类型,因此在您的情况下,调用将是 va_arg(pl, char *)
。每次您使用 va_arg()
时,它都会推进此 "cursor",以便下一次调用 va_arg()
时会为您提供下一个参数。所以每次你使用 va_arg()
时,你都应该用这个值做一些事情,因为你不会再得到它了。 (除非,也许你重新开始。)
那么你怎么知道你是否在列表的末尾?好吧,您只需要以某种方式知道即可。 va_arg()
不会告诉您您在列表的末尾,如果您继续使用它,它会很乐意继续越过末尾。通常,您会使用最后一个已知参数(在您的情况下 mj
)以某种方式指定有多少个参数,或者您可以在参数列表中查找一些标记值。
当您决定完成后,请务必使用
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);
确实 而不是 将任何内容分配给 mj
,正如您可能认为的那样。它仅使用 mj
设置 va_list pl
作为最后一个已知的显式参数。您还出于某种原因重新分配 mj
(该部分没有意义)。您甚至根本没有尝试连接任何字符串。看起来你只是想打印它们。从某个大小的动态分配的缓冲区开始,将字符串一个一个地附加到它,然后根据需要重新分配。 (这并不难,但第一次做时会显得不平凡。如何实现它超出了本题的范围。)
va_list
没有 "combine" 任何东西,它一次只给你一个函数的参数(它更像是把东西分开)。你必须自己进行组合,va_list
就是如何得到碎片。