如何为多维数组动态分配内存

Posted

技术标签:

【中文标题】如何为多维数组动态分配内存【英文标题】:How to allocate the memory dynamically for multi dimensional arrays 【发布时间】:2021-07-21 10:27:12 【问题描述】:

这里我使用一个指针来为其中的矩阵和值分配内存。

所以我声明了一个包含三个地址的指针:

int ***array ;

然后我成功为数组分配内存

array = (int***)malloc((sizeof(int) * 2));

这是我的代码!

void main(void)

    int matrices, rows, columns;
    int ***array;
    printf("\n\n HOW MANY MATRICES YOU TO CREATE ? : "); 
    scanf("%d",&matrices);
    array = (int***)malloc((sizeof(int) * matrices));
    printf("\n HOW MANY ROWS YOU TO CREATE ? : "); 
    scanf("%d",&rows);
    printf("\n HOW MANY COLUMNS YOU TO CREATE ? : "); 
    scanf("%d",&columns);
    for(int i = 1; i <= matrices; i++)
    
        printf("\n Enter %d - matrix! ",i);
        for(int j = 1; j <= columns; j++)
        
            for(int k = 1; k <= rows; k++)
            
                printf("\n Enter element [%d[%d] : ",j,k);
                scanf("%d",&array[i][j][k]);
            
        
    

    //printing two matrices elements!!!

    for(int l = 1; l <= matrices; l++)
    
        printf("\n MATRIX - %d !! \n",l);
        for(int m = 1; m <= columns; m++)
        
            for(int n = 1; n <= rows; n++)
            
                printf("%d\t",array[l][m][n]);
            
            printf("\n");
        
    

但是当我在这里尝试打印两个矩阵的元素时,只有第二个矩阵元素显示在两个矩阵的输出控制台上,并且两个矩阵中的第一个元素都显示为“0”。

例子:

输入:

第一个矩阵

     1       2        3
 
     4       5        6 

第二个矩阵

     9       8        7
 
     3       5        2 

输出:

第一个矩阵

     0       8        7
 
     3       5        2 

第二个矩阵

     0       8        7
 
     3       5        2 

我是本站新手,如有错误请评论!!

【问题讨论】:

复制:Correctly allocating multi-dimensional arrays 请注意,C 使用 0-indexed 数组。 for(int i = 1; i &lt;= matrices; i++) ,你应该这样做 for(int i = 0; i matrices; i++). 【参考方案1】:

你没有 SegFaulted 只是偶然的,因为指针的大小没有改变。因此,在您为 int* 分配的地方,您应该为 int** 分配的地方,您的分配大小不会受到影响(偶然发生...)

您通常希望避免成为三星级程序员,但有时,就像在这种情况下,这是必需的。在分配任何指针,或指针到指针,或者在这种情况下为指针到指针到指针的情况下,请理解不涉及任何“数组”。

当您声明int ***array; 时,您声明了一个指针。然后指针指向(保存地址)您分配的一个指针块(类型int**)。您为用户输入的int** 指针矩阵分配存储空间。

每个矩阵都是int** 类型,因此您必须为每个矩阵分配一个包含rows 指针数的内存块。

最后,您为每个矩阵中的每一行分配colsint(类型int*)。

所以你的矩阵集合是一个分配的指针块,每个矩阵都有一个指针。然后每个矩阵是一个指针分配块,该矩阵中的每一行都有一个指针。最后,您为每个矩阵分配一个价值为int 的列。

从视觉上看,您的分配和分配将类似于以下内容:

          array  (int***)
            |
            +  allocate matricies number of [Pointers]
            |
        +----------+
        | array[0] |        allocate rows number of [Pointers] for each matrix
        +----------+        assign to each pointer in array block
        | array[1] |
        +----------+            array[2] (int**)
        | array[2] |  <=======  +-------------+
        +----------+            | array[2][0] |
        |   ....   |            +-------------+     allocate cols no. of [int]
                                | array[2][1] |     for each allocated row pointer
                                +-------------+
                                | array[2][2] |  <===  array[2][2] (int*)
                                +-------------+        +----------------+
                                |     ...     |        | array[2][2][0] |
                                                       +----------------+
                                                       | array[2][2][1] |
                                                       +----------------+
                                                       | array[2][2][2] |
                                                       +----------------+
                                                       |       ...      |

为了始终保持每次分配的 type-size 正确,只需使用 dereferenced 指针 来设置 type-size。例如,在分配 array (int***) 时,您将使用:

    array = malloc (matrix * sizeof *array);            /* allocate matrix int** */

为每个array[i] 分配时,您将使用:

        array[i] = malloc (rows * sizeof *array[i]);    /* array[i] int** pointers */

最后,当为每一行分配int 的每个块时,每个array[i][j],你会使用:

            array[i][row] = malloc (cols * sizeof *array[i][row]);

如果你总是使用解引用指针来设置类型大小,你永远不会弄错。

按照上图并依次获取每个分配(并验证每个分配),您可以编写类似于以下内容的分配和释放例程:

    /* use dereferenced pointer for type-size */
    array = malloc (matrix * sizeof *array);            /* allocate matrix int** */
    if (!array)                                        /* validate EVERY allocation */
        perror ("malloc-array");
        return 1;
    
    
    for (int i = 0; i < matrix; i++) 
        array[i] = malloc (rows * sizeof *array[i]);    /* array[i] int** pointers */
        if (!array[i])                                 /* validate */
            perror ("malloc-array[i]");
            return 1;
        
        for (int row = 0; row < rows; row++) 
            /* allocate cols int per-row in each matrix */
            array[i][row] = malloc (cols * sizeof *array[i][row]);
            if (!array[i][row]) 
                perror ("malloc-array[i][row]");
                return 1;
            
        
    

使用用户输入的行数和列数分配矩阵数的完整示例是:

#include <stdio.h>
#include <stdlib.h>

int main (void) 
    
    int ***array = NULL,
        matrix,
        rows,
        cols;
    
    fputs ("no. of matricies: ", stdout);
    if (scanf ("%d", &matrix) != 1)                     /* validate EVERY input */
        return 1;
    
    fputs ("no. of rows     : ", stdout);
    if (scanf ("%d", &rows) != 1)                       /* ditto */
        return 1;
    
    fputs ("no. of cols     : ", stdout);
    if (scanf ("%d", &cols) != 1)                       /* ditto */
        return 1;
    
    /* use dereferenced pointer for type-size */
    array = malloc (matrix * sizeof *array);            /* allocate matrix int** */
    if (!array)                                        /* validate EVERY allocation */
        perror ("malloc-array");
        return 1;
    
    
    for (int i = 0; i < matrix; i++) 
        array[i] = malloc (rows * sizeof *array[i]);    /* array[i] int** pointers */
        if (!array[i])                                 /* validate */
            perror ("malloc-array[i]");
            return 1;
        
        for (int row = 0; row < rows; row++) 
            /* allocate cols int per-row in each matrix */
            array[i][row] = malloc (cols * sizeof *array[i][row]);
            if (!array[i][row]) 
                perror ("malloc-array[i][row]");
                return 1;
            
        
    
    
    /* fill matricies with any values */
    for (int i = 0; i < matrix; i++)
        for (int j = 0; j < rows; j++)
            for (int k = 0; k < cols; k++)
                array[i][j][k] = j * cols + k + 1;
    
    /* display each matrix and free all memory */
    for (int i = 0; i < matrix; i++) 
        printf ("\nmatrix[%2d]:\n\n", i);
        for (int j = 0; j < rows; j++) 
            for (int k = 0; k < cols; k++)
                printf (" %2d", array[i][j][k]);
            putchar ('\n');
            free (array[i][j]);                 /* free row of int (int*) */
        
        free (array[i]);                        /* free matrix[i] pointers (int**) */
    
    free (array);                               /* free matricies pointers (int***) */

注意:在释放指向每个矩阵的指针块之前,先释放 int 的每个块的内存,然后再释放每个矩阵中的行指针块的内存)

使用/输出示例

$ ./bin/allocate_p2p2p
no. of matricies: 4
no. of rows     : 4
no. of cols     : 5

matrix[ 0]:

  1  2  3  4  5
  6  7  8  9 10
 11 12 13 14 15
 16 17 18 19 20

matrix[ 1]:

  1  2  3  4  5
  6  7  8  9 10
 11 12 13 14 15
 16 17 18 19 20

matrix[ 2]:

  1  2  3  4  5
  6  7  8  9 10
 11 12 13 14 15
 16 17 18 19 20

matrix[ 3]:

  1  2  3  4  5
  6  7  8  9 10
 11 12 13 14 15
 16 17 18 19 20

内存使用/错误检查

在您编写的任何动态分配内存的代码中,对于分配的任何内存块,您都有 2 个职责:(1)始终保留指向起始地址的指针内存块,因此 (2) 当不再需要它时可以释放

您必须使用内存错误检查程序来确保您不会尝试访问内存或写入超出/超出分配块的边界,尝试读取或基于未初始化的值进行条件跳转,最后, 以确认您已释放所有已分配的内存。

对于 Linux,valgrind 是正常的选择。每个平台都有类似的内存检查器。它们都易于使用,只需通过它运行您的程序即可。

$ valgrind ./bin/allocate_p2p2p
==9367== Memcheck, a memory error detector
==9367== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==9367== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==9367== Command: ./bin/allocate_p2p2p
==9367==
no. of matricies: 4
no. of rows     : 4
no. of cols     : 5

matrix[ 0]:

  1  2  3  4  5
  6  7  8  9 10
 11 12 13 14 15
 16 17 18 19 20

matrix[ 1]:

  1  2  3  4  5
  6  7  8  9 10
 11 12 13 14 15
 16 17 18 19 20

matrix[ 2]:

  1  2  3  4  5
  6  7  8  9 10
 11 12 13 14 15
 16 17 18 19 20

matrix[ 3]:

  1  2  3  4  5
  6  7  8  9 10
 11 12 13 14 15
 16 17 18 19 20
==9367==
==9367== HEAP SUMMARY:
==9367==     in use at exit: 0 bytes in 0 blocks
==9367==   total heap usage: 23 allocs, 23 frees, 2,528 bytes allocated
==9367==
==9367== All heap blocks were freed -- no leaks are possible
==9367==
==9367== For counts of detected and suppressed errors, rerun with: -v
==9367== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

始终确认您已释放已分配的所有内存并且没有内存错误。

检查一下,如果您还有其他问题,请告诉我。

【讨论】:

现在我明白了在处理多维时如何使用指向数组的指针。谢谢您的解释,先生! 不客气。祝你编码好运!【参考方案2】:

只需将可变长度数组 (VLA) 与动态存储一起使用。

int (*array)[rows + 1][cols + 1] = malloc(sizeof(int[matrices + 1][rows + 1][cols + 1]));

使用 VLA 更简单,性能更高。

向每个维度添加 1 可让您从索引 1 开始寻址数组,并防止程序在访问元素 array[matrixes][rows][cols] 时出现未定义行为 (UB)。

但是,这是 BAD 的做法,因为 C 中的数组是从 0 开始索引的。其他方法会使您的代码的其他用户感到困惑。 因此,我强烈建议您从 0 开始索引数组并删除所有“+ 1”。

所以正确的分配代码应该是:

int (*array)[rows][cols] = malloc(sizeof(int[matrices][rows][cols]));

并更新所有循环以形成:

for(i = 0;  i < matrices ; i++)

最后,当数组不再使用时释放它。

free(array)

【讨论】:

是的,看起来不错 :) 也是使用正确分配的唯一答案。【参考方案3】:

由于您使用的是指向指针的指针。您需要在所有阶段动态分配内存。在您询问矩阵数量之后的第一阶段。应该是,

array = (int***)malloc (sizeof(int**) * matrices);

因为您分配的矩阵是int**。然后在询问行数后,为每个矩阵分配它,

for(i=1 ;  i <= matrices ; i++)
     array[i-1] = (int**)malloc (sizeof(int*)*ROWS);

最后你需要为每一行分配内存。所以,

for(i=1 ;  i <= matrices ; i++)
     for(j=1 ; j <= ROWS ; j++)
           array[i-1][j-1] = (int*)malloc (sizeof(int)*COLS);

在此之后,您可以像以前那样在闲暇时接受输入。试试这个,它应该工作。如果没有,应该还有其他问题。

【讨论】:

【参考方案4】:

在C语言中,避免

pointer = (some_type *) malloc(n * sizeof(some_type));  // Avoid

不是分配给类型,而是分配给引用的对象和drop the unneeded cast。首先形成最宽类型的尺寸计算。 sizeof 运算符返回 size_t 类型。

pointer = malloc(sizeof *pointer * n);  // Best

这更容易正确编码(OP 的 (sizeof(int) * matrices) 不正确且太小)、检查和维护。


强大的代码检查分配错误。

if (pointer == NULL) 
  fprintf(stderr, "Out of memory\n");  // Sample error code, adjust to your code's need
  exit(-1);


为矩阵数据分配内存,这是 OP 的代码没有做的。

array = malloc(sizeof *array * matrices);
// Error checking omitting for brevity, should be after each malloc()

// Use zero base indexing
// for(int i = 1; i <= matrices; i++) 
for (int m = 0; m < matrices; m++) 
  array[m] = malloc(sizeof *array[m] * rows);
  for (int r = 0; r < rows; r++) 
    array[m][r] = malloc(sizeof *array[m][r] * columns);
  


// Now read in data
// Use data

// Free when done
for (int m = 0; m < matrices; m++) 
  for (int r = 0; r < rows; r++) 
    free(array[m][r]);
  
  free(array[m]);

free(array);

size_t 用于数组维度的类型比int 更好,但int 将用于小程序。

【讨论】:

以上是关于如何为多维数组动态分配内存的主要内容,如果未能解决你的问题,请参考以下文章

多维数组动态内存分配背后的算法是啥?

内存堆问题 C++,动态分配多维数组

“动态分配的内存模拟多维数组”的正确术语?

C# 如何为 List<T> 动态分配内存?

如何为二维数组分配和释放堆内存?

正确使用 memset 与动态分配的多维数组