Лучшая практика для использования execvp в переполнении стека

В начале я написал что-то вроде этого

char* argv[] = { "ls", "-al", ..., (char*)NULL };
execvp("ls", argv);

Тем не менее, GCC выскочил этот предупреждение, «C ++ запрещает преобразование строковой константы в char*«.

Затем я изменил свой код в

const char* argv[] = { "ls", "-al", ..., (char*)NULL };
execvp("ls", argv);

В результате GCC выскочил этот ошибка, «неверное преобразование из const char** в char* const*«.

Затем я изменил свой код в

const char* argv[] = { "ls", "-al", ..., (char*)NULL };
execvp("ls", (char* const*)argv);

Наконец-то это работает и компилируется без каких-либо предупреждений и ошибок, но я думаю, что это немного громоздко, и я не могу найти, чтобы кто-нибудь написал что-то подобное в Интернете.

Есть ли лучший способ использовать execvp в С ++?

5

Решение

Вы столкнулись с реальной проблемой, потому что мы столкнулись с двумя несовместимыми ограничениями:

  1. Один из стандарта C ++, требующий от вас использования const char*:

    В C строковые литералы имеют тип char[]и может быть назначен напрямую
    к (неконстантному) char*, В C ++ 03 это тоже разрешено (но не рекомендуется,
    как литералы const в C ++). C ++ 11 больше не позволяет такие назначения
    без актерского состава.

  2. Другой из старого прототипа функции C, который требует массив (неконстантный) char*:

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

    Таким образом, должно быть бросать где-то и единственное решение, которое я нашел, это обернуть execvp функция.

Вот полная демонстрация C ++ этого решения. Неудобство состоит в том, что у вас есть некоторый клейкий код, чтобы написать один раз, но преимущество в том, что вы получаете безопаснее и чище C ++ 11 код (финал nullptr проверено).

#include <cassert>
#include <unistd.h>

template <std::size_t N>
int execvp(const char* file, const char* const (&argv)[N])
{
assert((N > 0) && (argv[N - 1] == nullptr));

return execvp(file, const_cast<char* const*>(argv));
}

int main()
{
const char* const argv[] = {"-al", nullptr};
execvp("ls", argv);
}

Вы можете скомпилировать это демо с:

g++ -std=c++11 demo.cpp

Вы можете увидеть похожий подход в CPP Справочный пример для std::experimental::to_array.

5

Другие решения

Это конфликт между декларацией execvp() (который не может обещать не изменять свои аргументы для обратной совместимости) и интерпретация строковых литералов в C ++ как массивов констант char,

Если приведение касается вас, оставшийся вариант — скопировать список аргументов, например так:

#include <unistd.h>
#include <cstring>
#include <memory>
int execvp(const char *file, const char *const argv[])
{
std::size_t argc = 0;
std::size_t len = 0;

/* measure the inputs */
for (auto *p = argv;  *p;  ++p) {
++argc;
len += std::strlen(*p) + 1;
}
/* allocate copies */
auto const arg_string = std::make_unique<char[]>(len);
auto const args = std::make_unique<char*[]>(argc+1);
/* copy the inputs */
len = 0;                    // re-use for position in arg_string
for (auto i = 0u;  i < argc;  ++i) {
len += std::strlen(args[i] = std::strcpy(&arg_string[len], argv[i]))
+ 1; /* advance to one AFTER the nul */
}
args[argc] = nullptr;
return execvp(file, args.get());
}

(Вы можете рассмотреть std::unique_ptr быть излишним, но эта функция делает правильно очистить, если execvp() не удается, и функция возвращает).

Демо-версия:

int main()
{
const char *argv[] = { "printf", "%s\n", "one", "two", "three", nullptr };
return execvp("printf", argv);
}
one
two
three
2

execvpe требует char *const argv[] как это второй аргумент. То есть требуется список константных указателей на неконстантные данные. Строковые литералы в C — это постоянная проблема с предупреждениями и приведением argv в char* const* взломать как execvp теперь разрешено писать в строки в вашем argv, Решение, которое я вижу, состоит в том, чтобы либо выделить буфер записи для каждого элемента, либо просто использовать execlp вместо этого, который работает с const char* args и позволяет передавать строковые литералы.

0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector