2016-02-06 4 views
0

Переход от вектора строк к вектору char * в char ** работал, когда аргумент пришел как char **, но у меня, похоже, проблема, и я не могу найти разницу.execvp не работает при преобразовании из вектора <string> в вектор <char*> в char **

Есть ли лучший способ сделать это?

vector<string> args; 

    /* code that correctly parses args from user input */ 

    pid_t kidpid = fork(); 
    if (kidpid < 0) 
    { 
     perror("Internal error: cannot fork."); 
     return -1; 
    } 
    else if (kidpid == 0) 
    { 
     // I am the child. 

     vector<char*>argcs; 
     for(int i=1;i<args.size();i++) 
     { 
      char * temp = new char[args.at(i).length()]; 
      for(int k=0;k<args.at(i).length();k++) 
      { 
       temp[k] = args.at(i).at(k); 
      } 
      argcs.push_back(temp); 
     } 

     char** argv = new char*[argcs.size() + 1]; 
     for (int i = 0; i < argcs.size(); i++) 
     { 
      argv[i] = argcs[i]; 
     } 
     argv[args.size()] = NULL; 

     execvp(program, args); 

     return -1; 
    } 
+0

Позволить нам о том, что проблема может помочь. – user4581301

+0

Извините user, execvp не смог найти подходящий конструктор – redux

ответ

3

Во-первых, нет никакого смысла в копировании std::string сек, если следующая вещь, которую вы собираетесь сделать, это вызов execvp.

Если execvp удался, то он никогда не вернется, и весь образ памяти исчезнет в дыму (или, точнее, будет заменен совершенно новым изображением). В процессе построения нового изображения exec* скопирует массив argv (и массив окружения) в него. В любом случае деструкторы std::vector и std::string никогда не будут вызываться.

Если, с другой стороны, execvp терпит неудачу, то переданный в него аргумент не будет изменен. (Posix: «The argv[] и envp[] массивы указателей и строки, в которой те точки массивов не должны быть изменены с помощью вызова одной из функций EXEC, за исключением того, как следствие замены образа процесса.»)

В в любом случае, не нужно было копировать строки символов. Вы можете использовать std::string::c_str() для извлечения указателя на базовую строку C (как const char*, но см. Ниже).

Во-вторых, если вы используете C++ 11 или более поздней версии, то std::vector удобно имеет функцию-член data(), которая возвращает указатель на базовое хранилище. Так что если у вас есть std::vector<char*> svec, то svec.data() будет лежащим в основе char*[], вот что вы хотите передать в execvp.

Таким образом, задача сводится к созданию std::vector<char*> из std::vector<std::string>, которая проста:

else if (kidpid == 0) { 
    // I am the child. 
    std::vector<char*> argc; 
    // const_cast is needed because execvp prototype wants an 
    // array of char*, not const char*. 
    for (auto const& a : args) 
     argc.emplace_back(const_cast<char*>(a.c_str())); 
    // NULL terminate 
    argc.push_back(nullptr); 
    // The first argument to execvp should be the same as the 
    // first element in argc, but we'll assume the caller knew 
    // what they were doing, and that program is a std::string. 
    execvp(program.c_str(), argc.data()); 
    // It's not clear to me what is returning here, but 
    // if it is main(), you should return a small positive value 
    // to indicate an error 
    return 1; 
} 
+0

Имейте 'std :: vector argc', не нужно создавать const. –

+0

@nm: Тогда argc.data является 'const char * []', который не является правильным прототипом для 'execvp' (TIAS:" error: invalid conversion from 'const char **' to 'char * const *' [- fpermissive] ") – rici

+0

Хм, да. У меня создалось впечатление, что аргументом execvp является 'const char * const *'. Но нет, этот прототип не слишком хорошо работал бы на C. Слишком плохо! –