【问题标题】:Trying to pass 2D array of chars, but getting garbage试图传递二维字符数组,但得到垃圾
【发布时间】:2017-03-16 23:17:59
【问题描述】:

我正在尝试解析std::string,将其拆分,然后将其存储在二维字符数组中。该数组的第一行将包含总行数。 我在getC_strings() 函数内动态分配数组,当我打印它时,我得到了预期的结果。但是,当我再次从 main() 打印时,我得到了第 0、2 行的垃圾。我做错了什么?

#include <iostream>
#include <string>
#include <vector>
#include <boost/algorithm/string/classification.hpp> // Include boost::for is_any_of
#include <boost/algorithm/string/split.hpp>          // Include for boost::split

using namespace std;

/**
*
* @param input a string separated by spaces
* @param numArgs_ an int
* @param cargs a const char ** something.  Pass it by its address aka &something.
*/
static inline void getC_strings(const std::string & input, int & numArgs_, const char *** cargs) {

    std::vector<std::string> args;
    boost::split(args, input, boost::is_any_of(" "), boost::token_compress_on);
    numArgs_ = int(args.size());
    *cargs = new const char* [numArgs_ + 1];


    // store the number of rows at the first row
    (*cargs)[0] = new char[to_string(numArgs_).size()];
    (*cargs)[0] = to_string(numArgs_).c_str();

    // write the characters from the vector per row
    int ind = 0;
    for(auto const &v:args) {
        ind++;
        (*cargs)[ind] = new char [int(v.size())];
        if((*cargs)[ind] == NULL) std::cout << "OUT OF MEMORY! " << std::endl;
        (*cargs)[ind] = const_cast<char*>(v.c_str());
    }


    for(int i = 0; i < numArgs_; ++i) {
        std::cout << i << " " << (*cargs)[i] << std::endl;
    }

}


int main () {

    string arg = "test ./MyDirectoryName/OPQ_Arksoatn.txt 1 SOMETHING 1 2 3 4 5 6 7";
    int numCargs = 0;
    const char ** cargs;
    getC_strings(arg, numCargs, &cargs);

    cout << "  ==============================================" << endl;
    for(int i = 0; i < numCargs; ++i) {
        std::cout << i << " " << cargs[i] << std::endl;
    }

    return 0;
}

输出:

    0 11
    1 test
    2 ./MyDirectoryName/OPQ_Arksoatn.txt
    3 1
    4 SOMETHING
    5 1
    6 2
    7 3
    8 4
    9 5
    10 6
    ==============================================
    0 ��`
    1 test
    2 `��
    3 1
    4 SOMETHING
    5 1
    6 2
    7 3
    8 4
    9 5
    10 6

【问题讨论】:

  • 默认的new 运算符永远不会返回 null - 如果失败,它们会抛出异常。不要检查空结果。
  • 你为什么在循环的乞求时增加ind?要跳过数组的第一位吗?
  • 是的,在第一行我只想存储行数。
  • 您的函数存在内存泄漏。你为什么要使用new?为什么不使用std::vector
  • 我正在与期望 const char ** 作为输入参数的旧代码交互

标签: c++ arrays


【解决方案1】:

你可以试试这种不泄漏内存的方法。它是根据解决方案found here 制作的。

#include <iostream>
#include <string>
#include <vector>
#include <list>
#include <boost/algorithm/string/classification.hpp> // Include boost::for is_any_of
#include <boost/algorithm/string/split.hpp>          // Include for boost::split

using namespace std;

class CharStarWrapper
{
    private:
        typedef std::vector<char> CharArray;
        typedef std::list<CharArray> StringList;
        typedef std::vector<const char *> ArgList;
        const char** m_Args;
        StringList m_sList;
        ArgList m_cStrings;

    public:
        CharStarWrapper(const std::string & input) : m_Args(nullptr)
        {
            std::vector<std::string> args;
            boost::split(args, input, boost::is_any_of(" "), boost::token_compress_on);
            for (auto const &v : args)
            {
                // create an array of char and place on list
                m_sList.push_back(CharArray(v.begin(), v.end()));

                // null terminate this entry
                m_sList.back().push_back(0);

                // add the pointer to this entry to the vector of const char *.
                m_cStrings.push_back(&m_sList.back()[0]);
            }
            m_Args = m_cStrings.data();
        }

        const char** getArgs() { return m_Args;  }
        int getArgCount() const { return static_cast<int>(m_cStrings.size()); }
};

void fake_main(int argc, const char **argv)
{
   std::cout << "The number of arguments is " << argc << "\n";
   for (int i = 0; i < argc; ++i) 
        std::cout << argv[i] << "\n";
}

int main() {
    string arg = "test ./MyDirectoryName/OPQ_Arksoatn.txt 1 SOMETHING 1 2 3 4 5 6 7";
    CharStarWrapper wrapper(arg);
    fake_main(wrapper.getArgCount(), wrapper.getArgs());        
}

Live Example

基本上,我们将const char** 包装在一个类中。该类维护字符串的动态数组,并且仅提供public 成员函数来返回const char** 以及参数的数量。对“假”main() 函数的调用演示了其用法。

没有对new[]delete[]strdup 等的调用需要调用释放例程以避免内存泄漏。当包装器超出范围时,会自动清理所有内存。

请注意,此解决方案依赖于包装对象在您将使用const char ** 值的生命周期内不会超出范围。原因是CharStarWrapper维护了基础设施,使用时破坏基础设施是不正确的。

【讨论】:

    【解决方案2】:

    在几个地方:

    // store the number of rows at the first row
    (*cargs)[0] = new char[to_string(numArgs_).size()];
    (*cargs)[0] = to_string(numArgs_).c_str();
    

       // Similar code that allocates then ignores some space
       (*cargs)[ind] = const_cast<char*>(v.c_str());
    

    您正在携带指向 std::string 内部部分的指针。您需要将其复制到您的数组中,以便在您的 char 数组结构中使用它。 (见 strdup)。

    替换为:

    (*cargs)[0] = strdup(to_string(numArgs_).c_str());
    

       (*cargs)[ind] = strdup(v.c_str());
    

    【讨论】:

    • 顺便说一句,有没有可以实现的标准(和可移植)功能?
    • strdup 在 posix 中,或者您是在询问将整个结构放在一起吗?
    • @nikferrari “标准”方式是使用 C++ 并将其包装在维护基础设施的类中。我发布的另一个答案证明了这一点。
    • 绝对支持类包装器。
    猜你喜欢
    • 1970-01-01
    • 2016-12-31
    • 2022-11-14
    • 2016-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多