![](/img/trans.png)
[英]Sorting a dynamically allocated, multidimensional C array with std::sort and lambda function in C++11
[英]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:我不確定這是否正確。
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::fill
或std::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 new
和delete
,這可以修復代碼所具有的異常安全問題。 這是一個簡單的包裝類,使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.