2013-04-07 69 views
0

經過一段時間和努力,我已經在我的代碼中追蹤了這個函數中的一個內存粉碎錯誤。我通過用堆棧分配數組的組合來替代兩個__block vector<int>變量以提供存儲和{klist|dlist}Ptr變量以允許塊內的代碼訪問數組(參見下面的推薦代碼)來停止內存粉碎。這使我相當確信這確實是使用__block vector<int>這是有問題的。爲什麼使用STL std :: vector作爲__block變量導致內存損壞?

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變量?

+0

你可以只顯示崩潰的代碼嗎?當您顯示其他代碼時,無法理解您所問的內容。你做'int * klistPtr = klist;'當'klist'是一個'vector '時會怎麼樣?在你的代碼中你的塊正在捕獲'klistPtr'和'dlistPtr',而不是'vector'。 – newacct 2013-04-08 05:47:27

+0

好點;我按照你的建議編輯了這個問題。 – 2013-04-08 11:59:04

回答

1

除非是拼寫錯誤,否則我看到dlist的初始化與數組不同:vector<int> dlist(1, depth);會生成長度爲1的矢量,而不是depth。這可能會導致出界。

通過使用dlist.at(currk)而不是dlist[currk],您可以隨時防止訪問向量元素,使其無法讀寫。

+0

啊哈,這確實是問題,謝謝! – 2013-04-08 22:31:57

0

允許C++對象作爲__block變量(儘管個人而言,如果您正在編寫C++,我會推薦lambdas; IMO沒有太多理由像這樣在純C++中使用塊)。

__block使用複製構造函數複製C++變量(請參見塊編程主題中的C++ Objects)。如果堆棧變量太深(這可能與你的「內存損壞」症狀相符),那麼你可能會因堆棧變量太多而導致堆棧溢出。

但是,我推薦使用lambdas而不是C++塊;有關更多討論,請參閱@sellibitze's answerHow do Clang 'blocks' work?

+0

算法不遞歸;它迭代地下降樹,本質上使用'klist'和'dlist'作爲遞歸算法在堆棧上需要的存儲。您關於** C++ ** lambdas的觀點是一個很好的觀點,但我仍然想知道使用塊在代碼中發生了什麼。 (你不能通過GCD發送一個** C++ ** lambda;我沒有在這個算法中這樣做,但是我會在其他代碼中使用這個習慣用法。) – 2013-04-08 12:04:58

相關問題