Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to convert variadic arguments to array in C

Tags:

arrays

c

variadic

For a project of mine, I created a function to run external commands (reduced to what's important for my question):

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);
    }
    ...
}

The function is called like this:

char *output;
int retval;
const char *command = "find";
const char* const args[] = { command, "/tmp", "-type", "f", NULL };
run_command(&output, &retval, command, args);

Now, I created a wrapper that uses variadic arguments instead of an array of arguments:

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;
}

EDIT: note on do-while loop:
For the last element, fprintf(stderr, "Argument %i: %s\n", i, NULL) is called, which is valid on GCC and will simply print '(null)'. For other compilers, behavior might be different or undefined. Thanks to @GiovanniCerretani for pointing this out.

The wrapper is called like this:

char *output;
int retval;
run_command2(&output, &retval, "find", "/tmp", "-type", "f", NULL);

My question:

The wrapper seems to work fine (Linux/x64/GCC 9.2.0), but is this actually a valid way to convert variadic arguments to array? Or does this just work by accident? The documentation on va_* is quite thin, e.g. there's no mention if a string retrieved using va_arg() remains valid when va_arg() is called again or after calling va_end().

like image 330
Maxxim Avatar asked Mar 14 '26 18:03

Maxxim


2 Answers

What you are doing will work as expected.

The calls to va_arg get you access to the char * arguments that were passed to the function. The values of these pointers is what was passed to run_command2 meaning their scope is valid at least in the calling function.

So they are valid even after calling va_end.

like image 131
dbush Avatar answered Mar 17 '26 08:03

dbush


As there's not much available on this topic, I decided to implement all possible wrapper variants I could think of and put them in a Gist: Link

Hopefully this helps others facing the same task.

like image 20
Maxxim Avatar answered Mar 17 '26 08:03

Maxxim



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!