Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Function to dynamically allocate matrix

I want to create a function to allocate (with malloc/calloc) a matrix declared as a double pointer. I understood how a double pointer matrix works and how allocate it with malloc, but when I pass my matrix (declared in main() and initialized to NULL) my program crashes. I suppose that the error is with my allocMatrix() function because if I allocate the matrix in main all works smoothly. Thanks :-)

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 function:

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;
 }
like image 380
CSDude Avatar asked Dec 05 '22 20:12

CSDude


1 Answers

Never use pointer-to-pointer to allocate multi-dimensional arrays. It is wide-spread but bad and incorrect practice. Doing so will not give you a true 2D array, and it will lead to slower code because of heap fragmentation. It also makes the code harder to write, read and maintain, which in turn increases the potential for memory leaks.

Instead, allocate the 2D array correctly in adjacent memory cells, like this:

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);

If you insist on keeping this code in a function, then it would be:

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

int(*matrix)[y] = allocMatrix(x, y);

Edit: explanation of the code and array pointers.

In the line int(*matrix)[y] = malloc (sizeof(int[x][y]));, the sizeof(int[x][y]) is pretty straight-forward, it is just the size of a 2D array of ints with dimensions x*y. It is using the concept of variable-length arrays from the C99 standard, which allows array dimensions to be specified in runtime.

An array pointer is a somewhat special type in C, it is able to point to whole arrays, rather than just at the first item of the array, as a normal pointer will do. The array pointer, unlike a regular pointer, knows the size of the array.

An array pointer is written as type(*name)[size], so for example an array pointer to an array of 5 ints would be written as int(*arr_ptr)[5] = &the_array;.

When accessing the contents pointed at, an array pointer behaves just as any pointer, you access the contents of it with *. So *arr_ptr gives the array pointed at, and (*arr_ptr)[0] gives the first item of that array.

For multi-dimensional arrays, the same rules apply. Given the array int arr[x][y], an array pointer to this type will be int(*arr_ptr)[x][y] = &arr;. Accessing the contents *arr_ptr will give you a two-dimensional array, which is equivalent to an array of arrays. (*arr_ptr)[0] will therefore give the first array in the array of arrays. The usual rule for any array name when used in an expression, is that it "decays" into a pointer to the first element. Same applies here, so (*arr_ptr)[0] will therefore also be the same as a pointer to the first element in the first array. And (*arr_ptr)[0][0] will give the first element of the first array.

Now this syntax (*arr_ptr)[0][0] looks a bit hard to read; to get the first item of a 2D array, we are used at writing just arr[0][0]. So when declaring the array pointer, there is a handy trick. Instead of declaring the complete and correct array pointer: int(*matrix)[x][y], an array pointer to a 2D array of dimensions x*y, we instead declare it as int(*matrix)[y], which is an array pointer to a 1D array with dimension y. It will point at the first item in the 2D array, which is a 1D array of size y. We know that the 2D array contains x such items.

And because of this trick, we'll now be able to use the array pointer with the same syntax as when accessing a 2D array, namely matrix[i][j], rather than the hard-to-read (*matrix)[i][j].

like image 188
Lundin Avatar answered Dec 30 '22 00:12

Lundin