对于我的一个项目,我创建了一个函数来运行外部命令(简化为对我的问题重要的内容):
int run_command(char **output, int *retval, const char *command, const char* const args[])
{
...
pid_t pid = fork();
if (pid == 0) {
...
execvp(command, (char * const *)args);
}
...
}
函数的调用方式如下:
char *output;
int retval;
const char *command = "find";
const char* const args[] = { command, "/tmp", "-type", "f", NULL };
run_command(&output, &retval, command, args);
现在,我创建了一个使用可变参数而不是参数数组的包装器:
int run_command2(char **output, int *retval, const char *command, ...)
{
va_list val;
const char **args = NULL;
int argc;
int result;
// Determine number of variadic arguments
va_start(val, command);
argc = 2; // leading command + trailing NULL
while (va_arg(val, const char *) != NULL)
argc++;
va_end(val);
// Allocate args, put references to command / variadic arguments + NULL in args
args = (const char **) malloc(argc * sizeof(char*));
args[0] = command;
va_start(val, command);
int i = 0;
do {
fprintf(stderr, "Argument %i: %s\n", i, args[i]);
i++;
args[i] = va_arg(val, const char *);
} while (args[i] != NULL);
va_end(val);
// Run command, free args, return result
result = run_command(output, retval, command, args);
free(args);
return result;
}
编辑:注意 do-while 循环:
对于最后一个元素,调用
fprintf(stderr, "Argument %i: %s\n", i, NULL)
,这在 GCC 上有效,并且只会打印“(null)”。对于其他编译器,行为可能不同或未定义。感谢@GiovanniCerretani 指出了这一点。
包装器的调用方式如下:
char *output;
int retval;
run_command2(&output, &retval, "find", "/tmp", "-type", "f", NULL);
我的问题:
包装器似乎工作正常(Linux/x64/GCC 9.2.0),但这实际上是将可变参数转换为数组的有效方法吗?或者这只是偶然起作用?
va_*
上的文档非常薄弱,例如没有提及当再次调用 va_arg()
或调用 va_arg()
后使用 va_end()
检索的字符串是否仍然有效。
你正在做的事情将会按预期进行。
对
va_arg
的调用使您可以访问传递给函数的 char *
参数。这些指针的值是传递给 run_command2
的值,这意味着它们的作用域至少在调用函数中有效。
所以即使在致电
va_end
之后它们仍然有效。
由于这个主题没有太多可用的内容,我决定实现我能想到的所有可能的包装变体,并将它们放在要点中:Link
希望这可以帮助其他面临同样任务的人。
这个
args = (const char **) malloc(argc * sizeof(char*));
很奇怪。我宁愿先分配 char **args = malloc(sizeof(char*) * (argc));
,然后分配 args[i] = malloc(sizeof(char) * (strlen(val) + 1));
您需要分配
char **
,然后分配该数组中的每个字符串。