为什么我在使用 istream 迭代器时会出现 seg 错误?
Why am I getting seg faults from using the istream iterator?
void parse_and_run_command(const std::string &command) {
std::istringstream iss(command);
std::istream_iterator<char*> begin(iss), end;
std::vector<char*> tokens(begin, end); //place the arguments in a vector
tokens.push_back(NULL);
根据 GDB 的说法,段错误发生在执行带有istream_iterator的第二行之后。当我使用字符串向量时,它早些时候没有段错误。
首先需要创建一个std::vector
ofstd::string
它将拥有字符串数据,然后您可以将该std::vector
转换为指针std::vector
,请注意,指针仅在std::string
std::vector
的生命周期内有效:
#include <string>
#include <iostream>
#include <sstream>
#include <iterator>
#include <vector>
#include <algorithm>
void parse_and_run_command(const std::string &command) {
std::istringstream iss(command);
std::istream_iterator<std::string> begin(iss), end;
std::vector<std::string> tokens(begin, end);
std::vector<char*> ctokens;
std::transform(tokens.begin(), tokens.end(), std::back_inserter(ctokens), [](std::string& s) { return s.data(); });
ctokens.push_back(nullptr);
for (char* s : ctokens) {
if (s) {
std::cout << s << "n";
}
else {
std::cout << "nullptrn";
}
}
}
int main() {
parse_and_run_command("test test2 test3");
}
首先,您需要将std::string
命令拆分为std::vector<std::string>
类型的标记列表。然后,您可能希望使用std::transform
来填充类型为std::vector<char const*>
的新令牌列表。
下面是一个示例代码:
void parse_and_run_command(std::string const& command) {
std::istringstream iss(command);
std::vector<std::string> results(std::istream_iterator<std::string>{iss},
std::istream_iterator<std::string>());
// debugging
for (auto const& token : results) {
std::cout << token << " ";
}
std::cout << std::endl;
std::vector<const char*> pointer_results;
pointer_results.resize(results.size(), nullptr);
std::transform(
std::begin(results), std::end(results),
std::begin(pointer_results),
[&results](std::string const& str) {
return str.c_str();
}
);
// debugging
for (auto const& token : pointer_results) {
std::cout << token << " ";
}
std::cout << std::endl;
// execv expects NULL as last element
pointer_results.push_back(nullptr);
char **cmd = const_cast<char**>(pointer_results.data());
execv(cmd[0], &cmd[0]);
}
请注意函数的最后一部分:execv
期望最后一个元素nullptr
。
嗯,非常有趣。听起来像是一件容易的事,但有几个警告。
首先,我们需要考虑execv
至少有 2 种不同的实现。
一个在Posix/Linux下,请参阅此处和Windows版本:请参阅此处和此处。
请注意不同的函数签名:
Linux / POSIX: int execv(const char *path, char *const argv[]);
Windows: intptr_t _execv(const char *cmdname, const char *const *argv);
在这种情况下,我发现 WIndows 版本更干净一些,因为 argv 参数的类型为const char *const *
.无论如何,主要问题是,我们必须调用遗留代码。
好吧,让我们看看。
execv
函数需要一个以 NULL 结尾的字符指针数组,其中包含函数调用的参数。我们需要创建这个。
我们从包含命令的std::string
开始。这需要分成几部分。有几种方法,我添加了不同的例子。
最简单的方法可能是将std::string
放入std::istringstream
中,然后使用std::istream_iterator
将其分成几部分。这是典型的短序列:
// Put this into istringstream
std::istringstream iss(command);
// Split
std::vector parts(std::istream_iterator<std::string>(iss), {});
我们使用范围构造函数来表示std::vector
。我们可以在没有模板参数的情况下定义std::vector
。编译器可以从给定的函数参数推断参数。此功能称为 CTAD("类模板参数推导"(。
此外,您可以看到我没有明确使用"end(("迭代器。
此迭代器将从具有正确类型的空大括号括起来的默认初始值设定项构造,因为由于 std::vector 构造函数需要它,它将被推导出为与第一个参数的类型相同。
我们可以避免使用std::istringstream
,并使用std::sregex_token_iterator
直接将字符串转换为令牌。使用起来非常简单。结果是用于拆分原始逗号字符串的单行:
// Split
std::vector<std::string> parts(std::sregex_token_iterator(command.begin(), command.end(), re, -1), {});
所有这些归结为 6 行代码,包括变量的定义和execv
函数的调用:
请看:
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <iterator>
#include <memory>
#include <algorithm>
#include <regex>
const std::regex re{ " " };
// Define Dummy function for _execv (Windows style, eveything const)
// Note: Type of argv decays to " const char* const* "
int _execv(const char* path, const char* const argv[]) {
std::cout << "nnPath: " << path << "nnArguments:nn";
while (*argv != 0) std::cout << *argv++ << "n";
return 0;
}
// Define Dummy function for _execv (Posix style)
// Note: Type of argv decays to " char* const* "
int execv(const char* path, char* const argv[]) {
std::cout << "nnPath: " << path << "nnArguments:nn";
while (*argv != 0) std::cout << *argv++ << "n";
return 0;
}
int main() {
{
// ----------------------------------------------------------------------
// Solution 1
// Initial example
char path[] = "path";
const char* const argv[] = { "arg1", "arg2", "arg3", 0 };
_execv(path, argv);
}
{
// ----------------------------------------------------------------------
// Solution 2
// Now, string, with command convert to a handmade argv array
std::string command{ "path arg1 arg2 arg3" };
// Put this into istringstream
std::istringstream iss(command);
// Split into substrings
std::vector parts(std::istream_iterator<std::string>(iss), {});
// create "argv" List. argv is of type " const char* "
std::unique_ptr<const char*[]> argv = std::make_unique<const char*[]>(parts.size());
// Fill argv array
size_t i = 1U;
for (; i < parts.size(); ++i) {
argv[i - 1] = parts[i].c_str();
}
argv[i - 1] = static_cast<char*>(0);
// Call execv
// Windows
_execv(parts[0].c_str(), argv.get());
// Linux / Posix
execv(parts[0].c_str(), const_cast<char* const*>(argv.get()));
}
{
// ----------------------------------------------------------------------
// Solution 3
// Transform string vector to vector of char*
std::string command{ "path arg1 arg2 arg3" };
// Put this into istringstream
std::istringstream iss(command);
// Split
std::vector parts(std::istream_iterator<std::string>(iss), {});
// Fill argv
std::vector<const char*> argv{};
std::transform(parts.begin(), parts.end(), std::back_inserter(argv), [](const std::string& s) { return s.c_str(); });
argv.push_back(static_cast<const char*>(0));
// Call execv
// Windows
_execv(argv[0], &argv[1]);
// Linux / Posix
execv(argv[0], const_cast<char* const*>(&argv[1]));
}
{
// ----------------------------------------------------------------------
// Solution 4
// Transform string vector to vector of char*. Get rid of istringstream
std::string command{ "path arg1 arg2 arg3" };
// Split
std::vector<std::string> parts(std::sregex_token_iterator(command.begin(), command.end(), re, -1), {});
// Fill argv
std::vector<const char*> argv{};
std::transform(parts.begin(), parts.end(), std::back_inserter(argv), [](const std::string& s) { return s.c_str(); });
argv.push_back(static_cast<const char*>(0));
// Call execv
// Windows
_execv(argv[0], &argv[1]);
// Linux / Posix
execv(argv[0], const_cast<char* const*>(&argv[1]));
}
return 0;
}
- C++ - 将元素按升序插入数组的 SEG 错误
- 为什么我在使用 istream 迭代器时会出现 seg 错误?
- 字符串上的合并排序上的 Seg 错误
- 导致 Seg 错误的 SingleLinkedList 的析构函数
- g++ 9.2.1 (Linux) 会导致 seg 错误,但 Windows 上的代码块不会
- 函数抛出seg错误,我不知道为什么
- Microsoft Visual Studio 2019交叉编译CMake库 - 有效,但存在SEG错误
- 由于相同的文件名/类名存在于不同的SO中而导致的SEG错误
- 尝试访问 3D 矢量中的元素时出现 Seg 错误
- 垫子对象数组太大会导致 Seg 错误
- 为什么push_back成矢量<矢量<int>>导致 seg 错误?
- 在向量中存储字符串存在 SEG 错误
- 更改本地类名时出现 seg 错误
- 使用 ->GetString( " ") 时出现 Seg 错误,它位于单独的类中
- 将字符数组转换为 std::string 以传递到 std::bitset seg 错误
- 字符和 int 连接何时会导致 seg 错误?
- 静态数组溢出堆栈(seg 错误 11)仅在类定义内部时,否则不会..?
- .EOF 在读取文件时导致 Seg 错误:FIXXED
- 在 qt5 中使用 QSharedPointer 时出现 Seg 错误
- 为什么我的 if 语句会出现 seg 错误?