0

I have been trying to avoid using char ** arrays, however because i am using execvp, i wrote a method to convert a vector of strings to a null terminated char **.

Unfortunately i have until recently been getting segmentation faults, which makes me suspicious that my method doesn't always work for one reason or another:

void vectorToNullArray(std::vector<std::string> const &v, char **a) {
  int i = 0;
  while (i < v.size()) {
    std::string str = v[i];
    char *cstr = new char[str.size()+1];
    strcpy(cstr, str.c_str());
    a[i] = cstr;                       // <-- segfault here
    i++;
  }
  a[i] = nullptr;
}

if i can be completely free of char ** in the first place, that would be ideal. Why is this code segfaulting?

EDIT: this is how i call it:

  char **argvA;
    vectorToNullArray(_ARGV, argvA);

where _ARGV is an std::vector<std::string>, a member variable of some class.

EDIT 2: working solution

std::vector<char *> nullTerminatedArgV(std::vector<std::string> const &v) {
  std::vector<char *> result;
  for (auto const &s : v) {
    result.push_back(const_cast<char *>(s.c_str()));
  }
  result.push_back(nullptr);
  return result;
}

usage with execvp:

    std::vector<char *> argv = nullTerminatedArgV(_ARGV);
    char **command = argv.data();

      int status = execvp(command[0], command);
      perror("execvp error");

essentially the accepted answer, with additions from here:

https://stackoverflow.com/a/47714627/5133238

Tak Makoni
  • 467
  • 1
  • 4
  • 7

2 Answers2

4

As Pete Becker says, the issue's probably that you haven't called it properly - and specifically that the a argument doesn't point to a sufficiently large array of char*s to write to.

Anyway, do you really need to screw around with dynamic memory allocation here? Having std::vector manage memory for you is much easier, safer, and more concise. Perhaps you can do something like below, then pass the result's .data() value to execvp? You do need to make sure the vector of strings passed as an argument is not mutated by the caller after this call and before calling execvp, otherwise the pointers you collect may be invalidated.

std::vector<const char*> getVectorOfPointersToStrings(std::vector<std::string> const& v) {
    std::vector<const char*> result;
    for (const auto& s : v)
        result.push_back(s.c_str());
    result.push_back(nullptr);
    return result; 
}
Tony Delroy
  • 102,968
  • 15
  • 177
  • 252
1
char **argvA;
vectorToNullArray(_ARGV, argvA);

argvA has some random value; passing it to vectorToNullArray results in undefined behavior.

The code doesn't allocate the array itself, but assigns values to it.

Pete Becker
  • 74,985
  • 8
  • 76
  • 165