我对 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::stringstd::vectordata 个成员是有原因的。使用它们。

"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!
  1. i 递增两次。假设 i 在这两行之前是 0。第一行为 argv_[0] 分配内存。 i 递增,其值变为 1。在第二行中,您尝试复制到 argv_[1] 并且 i 再次递增。这是一个问题,因为您还没有为 argv_[1] 分配内存。

    这会导致更多问题。在 for 循环的下一次迭代中,您访问 argv_[2]argv_[3],这使问题进一步复杂化,因为它们可能是 argv_.

    的无效索引

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

  2. 第一行分配不够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_;
};