我想创建5 * 5 2D Matrix。我通常使用以下内存分配方式:
int **M = malloc(5 * sizeof(int *));
for (i = 0; i < 5; i++)
{
M[i] = malloc(5 * sizeof(int));
}
在我阅读博客时,我发现了另一种方法:
int **M = malloc(5 * sizeof(int*));
M[0] = malloc((5*5) * sizeof(int));
我的问题是:两种方法有什么区别?哪一个效率更高?
答案 0 :(得分:5)
对于第二个代码,请注意您需要初始化其他数组成员才能使其正常工作:
for (int i = 1; i < 5; i++) {
M[i] = M[0] + i * 5;
}
所以在第二个代码中,数组成员(通过所有数组)是连续的。访问它们没有任何区别(例如,您仍然使用M[i][j]
语法访问它们)。它优于第一个代码,只需要两个malloc
调用,并且在评论中提到有利于缓存,这可以大大提高访问性能。
但是如果你打算动态分配大型数组,最好使用第一种方法,因为内存碎片(大的连续内存分配可能不可用或者会加剧内存碎片)。
这种动态分配数组数组的类似例子可以在c-faq中找到:http://c-faq.com/aryptr/dynmuldimary.html
答案 1 :(得分:5)
在看到ouah的答案并看到C FAQ中的示例后,我现在明白了第二种技术的来源,尽管我个人不会在可以帮助它的地方使用它。
您展示的第一种方法的主要问题是数组中的行不保证在内存中相邻; IOW,紧跟在M[0][4]
之后的对象不一定是M[1][0]
。如果从不同页面分配了两行,则可能会降低运行时性能。
第二种方法保证所有行都是连续分配的,但您必须手动指定M[1]
到M[4]
才能使正常M[i][j]
下标工作,如
for ( size_t i = 0; i < 5; i++ )
M[i] = M[i-1] + 5;
IMO与以下相比,这是一种笨拙的方法:
int (*M)[5] = malloc( sizeof *M * 5 );
这也保证了内存是连续分配的,M[i][j]
下标可以不需要任何进一步的工作。
但是,有一个缺点;在不支持可变长度数组的编译器上,必须在编译时知道数组大小。除非您的编译器支持VLA,否则您无法执行类似
的操作size_t cols;
...
int (*M)[cols] = malloc( sizeof *M * rows );
在这种情况下,M[0] = malloc( rows * cols * sizeof *M[0])
然后手动分配M[1]
到M[rows - 1]
将是一个合理的替代品。
答案 2 :(得分:4)
我希望我不会在这里遗漏一些东西,但是我试图回答这个问题&#34;有什么区别......&#34;。如果我完全偏离基础,原谅我,我会纠正我的答案,但这里是:
我试图弄清楚你的两个mallocs中发生了什么,所以我要说的是与我手工绘制的图片(手工制作的答案?)相关联。
第一个选项:
对于第一个选项,您将分配一个大小为5 int * s的内存块。 M,这是一个int **指向该内存块的开头。
然后,遍历每个内存块(int *的大小),并在每个块中放入大小为5 int的内存块的地址。请注意,它们位于内存的一些随机部分(堆)中,有足够的空间可以占用5个整数。
这是关键 - 它是一个不连续的内存块。因此,如果您将内存视为数组,则指向数组中的不同起始位置。
第二个选项
你的第二个int **的分配完全相同。但相反,它分配 25 整数的大小并返回将该数组的地址放在内存块M [0]中。注意:您从未在内存位置M [1] - M [4]中放置任何地址。
那么,会发生什么?你有一个25个整数的连续块,其地址可以在M [0]中找到。当你尝试获得M [1]时会发生什么?你猜对了 - 它是空的或包含垃圾值。更重要的是,它是一个不指向已分配内存空间的值,因此您可以使用Segfault。
答案 3 :(得分:3)
如果要在连续内存中分配5x5数组,正确的方法是
int rows = 5;
int cols = 5;
int (*M)[cols] = malloc(rows * sizeof(*M));
然后,您可以使用正常的数组索引访问该数组,例如
M[3][2] = 6;
答案 4 :(得分:1)
int **M = malloc(5 * sizeof(int *));
指的是为指针分配内存M[i] = malloc(5 * sizeof(int));
指的是为int变量分配内存。
也许这会帮助您了解正在发生的事情:
int **M = malloc(5 * sizeof(void *));
/* size of 'void *' and size of 'int *' are the same */
for (i = 0; i < 5; i++)
{
M[i] = malloc(5 * sizeof(int));
}
答案 5 :(得分:0)
使用malloc((5*5) * sizeof(int));
时的另一个小差异。当然是OP正在寻找的一个侧面问题,但仍然是一个问题。
以下两个与2个操作数的顺序相同仍然导致产品使用size_t
数学。
#define N 5
malloc(N * sizeof(int));
malloc(sizeof(int) * N);
考虑一下:
#define N some_large_value
malloc((N*N) * sizeof(int));
sizeof()
的结果类型是类型size_t
,无符号整数类型,肯定有SIZE_MAX >= INT_MAX
,可能远更大。所以要避免int
溢出不会溢出size_t
数学使用
malloc(sizeof(int) * N * N);