Leif Chipman
Leif Chipman

Reputation: 51

Creating a char* const * for execvp()

Having a great deal of trouble satisfying the second argument of execvp. The compiler says it needs a char* const* passed where I have a const char**. I would prefer it made from user input:

std::string x;
std::cout<<" [CMD]: ";
getline(std::cin, x);
const char** args = fillArgs(x);
execvp(args[0], args);

The code for fillArgs is to convert the string to a dynamically allocated pointer to an array of chars. In a nutshell it looks like this:

const char** fillArgs(std::string currentCMD)
{
     //Above this, the string passed into the function is split into substrings separated
    //by whitespaces and each is placed into the string vector called input.

    const char** command = new const char*[SIZE];
    for(int i = 0; i < SIZE; i++)
    {
        command[i] = input[i].c_str();
    }
    return command;
}

I am aware it needs to end with a nullpointer and took care of that in the code above. Suggestions?

Upvotes: 4

Views: 2912

Answers (2)

Leif Chipman
Leif Chipman

Reputation: 51

Well, I went back to using that vector of strings after execvp not working when converting from vector<string> to vector<char*> to char** was brought to my attention. More efficient that way and easier to read my code. I was missing the .data() function.

Upvotes: 0

tmlen
tmlen

Reputation: 9090

char* const argv[] (and char* const *argv) is an array of const pointers to mutable char arrays. (Even if execvp probably will not modify the strings).

But const char** args (same as char const** args) is an array of mutable pointers to const char arrays.

std::string::c_str returns a const char* pointer to a const char array. But execvp requires mutable char arrays.


One solution would be to use const_cast:

const char* command = input[i].c_str();
char* command_mutable = const_cast<char*>(command);

char* const *argv;
argv[i] = command_mutable;

This works in the case of execvp because it just reads the strings and copies them for use by the new executable, and does not return. For a function that could modify the strings and return normally, it is undefined behavior.

But another problem is that the pointer returned by c_str is valid only as long as the std::string object exists. The function fillArgs is incorrect because when it returns the pointers in the return value are invalid. (Also the command array itself if it was allocated on the stack.)

So this would work:

std::string str = "example";
char* const args[3];
args[1] = const_cast<char*>(str.c_str());
args[2] = nullptr;
execvp(args[0], args);
// Not the other program is running

When using another function than execvp that could modify the array, it would be better to create a temporary array for the function:

std::string str = "example";
std::unique_ptr<char[]> tmp_str(new char[str.size() + 1]);
std::memcpy(tmp_str.get(), str.c_str(), str.size() + 1);
char* const args[3];
args[1] = tmp_str.get();
args[2] = nullptr;
something(args[0], args);
tmp_str.reset(); // release the temporary memory

Upvotes: 2

Related Questions