提问人:Cheatah 提问时间:9/22/2021 更新时间:9/22/2021 访问量:62
如何为多维数组分配内存?
How do I allocate memory for a multidimensional array?
问:
如何使用为多维数组分配内存?malloc
例如,您希望使用 .arr[6][9]
您可能已经尝试了以下方法:
// Warning: broken example
int **arr = malloc(50 * sizeof(int));
arr[6][9] = 42; // dangerous! Segmentation fault (core dumped)
这显然是错误的。但是,为多维数组分配(和释放)内存的正确方法是什么?
答:
最基本的多维数组当然是二维数组。
它有两个维度,在此示例中,我将使用 size by 的数组。
为简单起见,我使用整数类型来存储数据。存储类型与要使用的常规技术无关。x
y
为清楚起见,在前几个示例中跳过了任何错误检查。
后面的示例包括一些基本形式的错误检查。
该类型用于索引偏移量,以避免与存储在多维数组中的类型(整数)混淆。size_t
基本 2D 示例
/*
* Warning: no error checks!
*/
int **create_2d(size_t x, size_t y)
{
int *values = malloc(x * y * sizeof *values);
int **index_x = malloc(x * sizeof *index_x);
for (size_t i = 0; i < x; i++)
index_x[i] = &values[i * y];
return index_x;
}
您现在可以读取和写入 2D 数组中的所有位置,只要您不低于或超过,因为这将越界访问数组。0
x
y
int **arr = create_2d[20][24];
arr[6][9] = 42; // perfectly fine!
也许你对这段代码很满意,你把它复制/粘贴到你的项目中。 这完全没问题,但风险自负。我将提供进一步的解释和一些警告。
对这一切意味着什么的一些解释。最后,多维数组需要存储 类型的行和列。这意味着所需的存储大小至少是。
在此示例中,所有必需的存储都是一次性分配的。但是,由于这更容易维护,因此应该更改存储类型。这样就不容易出错了。x
y
int
x * y * sizeof(int)
sizeof(int)
sizeof *values
现在,所有内存都是“连续的”,并且可以作为 to 的偏移量进行访问。实际上,通过使用一些简单的算术,这通常已经可以用作仿二维数组。例如,您可以说索引已经可以通过 访问。第一个值是 row ,下一个值是 row ,以此类推。values[0]
values[x * y]
(i,j)
values[i * y + j];
y
0
y
1
为了真正通过索引访问它,实际上还需要分配该索引。在这种情况下,我称之为.它必须能够指向不同的内存位置,特别是每个“行”的“第一个”值。[i][j]
index_x
x
y
很多时候,你会看到人们在循环中执行分配。这实际上没有必要,并且在错误检查和解除分配方面使事情变得更加复杂。尽管如此,为 -rows 开头分配内存位置需要在循环中完成,我将其用作迭代器值,范围从 到 。因为需要指向指针,所以我们把 的地址放在 .y
i
0
x
index_x
values[i * y]
index_x
需要注意的是,返回的也是,而不是.如果您确实需要访问 ,仍然可以通过 来完成。当我们需要释放内存时,这将很方便。index_x
values
values
index_x[0]
基本释放 2D 示例
以下函数将增加分配的内存:free
/*
* Warning: no error checks!
*/
void destroy_2d(int **ptr)
{
free(ptr[0]);
free(ptr);
}
正如你所看到的,这里不需要循环。
现在可能还不清楚为什么 with 比 use in the loop 更可取。一旦你开始添加错误检查代码,或者当你需要分配大量项目或有大量嵌套时,它应该变得很明显。同样的原理也适用于三维数组。为了清楚起见,让我演示一下 3D 阵列:malloc
基本 3D 示例
int ***create_3d(size_t x, size_t y, size_t z)
{
int *values = malloc(x * y * z * sizeof *values);
int **index_y = malloc(x * y * sizeof *index_y);
int ***index_x = malloc(x * sizeof *index_x);
for (size_t i = 0; i < x; i++) {
index_x[i] = &index_y[i * y];
for (size_t j = 0; j < y; j++) {
// remove ONE of the following two lines
index_x[i][j] = &values[(i * y + j) * z]; // or, alternatively:
index_y[i * y + j] = &values[(i * y + j) * z]; // this is exactly the same
}
}
return index_x;
}
void destroy_3d(int ***ptr)
{
free(ptr[0][0]);
free(ptr[0]);
free(ptr);
}
这是相同的原理,尽管算术更复杂一些。
让我通过添加非常基本的错误检查来向您展示为什么这很重要:
带错误检查的基本 3D 示例
int ***create_3d_e(size_t x, size_t y, size_t z)
{
int *values = malloc(x * y * z * sizeof *values);
if (!values)
return NULL;
int **index_y = malloc(x * y * sizeof *index_y);
if (!index_y) {
free(values);
return NULL;
}
int ***index_x = malloc(x * sizeof *index_x);
if (!index_x) {
free(index_y);
free(values);
return NULL;
}
for (size_t i = 0; i < x; i++) {
index_x[i] = &index_y[i * y];
for (size_t j = 0; j < y; j++) {
index_y[i * y + j] = &values[(i * y + j) * z];
}
}
return index_x;
}
或者,如果您更喜欢不同的代码样式:
int ***create_3d_g(size_t x, size_t y, size_t z)
{
int *values;
int **index_y;
int ***index_x;
size_t i, j;
values = malloc(x * y * z * sizeof *values);
if (!values)
goto err;
index_y = malloc(x * y * sizeof *index_y);
if (!index_y)
goto err_y;
index_x = malloc(x * sizeof *index_x);
if (!index_x)
goto err_x;
for (i = 0; i < x; i++) {
index_x[i] = &index_y[i * y];
for (j = 0; j < y; j++) {
index_y[i * y + j] = &values[(i * y + j) * z];
}
}
return index_x;
err_x:
free(index);
err_y:
free(values);
err:
return NULL;
}
然后是释放时的一些基本错误防止逻辑:
带错误检查的基本释放 3D 示例
void destroy_3d_e(int ***ptr)
{
if (ptr) {
if (ptr[0]) {
free(ptr[0][0]);
free(ptr[0]);
}
free(ptr);
}
}
这是不在循环中分配内存的另一个优点!在这种情况下,“destroy”函数还应该知道循环中的维度和所有分配。当某些分配在嵌套多维数组的循环中途失败时,增加了复杂性。使程序崩溃并不总是一种选择,您可能希望或需要释放内存以防止讨厌的错误。也就是说,释放“连续”内存比“loop-malloc”方法容易得多。我没有为此提供示例,因为我认为这没有帮助。如果其他人想单独回答,请这样做,并有适当的保留意见。free
作为读者的练习:尝试为三维数组实现它。在构建阵列的中途检查故障,并在不发生内存泄漏的情况下优雅地拆除所有内容。
HEAP SUMMARY:
in use at exit: 0 bytes in 0 blocks
total heap usage: 3 allocs, 3 frees, 96,481,600 bytes allocated
All heap blocks were freed -- no leaks are possible
ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
我希望将来要求这种方法的人会少得多。我希望这些例子能让你更好地理解多维数组的内部工作原理。
评论
int (*p)[y] = malloc(x * sizeof *p);
评论