我遇到运行时错误,试图将std :: strcopy与std :: string向量中的元素一起使用。
矢量没有问题。我有更高级别的功能,可以毫无障碍地工作。我的低级函数char ** argv()
遇到了问题。
这是我正在写的课程的一大块。我想我已经发布了足够的问题。我正在努力将注意力集中在这个问题上。
在运行时,下面代码中指示的行会爆炸。
class ArgParser{
public:
... MORE CODE ...
int & argc()
{
argc_ = exePath_.empty() ? 0 : 1 + args_.size();
return argc_;
}
char ** argv()
{
const int argCount = argc();
if( argCount==0 ) return argv_;
if( argv_ )
{
for( int i=0; i < argCount; i++ )
delete argv_[i];
delete argv_;
}
argv_ = new char*[argCount];
*(argv_ + 0)=new char[ exePath().size() ];
strcpy( *(argv_ + 0), exePath_.c_str() );
int i=1;
for( auto &arg : args_ )
{
*(argv_ + i++)=new char[ arg.size() ];
strcpy( *(argv_ + i++), arg.c_str() ); // SEG FAULT!
}
return argv_;
}
private:
int argc_;
char **argv_;
std::vector <std::string> args_;
std::string exePath_;
};
答案 0 :(得分:2)
以下几行有几个问题。
*(argv_ + i++)=new char[ arg.size() ];
strcpy( *(argv_ + i++), arg.c_str() ); // SEG FAULT!
i
增加两次。假设i
在这两行之前为0。第一行为argv_[0]
分配内存。 i
递增,其值变为1
。在第二行,您尝试复制到argv_[1]
,i
再次递增。这是一个问题,因为你还没有为argv_[1]
分配内存。
这导致了进一步的问题。在for
循环的下一次迭代中,您可以访问argv_[2]
和argv_[3]
,这会进一步加剧问题,因为它们可能是argv_
的无效索引。
您可以通过在执行行后递增i
来解决此问题。作为编码惯例,最好避免在这些地方使用i++
和++i
。
第一行没有分配足够的空间。它还需要一个字符来保存终止空字符。
将这些行更改为:
argv_[i] = new char[arg.size() + 1];
strcpy(argv_[i], arg.c_str());
++i;
答案 1 :(得分:1)
您没有将argv_[1]
分配给argv_[argCount-1]
。 击>
您将i
递增两次,因此您仍然取消引用无效指针
您没有为终止'\ 0'分配足够的空间。
停止做你正在写的事情C.有std::string
和std::vector
成员data
的原因。使用它们。
“其他一些库...删除了参数”如果它在指针上调用delete
它没有new
,你应该停止使用它,因为那是等待发生的未定义行为。如果没有,并且每个实例调用argv()
一次,您可以让它在args_
已分配的数据中使用
答案 2 :(得分:1)
另一个答案已经解释了你的实施中的错误。
在这个答案中,我只想向您展示一种更简单的方法来实现同样的事情,而不会产生令人讨厌的(即任何)手动分配:
int argc_;
std::vector <std::string> args_;
std::string exePath_;
// New fields:
std::vector<std::string> argv_data_;
std::vector<char *> argv_;
char **argv()
{
argv_data_.clear();
argv_data_.push_back(exePath_);
argv_data_.insert(argv_data_.end(), args_.begin(), args_.end());
argv_.clear();
for (auto &it : argv_data_)
argv_.push_back(it.c_str());
argv_.push_back(0); // The standard `argv` is null-terminated, we should do it to.
return argv_.data();
}
这就是全部。没有new
,没有泄漏任何风险。
此代码仍然允许您的C api安全地修改argv[i]
和argv[i][j]
,就像它是argv
收到的普通main()
一样。
答案 3 :(得分:0)
这就是我的用途,它起作用了。这与HolyBlackCat发布的非常相似。
class ArgParser{
public:
...
int & argc()
{
argc_ = exePath_.empty() ? 0 : 1 + args_.size();
return argc_;
}
char ** argv()
{
if( argc()==0 ) return 0;
argv_.clear();
argvData_.clear();
argvData_.push_back( exePath_ );
for( auto arg : args_ )
argvData_.push_back( arg );
for( auto & arg : argvData_ )
argv_.push_back( &arg.front() );
return argv_.data();
}
private:
std::string exePath_;
std::vector <std::string> args_;
int argc_;
std::vector <std::string> argvData_;
std::vector <char*> argv_;
};