为啥使用 STL std::vector 作为 __block 变量会导致内存损坏?

Posted

技术标签:

【中文标题】为啥使用 STL std::vector 作为 __block 变量会导致内存损坏?【英文标题】:Why does using an STL std::vector as a __block variable cause memory corruption?为什么使用 STL std::vector 作为 __block 变量会导致内存损坏? 【发布时间】:2013-04-07 21:42:51 【问题描述】:

经过一些时间和努力,我在我的代码中找到了这个函数的内存破坏错误。我通过将两个 __block vector<int> 变量替换为堆栈分配数组的组合来提供存储和一个 klist|dlistPtr 变量以允许块内的代码访问数组,从而停止了内存粉碎(见下面的推荐代码)。这让我相当有信心,确实是使用__block vector<int> 有问题。

void
traceTree(Matrix<double> Z, double s[3], int k, unsigned int depth)

    int m = Z.size(1) + 1;
    __block vector<int> klist(m, 0);
    // int klist[m]; int * klistPtr = klist;
    // klist[0] = k;
    __block vector<int> dlist(1, depth);
    // int dlist[depth]; int * dlistPtr = dlist;
    // dlist[0] = depth;
    __block int topk = 0;
    int currk = 0;

    void (^ subtree)(int i) = ^(int i) 
        if (i > m)                 // If it's not a leaf...
            topk += 1;
            klist[topk] = i - m;
            dlist[topk] = depth - 1;
        
    ;

    while (currk <= topk) 
        k = klist[currk];
        depth = dlist[currk];
        s[0] += Z[2,k];            // Sum of the edge lengths so far
        s[1] += Z[2,k] * Z[2,k]; // ... and the sum of the squares
        s[2] += 1;                   // ... and the count of the edges
        if (depth > 0) 
            subtree(Z[0,k]);       // Consider left subtree
            subtree(Z[1,k]);       // Consider right subtree
        
        currk += 1;
    

[我要指出,这是一个纯粹的迭代算法;没有递归。该块的存在只是为了避免重复处理左右子树所需的代码。]

显而易见的问题是,为什么 STL vector 对象会导致内存损坏?他们甚至没有进行任何动态调整大小……难道不支持将 C++ 对象用作__block 变量吗?

【问题讨论】:

你能只显示崩溃的代码吗?当您显示其他代码时,无法理解您在问什么。你做int * klistPtr = klist;klistvector&lt;int&gt; 时,这将如何工作?在您的代码中,您的块正在捕获 klistPtrdlistPtr,而不是 vector 好点;我已按照您的建议编辑了问题。 【参考方案1】:

除非是拼写错误,否则我看到您对 dlist 的初始化与数组不同:vector&lt;int&gt; dlist(1, depth); 生成长度为 1 的向量,而不是 depth。这可能会导致超出范围。

您始终可以通过使用dlist.at(currk) 而不是dlist[currk] 来防止越界访问矢量元素,用于读取和写入。

【讨论】:

【参考方案2】:

C++ 对象被允许作为__block 变量(虽然我个人推荐使用 lambdas,如果你正在编写 C++;IMO 没有太多理由像这样在纯 C++ 中使用块)。

__block C++ 变量使用复制构造函数进行复制(参见块编程主题中的C++ Objects)。如果这很深(这与您的“内存损坏”症状相匹配),您可能会因为大型堆栈变量的副本过多而导致堆栈溢出。

但我还是推荐 lambdas 而不是 C++ 的块;更多讨论请参见@sellibitze's answerHow do Clang 'blocks' work?。

【讨论】:

算法不递归;它迭代地下降树,本质上使用klistdlist 作为递归算法在堆栈上所需的存储。您关于 C++ lambdas 的观点很好,但我仍然想知道使用块的代码中发生了什么。 (您不能通过 GCD 调度 C++ lambda;我在这个算法中没有这样做,但我会在其他代码中大量使用该习语。)

以上是关于为啥使用 STL std::vector 作为 __block 变量会导致内存损坏?的主要内容,如果未能解决你的问题,请参考以下文章

为啥在 std::vector 擦除中需要 begin()?

为啥不能对 std::vector 使用前向声明?

C++ STL应用与实现2: 如何使用std::vector

C++ STL应用与实现2: 如何使用std::vector

C++ STL应用与实现2: 如何使用std::vector

STL 容器类型作为模板参数