我的std :: strcopy实现有什么问题? (分段故障)

时间:2018-02-02 16:46:23

标签: c++ string c++11 vector strcpy

我遇到运行时错误,试图将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_;
};

4 个答案:

答案 0 :(得分:2)

以下几行有几个问题。

*(argv_ + i++)=new char[ arg.size() ];
strcpy( *(argv_ + i++), arg.c_str() ); // SEG FAULT!
  1. i增加两次。假设i在这两行之前为0。第一行为argv_[0]分配内存。 i递增,其值变为1。在第二行,您尝试复制到argv_[1]i再次递增。这是一个问题,因为你还没有为argv_[1]分配内存。

    这导致了进一步的问题。在for循环的下一次迭代中,您可以访问argv_[2]argv_[3],这会进一步加剧问题,因为它们可能是argv_的无效索引。

    您可以通过在执行行后递增i来解决此问题。作为编码惯例,最好避免在这些地方使用i++++i

  2. 第一行没有分配足够的空间。它还需要一个字符来保存终止空字符。

  3. 将这些行更改为:

    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::stringstd::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_;
};