我对 std::strcopy 的实施有什么问题? (分段故障)
What's wrong with my implementation of std::strcopy? (segmentation fault)
我在尝试对 std::string 向量中的元素使用 std::strcopy 时遇到 运行 时间错误。
向量没有问题。我有更高级别的功能,可以顺利运行。我 运行 我的低级函数 char ** argv()
遇到了问题。
这是我正在写的 class 的一部分。我想我已经为这个问题发布了足够多的内容。我正在努力将注意力集中在问题上。
在运行的时候,下面代码中的那一行炸毁了。
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_;
};
您没有将 argv_[1]
分配给 argv_[argCount-1]
。
你增加了 i
两次,所以你仍然解引用了一个无效的指针
您没有为终止的“\0”分配足够的 space。
别像写 C 那样做事了。std::string
和 std::vector
有 data
个成员是有原因的。使用它们。
"Some other library ... erases the arguments" 如果它调用 delete
而不是 new
,你应该停止使用它,因为这是等待发生的未定义行为。如果没有,并且 argv()
每个实例调用一次,您可以让它在 args_
分配的数据中播放
做事正确的方式™
其他答案已经解释了您实施中的错误。
在这个答案中,我只是想向您展示一种更简单的方法来实现同样的事情,而没有令人讨厌的(即任何)数量的手动分配:
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]
,就好像它是由 main()
接收的普通 argv
.
以下几行有几个问题。
*(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
。
第一行分配不够space。它需要多一个字符来保存终止空字符。
将这些行更改为:
argv_[i] = new char[arg.size() + 1];
strcpy(argv_[i], arg.c_str());
++i;
这是我使用的方法,效果很好。这与 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_;
};
我在尝试对 std::string 向量中的元素使用 std::strcopy 时遇到 运行 时间错误。
向量没有问题。我有更高级别的功能,可以顺利运行。我 运行 我的低级函数 char ** argv()
遇到了问题。
这是我正在写的 class 的一部分。我想我已经为这个问题发布了足够多的内容。我正在努力将注意力集中在问题上。
在运行的时候,下面代码中的那一行炸毁了。
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_;
};
您没有将 argv_[1]
分配给 argv_[argCount-1]
。
你增加了 i
两次,所以你仍然解引用了一个无效的指针
您没有为终止的“\0”分配足够的 space。
别像写 C 那样做事了。std::string
和 std::vector
有 data
个成员是有原因的。使用它们。
"Some other library ... erases the arguments" 如果它调用 delete
而不是 new
,你应该停止使用它,因为这是等待发生的未定义行为。如果没有,并且 argv()
每个实例调用一次,您可以让它在 args_
分配的数据中播放
做事正确的方式™
其他答案已经解释了您实施中的错误。
在这个答案中,我只是想向您展示一种更简单的方法来实现同样的事情,而没有令人讨厌的(即任何)数量的手动分配:
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]
,就好像它是由 main()
接收的普通 argv
.
以下几行有几个问题。
*(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
。第一行分配不够space。它需要多一个字符来保存终止空字符。
将这些行更改为:
argv_[i] = new char[arg.size() + 1];
strcpy(argv_[i], arg.c_str());
++i;
这是我使用的方法,效果很好。这与 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_;
};