C++: от строкового потока до символа **
У меня класс с parse(int argc, char* argv[])
функция, которую я должен использовать, чтобы установить желаемое состояние объекта. Я беру параметры из графического интерфейса, используя stringstream
а затем я пытаюсь преобразовать их в char**, чтобы передать их в функцию. Вот что у меня есть:
std::stringstream sstream;
sstream << "-clip" << " " << min_x_entry.get_text()
<< " " << max_x_entry.get_text(); // etc.
std::cout << sstream.str(); // All looks good here
std::vector<std::string> args;
std::vector<char*> argv;
std::string arg;
while (sstream >> arg)
{
args.push_back(arg);
argv.push_back(const_cast<char*>(args.back().c_str()));
}
argv.push_back(0);
int argc = args.size();
for (int i = 0; i < argc; ++i)
std::cout << &argv[0][i]; // This outputs garbage
my_object.parse(argc, &argv[0]) // And this fails
Что мне не хватает? Есть ли лучший способ добиться этого?
4 ответа
Проблемой будет перераспределение args
вектор как push_back()
увеличит размер вектора, если потребуется:
Если новый размер () не больше емкости (), никакие итераторы или ссылки не будут признаны недействительными. В противном случае все итераторы и ссылки становятся недействительными.
argv
вектор хранит указатели на внутренние элементы в args
, так что они будут признаны недействительными.
Решение было бы создать args
сначала вектор, а затем создать argv
вектор потом:
while (sstream >> arg) args.push_back(arg);
for (auto i = args.begin(); i != args.end(); i++)
{
argv.push_back(const_cast<char*>(i->c_str()));
}
argv.push_back(0);
for
цикл, который распечатывает argv
строки неверны. Это:
&argv[0][i]
это char*
но начинается с i
элемент первой записи в argv
, Например, если первая c-строка в argv
было "string"
:
&argv[0][1] is "tring"
&argv[0][2] is "ring"
изменить на:
for (int i = 0; i < argc; i++)
std::cout << argv[i] << std::endl; // Added 'endl' to flush 'cout'.
std::vector<std::string> args;
std::vector<char*> argv;
/* ... */
argv.push_back(const_cast<char*>(args.back().c_str()));
Здесь много проблем.
- Указатель возвращается
c_str()
не гарантируется быть действительным после любого последующего вызоваconst
функция-член того жеstring
, Указатель возвращается изc_str()
обычно не следует хранить и использовать позже, особенно если вы не уверены, будет ли другой код вызыватьconst
членstring
, - Вы
const_cast
вconst
-ndd от указателя, возвращенногоc_str()
, Сам актерский состав является законным, если не анти-шаблон. Но если позже вы попытаетесь изменить данные, хранящиеся в этом указателе, это неопределенное поведение.
Вот что говорит Стандарт о c_str()
:
21.3.6 Строковые операции basic_string [lib.string.ops]
const charT* c_str() const;
1 / Возвращает: указатель на начальный элемент массива длины size() + 1, чьи первые элементы size () равны соответствующим элементам строки, управляемой *this, и чей последний элемент является нулевым символом, заданным charT(),
2/ Требуется: программа не должна изменять ни одно из значений, хранящихся в массиве. Кроме того, программа не должна обрабатывать возвращаемое значение как действительное значение указателя после любого последующего вызова неконстантной функции-члена класса basic_string, которая обозначает тот же объект, что и этот. const charT * data () const;
3 / Returns: Если size () не равен нулю, элемент возвращает указатель на начальный элемент массива, чьи первые элементы size () равны соответствующим элементам строки, управляемой *this. Если size () равен нулю, элемент возвращает ненулевой указатель, который можно скопировать и к которому можно добавить ноль.
4 / Требуется: программа не должна изменять какие-либо значения, хранящиеся в массиве символов. Также программа не должна обрабатывать возвращаемое значение как допустимое значение указателя после любого последующего вызова неконстантной функции-члена basic_string, которая обозначает тот же объект, что и этот. allocator_type get_allocator () const;
5 / Returns: копия объекта Allocator, используемого для построения строки.
Вы забыли инициализировать переменную i
в петле. И вы пытаетесь распечатать только первый элемент в векторе argv
,
for (int i = 0; i < argc; ++i)
std::cout << argv[i];
Вы можете избавиться от const_cast
и не беспокоиться о parse()
Метод, возможно, изменяет аргументы, делая что-то вроде этого:
std::vector<std::vector<char>> args;
std::for_each(std::istream_iterator<std::string>(sstream),
std::istream_iterator<std::string>(),
[&args](const std::string& str)
{
std::vector<char> temp(str.begin(), str.end());
temp.push_back('\0');
args.push_back(temp);
});
std::vector<char*> argv(args.size());
for (auto& v : args) argv.push_back(v.data());
my_object.parse(argv.size(), argv.data());