如何在 C 中将可变参数转换为数组

问题描述 投票:0回答:3

对于我的一个项目,我创建了一个函数来运行外部命令(简化为对我的问题重要的内容):

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()
检索的字符串是否仍然有效。

c arrays variadic
3个回答
3
投票

你正在做的事情将会按预期进行。

va_arg
的调用使您可以访问传递给函数的
char *
参数。这些指针的值是传递给
run_command2
的值,这意味着它们的作用域至少在调用函数中有效。

所以即使在致电

va_end
之后它们仍然有效。


0
投票

由于这个主题没有太多可用的内容,我决定实现我能想到的所有可能的包装变体,并将它们放在要点中:Link

希望这可以帮助其他面临同样任务的人。


-2
投票

这个

args = (const char **) malloc(argc * sizeof(char*));
很奇怪。我宁愿先分配
char **args = malloc(sizeof(char*) * (argc));
,然后分配
args[i] = malloc(sizeof(char) * (strlen(val) + 1));

您需要分配

char **
,然后分配该数组中的每个字符串。

© www.soinside.com 2019 - 2024. All rights reserved.