Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I pass a const char* array to execv?

This is the prototype for execv:

int execv(const char *path, char *const argv[]);

Can I pass an array of const char pointers as the second argument?

This example program gives a warning when USE_CAST is not set:

#include <unistd.h>
int main(int argc, char *argv[])
{
    if (argc > 0) {
        const char *exe_name = "/bin/echo", *message = "You ran";
        const char *exe_args[] = { exe_name, message, argv[0], NULL };
#ifdef USE_CAST
    execv("/bin/echo", (char **) exe_args);
#else
    execv("/bin/echo", exe_args);
#endif
    }
    return 0;
}

When compiling, gcc says, "passing argument 2 of 'execv' from incompatible pointer type" if I don't use the cast.

From the POSIX documentation for execv (halfway through the Rationale section), it looks like the second argument is a char *const array only for backwards compatibility:

The statement about argv[] and envp[] being constants is included to make explicit to future writers of language bindings that these objects are completely constant. ... It is unfortunate that the fourth column cannot be used...

where the "fourth column" refers to const char* const[].

Is the (char **) cast safe to use here? Should I create a char * array and pass that to execv instead?

like image 807
yellowantphil Avatar asked Apr 28 '16 21:04

yellowantphil


1 Answers

Can I pass an array of const char pointers as the second argument?

Well yes, you already know that you can cast in order to do so.

From the POSIX documentation for execv (halfway through the Rationale section), it looks like the second argument is a char *const array only for backwards compatibility:

I wouldn't put it in those terms, but yes, there is a compatibility aspect to the chosen signature. The section you reference explains that C does not have a wholly satisfactory way to express the degree of const-ness that POSIX requires execv() to provide for the arguments. POSIX guarantees that the function will not change either the pointers in argv or the strings to which they point.

With that being the case, I think it not unreasonable to cast the argv pointer as you propose to do, though I would leave a comment in my code explaining why doing so is safe.

On the other hand, you should consider simply leaving the const off of your array declaration:

char *exe_name = "echo", *message = "You ran";
char *exe_args[] = { exe_name, message, argv[0], NULL };

Or, in your simple example, even this would do:

char *exe_args[] = { "echo", message, argv[0], "You ran", NULL };

C string literals correspond to arrays of type char, not const char, so this is perfectly legal as far as C is concerned, even though actually trying to modify the contents of those strings might fail.

On the third hand, modern C has array literals, so you could even do this:

execv("/bin/echo", (char *[]) { "echo", "You ran ", argv[0], NULL });

In that last case you don't even have a cast (the thing that resembles one is just part of the syntax for an array literal).

like image 62
John Bollinger Avatar answered Sep 29 '22 06:09

John Bollinger