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

时间:2013-04-07 21:42:51

标签: memory-management vector block objective-c++ vmat

经过一段时间和精力后,我在我的代码中找到了一个内存粉碎错误的函数。我通过使用堆栈分配的数组的组合替换两个__block vector<int>变量以提供存储和{klist|dlist}Ptr变量以允许块内的代码访问数组来停止内存粉碎(在推荐的数组中看到) - 下面的代码)。这使我相信确实使用__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变量?

2 个答案:

答案 0 :(得分:1)

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

您可以使用dlist.at(currk)代替dlist[currk]来防止访问向量元素超出范围,无论是阅读还是写作。

答案 1 :(得分:0)

C ++对象被允许作为__block变量(虽然如果你正在编写C ++,我个人会推荐lambdas; IMO在纯C ++中使用块的原因并不多见。)

__block使用复制构造函数复制C ++变量(请参阅块编程主题中的C++ Objects)。由于大量堆栈变量的副本太多而非常深(这与您的“内存损坏”症状相符),您可能会溢出堆栈。

但是,我再次推荐使用lambdas而不是C ++的块;请参阅@sellibitze's answer How do Clang 'blocks' work?进行更多讨论。