簡體   English   中英

c ++ 11使用std :: fill來填充2D動態分配的數組

[英]c++11 using std::fill to fill a 2D dynamically allocated array

沿着這條線的討論可以在這個問題中找到,也可以在這里找到 ,但我的情況略有不同,因為我正在處理一個動態分配的內存。

還請注意, memset不太適用於double價值。

無論如何,我試圖使用std::fill來填充動態分配的2D數組 -

#include <iostream>
#include <algorithm>

using std::cout ; using std::endl ;
using std::fill ;

int main()
{
    double **data ;
    int row = 10, col = 10 ;
    data = new double*[row] ;
    for(int i = 0 ; i < col ; i++)
        data[i] = new double[col];

    // fill(&(data[0][0]), 
    //      &(data[0][0]) + (row * col * sizeof(double)), 1); // approach 1
    // fill(&(data[0][0]), &(data[0][0]) + (row * col), 1);   // approach 2
    // fill(data, data + (row * col * sizeof(double)), 1);    // approach 3
    // fill(&(data[0][0]), 
    //      &(data[0][0]) + 
    //       ((row * sizeof(double*)) + 
    //        (col * sizeof(double))), 1);                    // approach 4

    for(int i = 0 ; i < row ; i++) {
        for(int j = 0 ; j < col ; j++)
            cout << data[i][j] << " " ;
        cout << endl ;
    }

    for(int i = 0 ; i < row ; i++)
        delete [] data[i] ;
    delete [] data ;
}

方法1:我理解,方法1應該是正確的代碼,我從頭開始&(data[0][0])並且數組的末尾應該位於&(data[0][0]) + (row * col * sizeof(double)) ,但是當我運行時,我收到此錯誤,但數組已被填充 -

1 1 1 1 1 1 1 1 1 1 
1 1 1 1 1 1 1 1 1 1 
1 1 1 1 1 1 1 1 1 1 
1 1 1 1 1 1 1 1 1 1 
1 1 1 1 1 1 1 1 1 1 
1 1 1 1 1 1 1 1 1 1 
1 1 1 1 1 1 1 1 1 1 
1 1 1 1 1 1 1 1 1 1 
1 1 1 1 1 1 1 1 1 1 
1 1 1 1 1 1 1 1 1 1 
*** Error in `./test': free(): invalid next size (fast): 0x0000000000da3070 ***
Aborted (core dumped)

Approrach 2:但是,根據這篇文章 ,推薦方法2,但我不太明白這個代碼,因為缺少sizeof(double) ,我得到了這個輸出 -

1 1 1 1 1 1 1 1 1 1 
1 1 1 1 1 1 1 1 1 1 
1 1 1 1 1 1 1 1 1 1 
1 1 1 1 1 1 1 1 1 1 
1 1 1 1 1 1 1 1 1 1 
1 1 1 1 1 1 1 1 1 1 
1 1 1 1 1 1 1 1 1 1 
1 1 1 1 1 1 1 1 1 1 
1 1 1 1 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
*** Error in `./test': free(): invalid next size (fast): 0x0000000000bf5070 ***
Aborted (core dumped)

方法3:我不確定為什么這不編譯, data&(data[0][0])應該具有相同的含義,對吧?

方法4:我不確定這是否正確。

  1. 我該怎么做 ?
  2. std::fill是否為兩個嵌套循環提供了額外的好處?

與堆棧分配的2D陣列不同,動態2D陣列不能保證是連續的范圍。 然而,它是一個連續的指針范圍,但是數組中的每個指針可能指向非連續的存儲區域。 換句話說, data + i + 1的第一個元素可能不一定跟隨data + i指向的數組的最后一個元素。 如果你想知道為什么堆棧分配的2D數組是連續的,那是因為當你聲明類似的東西時

double data[10][20];

然后編譯器將其理解為10個(連續)元素的數組,每個元素的類型為double[20] 后一種類型也是一個數組,它保證了連續的元素,因此double[20]元素(即20個double一個接一個)在存儲器中一個接一個地堆疊。 double[10][20]double**有驚人的不同。

這就是std::fillstd::memset讓你頭疼的原因,因為它們都假設一個連續的范圍。 因此,在您的情況下,嵌套循環似乎是填充數組的最簡單方法。

一般來說,使用一個“模擬”2D訪問的一維數組要好得多,完全出於上述原因:數據局部性。 數據位置意味着錯過的緩存更少,整體性能更好。

指針運算要求指針僅增加到結果仍指向同一數組(或結束一個數字)的程度。

您在for循環中將每一行分配為單獨的數組:

for(int i = 0 ; i < col ; i++)
    data[i] = new double[col]; // this creates a distinct array for each row

由於您分配的每個行數組都是col元素,因此可以合法地添加到&(data[0][0])的最大值是col 但是在每個std::fill用法示例中,您向指針添加的內容比允許的多。

鑒於您分配數組的方式,您無法將原始指針傳遞給std::fill的單個調用,以便初始化整個2D數組。 您必須使用多次調用std::fill (這違背了使用std::fill的目的),或者您必須創建一個知道如何處理每行的單獨分配的Iterator類型,或者您必須更改你分配數組的方式。

我建議將整個數組一次分配為一維數組,然后編寫一些額外的代碼,使其像二維數組一樣工作。 這有很多好處:

  • 標准庫包含一種動態分配單維數組的便捷方法: std::vector
  • 使用std::vector意味着您不再需要使用naked newdelete ,這可以修復代碼所具有的異常安全問題。
  • 單個分配通常具有比許多分配更好的性能特征(當然,在某些情況下,單獨的分配更好)。

這是一個簡單的包裝類,使1D數組看起來像2D數組:

class Matrix {
  std::vector<double> data;
  int cols;

public:
  Matrix(int row, int col)
    : data(row * col)
    , cols(col)
  {}

  auto begin() { return data.begin(); }
  auto end() { return data.end(); }

  struct index_type { int row; int col; };

  double &operator[](index_type i) {
    return data[i.row * cols + i.col];
  }

  int row_count() const { return data.size()/cols; }
  int column_count() const { return cols; }
};

使用此功能,您可以重寫代碼:

#include "Matrix.h"

#include <iostream>
#include <algorithm>

using std::cout ; using std::endl ;
using std::fill ;

int main()
{
    Matrix data(10, 10);

    fill(data.begin(), data.end(), 1.0);

    for(int i = 0 ; i < data.row_count() ; i++) {
        for(int j = 0 ; j < data.column_count() ; j++)
            cout << data[{i, j}] << " " ;
        cout << endl ;
    }
}

std::fill是否為兩個嵌套循環提供了額外的好處?

使用循環的可讀性較差,因為循環可以執行許多其他操作,並且您必須花費更多時間來確定任何特定循環正在執行的操作。 出於這個原因,人們應該總是更喜歡使用STL算法而不是手動循環,其他條件相同。


// fill(&(data[0][0]), 
//      &(data[0][0]) + (row * col * sizeof(double)), 1); // approach 1

指針算術自動考慮數組元素的大小。 你不需要sizeof(double) 這里乘以sizeof(double)與乘以[] sizeof(double)相同。 你不會這樣做: data[i * sizeof(double)] ,所以不要做data + (i * sizeof(double))


您的示例代碼使用&(data[0][0]) 想想這是否與data[0]相同或不同。 考慮表達式的類型和值。

我同意上述評論。 您已經分配了10個單獨的數組,因此無法使用單個std :: fill調用來初始化這些數組。 此外,當您對非void類型的指針執行算術運算時,編譯器會自動將結果乘以給定類型的sizeof。 但是,當您使用memset或memcpy等函數時,實際上您必須將給定類型的sizeof乘以元素數並將其傳遞給其中一個函數。 這是因為這些函數對字節進行操作,並且它們接受void類型的指針。 因此,編譯器無法調整大小,因為void類型沒有指定的大小。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM