【问题标题】:Function to dynamically allocate matrix动态分配矩阵的函数
【发布时间】:2015-11-10 01:58:33
【问题描述】:

我想创建一个函数来分配(使用malloc/calloc)一个声明为双指针的矩阵。我了解双指针矩阵如何工作以及如何使用malloc 分配它,但是当我传递我的矩阵(在main() 中声明并初始化为NULL)时,我的程序崩溃了。我想错误出在我的allocMatrix() 函数上,因为如果我在main 中分配矩阵,一切都会顺利进行。谢谢:-)

主要:

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

#define ROW 5
#define COL 5

int main(void) {

    int i,j, ret;
    int nRow, nCol;
    int **mat=NULL; // double pointer matrix

    nRow = 0;
    nCol = 0;

    //Insert n of row and columns
    printf("Insert n of rows and columns:\n");
    scanf("%d %d", &nRow, &nCol);

    //Functions to allocate matrix
    ret=allocMatrix(mat, nRow, nCol);
    printf("Return value: %d\n",ret);

    /*
    this code works perfect!
    mat= malloc(nRow * sizeof(int));
    i=0;
    while( i < nRow)
    {
        mat[i]=malloc(nCol * sizeof(int));
        i++;
    }
    */

    //Get Values from stdin
    i=0;
    while( i < nRow)
    {
        j=0;
        while (j < nCol)
        {
            printf("Insert value pos[%d,%d]:\n", i, j);
            scanf("%d", &mat[i][j]);
            j++;
        }
        i++;
    }

    //Print values
    i=0;
    while (i < nRow)
    {
        j=0;
        while( j < nCol)
        {
            printf("Value pos[%d,%d] is: %d \n", i, j, mat[i][j]);
            j++;
        }
        i++;
    }


    system("pause");
    return EXIT_SUCCESS;
}

allocateMatrix函数:

int allocMatrix(int **matrix, int nRow, int nCol)
{
    int i;
    int ext_status;

    //Classic allocation method for matrix
    matrix= malloc( nRow * sizeof(int));

    if ( matrix != NULL)
    {
        i=0;
        while (i < nRow)
        {
            matrix[i]= malloc(nCol * sizeof(int));

            if( matrix[i] != NULL)
                ext_status= 1;
            else
                ext_status= 0;
            i++;
        }
    }
    else
        ext_status= 0;

    return ext_status;
 }

【问题讨论】:

    标签: c pointers matrix dynamic malloc


    【解决方案1】:

    永远不要使用指针到指针来分配多维数组。这是广泛传播但不好和不正确的做法。这样做不会给你一个真正的二维数组,而且由于堆碎片会导致代码变慢。它还使代码更难编写、读取和维护,从而增加了内存泄漏的可能性。

    相反,在相邻的内存单元中正确分配二维数组,如下所示:

    int x;
    int y;
    
    // store some values in x and y here
    
    int(*matrix)[y] = malloc (sizeof(int[x][y]));
    
    if(matrix == NULL)
    {
      // error handling here
    }
    
    matrix[i][j] = something; // do something with the matrix
    
    free(matrix);
    

    如果你坚持将这段代码保存在一个函数中,那么它会是:

    void* allocMatrix (int nRow, int nCol)
    {
      return malloc (sizeof(int[nRow][nCol]));
    }
    
    int(*matrix)[y] = allocMatrix(x, y);
    

    编辑:代码和数组指针的解释。

    int(*matrix)[y] = malloc (sizeof(int[x][y])); 行中,sizeof(int[x][y]) 非常简单,它只是一个二维整数数组的大小,维度为 x*y。它使用了 C99 标准中变长数组的概念,允许在运行时指定数组维度。

    数组指针 在 C 中是一种特殊的类型,它能够指向整个数组,而不是像普通指针那样只指向数组的第一项。数组指针与常规指针不同,它知道数组的大小。

    一个数组指针写成type(*name)[size],例如一个指向5个整数数组的数组指针将写成int(*arr_ptr)[5] = &amp;the_array;

    访问指向的内容时,数组指针的行为与任何指针一样,您可以使用* 访问它的内容。所以*arr_ptr 给出了指向的数组,(*arr_ptr)[0] 给出了该数组的第一项。

    对于多维数组,同样的规则适用。给定数组int arr[x][y],指向此类型的数组指针将是int(*arr_ptr)[x][y] = &amp;arr;。访问内容*arr_ptr 会给你一个二维数组,它相当于一个数组数组。 (*arr_ptr)[0] 因此将给出数组数组中的第一个数组。在表达式中使用任何数组名称时,通常的规则是它“衰减”为指向第一个元素的指针。同样适用于此处,因此(*arr_ptr)[0] 也将与指向第一个数组中第一个元素的指针相同。而(*arr_ptr)[0][0] 将给出第一个数组的第一个元素。

    现在(*arr_ptr)[0][0] 这个语法看起来有点难读;为了得到一个二维数组的第一项,我们习惯于写arr[0][0]。因此,在声明数组指针时,有一个方便的技巧。我们没有声明完整且正确的数组指针:int(*matrix)[x][y],一个指向维度为 x*y 的二维数组的数组指针,而是将其声明为int(*matrix)[y],它是一个指向维度为 y 的一维数组的数组指针。它将指向二维数组中的第一项,这是一个大小为 y 的一维数组。我们知道二维数组包含 x 个这样的项目。

    由于这个技巧,我们现在可以使用与访问二维数组时相同语法的数组指针,即matrix[i][j],而不是难以阅读的(*matrix)[i][j]

    【讨论】:

    • 我已经阅读过关于连续内存块的问题,我完全同意你的看法,但我是一名学生,所以我必须遵循我教授的指导方针(**矩阵)。顺便说一句,我对您的解决方案感兴趣,原因是最正确的。那么你能更好地解释一下语法到底是什么意思吗?这种方法的详细工作原理是什么?我在说这个:int(*matrix)[y] = malloc (sizeof(int[x][y]));
    • @Lundin 如何使用 sizeof 运算符获取 vla 的大小,我尝试了一些排列,例如 sizeof(*a)sizeof(*a[x]) 等,但结果并非如此。鉴于我们只从内存中分配了一个“连续”块,而没有其他任何东西,内存应该是sizeof(int) * x * y
    【解决方案2】:

    double 写在哪里,您可以根据自己的矩阵类型进行更改

    void allocMatrix(double ***matrix, int row, int col){
        int i = 0;
        *matrix = (double **)malloc(sizeof(double *) * row);
        for(i = 0; i < col; i++){
            *(*matrix + i) = (double *)malloc(sizeof(double) * col);
        }
    }
    
    void deallocMatrix(double **matrix, int row){
        int i = 0;
        for(i = 0; i < row; i++){
            free(matrix[i]);
        }
        free(matrix);
    }
    

    在 main 中你可以像这样使用它们

    int main(){
        int i = 0, j = 0, k = 0, row = 3, col = 4;
        double **myMatrix = NULL;
        allocMatrix(&myMatrix, row, col);
        for(i = 0; i < row; i++){
            for(j = 0; j < col; j++){
                myMatrix[i][j] = k++;
                printf("%.0lf\t", myMatrix[i][j]);
            }
            printf("\n");
        }
        deallocMatrix(myMatrix, row);
        return 0;
    }
    

    输出

    1    2    3    4
    5    6    7    8
    9    10   11   12
    

    这可以避免内存泄漏,但正如Lundin 所说,最好不要使用二维数组

    永远不要使用指针到指针来分配多维数组。这是广泛传播但不好和不正确的做法。这样做不会给你一个真正的二维数组,而且由于堆碎片会导致代码变慢。它还使代码更难编写、读取和维护,从而增加了内存泄漏的可能性。 相反,在相邻的内存单元中正确分配二维数组

    []

    【讨论】:

      【解决方案3】:

      这里的问题是,您将mat 本身传递给分配器函数,它不能保持返回时从函数分配的内存C 使用按值传递函数参数传递。

      您需要传递一个指向mat 的指针并进行相应的分配。

      【讨论】:

        【解决方案4】:

        您将矩阵按值传递给allocMatrix,而它应该按引用传递。

        将您的函数定义为int allocMatrix(int ***matrix, int nRow, int nCol) 并将matrix 替换为*matrix(顺便说一句,请注意索引的语法,它必须是(*matrix)[i],而不是*matrix[i])。

        这意味着你将把这个函数称为allocMatrix(&amp;mat, nRow, nCol)

        【讨论】:

        • 避免碎片化的查找表和一般的三星级编程。最好分配一个真正的二维数组。
        • @Lundin - 一如既往,这取决于情况。一个连续的块很可能会带来更好的性能,但是您的堆可能会碎片化到您没有足够大的单个块的程度。请记住,VLA 现在是可选的,因此未来的所有实现可能都不支持 VLA 语法。
        • 我怀疑这是我的问题...但是当我将矩阵传递给另一个函数时,它用一个简单的 scanf() o 随机值填充它 rand(),一切都很好(函数返回时填充矩阵)。那么这是同样的情况,还是分配功能发生了变化?因为我现在不明白!谢谢
        • 是的,只要传递一个int **,用值填充它应该可以正常工作,因为您正在分配给引用的int 元素。但是,如果您希望对双指针本身的更改在函数外部产生影响,则需要传递对它的引用。这就像当您想要一个函数对int 参数进行持久更改时,您必须传递一个int * 并通过在函数内部取消引用来设置int 值。将该原则提升一个层次,如果您想设置int * 的值(例如分配内存),您需要传递int **。如果你想设置一个int **...
        • @JohnBode 但是碎片的处理应该由堆分配例程而不是程序员来完成。 VLA 可能是可选的,但如果某些编译器决定不支持它们,则不会使用该编译器。从历史上看,编译器支持的无意义功能总是比语言标准提供的功能多十倍。为什么他们现在会突然停止这样做?特别是因为所有体面的编译器都已经实现了 VLA。
        猜你喜欢
        • 1970-01-01
        • 2021-10-13
        • 2023-03-03
        • 1970-01-01
        • 2014-03-25
        • 2021-12-19
        • 2010-11-27
        • 2015-10-14
        • 2011-07-07
        相关资源
        最近更新 更多