简体   繁体   中英

malloc-ating multidimensional array in function

I'm trying to allocate a 2d array in a C program. It works fine in the main function like this (as explained here ):

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

int main(int argc, char ** argv)
{
    int ** grid;
    int i, nrows=10, ncols=10;
    grid = malloc( sizeof(int *) * nrows);

    if (grid == NULL){
        printf("ERROR: out of memory\n");
        return 1;
    }

    for (i=0;i<nrows;i++){
        grid[i] = malloc( sizeof(int) * ncols);
        if (grid[i] == NULL){
            printf("ERROR: out of memory\n");
            return 1;
        }
    }
    printf("Allocated!\n");

    grid[5][6] = 15;
    printf("%d\n", grid[5][6]);
    return 0;
}

But since I have to do this several times with different arrays, I was trying to move the code into a separate function.

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

int malloc2d(int ** grid, int nrows, int ncols){
    int i;
    grid = malloc( sizeof(int *) * nrows);

    if (grid == NULL){
        printf("ERROR: out of memory\n");
        return 1;
    }

    for (i=0;i<nrows;i++){
        grid[i] = malloc( sizeof(int) * ncols);
        if (grid[i] == NULL){
            printf("ERROR: out of memory\n");
            return 1;
        }
    }
    printf("Allocated!\n");
    return 0;
}

int main(int argc, char ** argv)
{
    int ** grid;

    malloc2d(grid, 10, 10);
    grid[5][6] = 15;
    printf("%d\n", grid[5][6]);
    return 0;
}

However, although it doesn't complain while allocating, I get segmentation fault when accessing the array. I read different posts on decayed arrays and similar topics, but I still can't figure out how to solve this problem. I imagine I'm not passing the 2d array correctly to the function.

Many thanks.

That is not a multidimensional array; it is a single dimensional array containing pointers to single dimensional arrays. Multidimensional arrays do not contain pointers; they are single memory blocks.

Your problem here is that you have a pointer to a pointer, and you're trying to return it from your function through a parameter. If you're going to do that, you're going to need a pointer to a pointer to a pointer as your parameter, and you're going to have to pass the address of a pointer to a pointer to the method. If you don't do this, you're not changing the value of the variable grid in main -- you're changing the one which was copied as the parameter to the malloc2d function. Because the grid in main is left uninitialized, you get a undefined behavior.

Here's an example of what I mean as the fix:

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

int malloc2d(int *** grid, int nrows, int ncols){
    int i;
    *grid = malloc( sizeof(int *) * nrows);

    if (*grid == NULL){
        printf("ERROR: out of memory\n");
        return 1;
    }

    for (i=0;i<nrows;i++){
        (*grid)[i] = malloc( sizeof(int) * ncols);
        if ((*grid)[i] == NULL){
            printf("ERROR: out of memory\n");
            return 1;
        }
    }
    printf("Allocated!\n");
    return 0;
}

int main(int argc, char ** argv)
{
    int ** grid;

    malloc2d(&grid, 10, 10);
    grid[5][6] = 15;
    printf("%d\n", grid[5][6]);
    return 0;
}

Additional notes:

  • If a single allocation fails, you leak the allocation for the first array, as well as the allocations for all previous rows. You need to call free on those before returning.
  • You're returning through a parameter, even though you really don't have to. If I were writing this, I'd make the method return int ** , and signal error by returning 0 .

Here is your function, fixed:

int malloc2d(int *** grid, int nrows, int ncols){
    int i;
    *grid = (int**)malloc( sizeof(int *) * nrows);

    if (*grid == NULL){
        printf("ERROR: out of memory\n");
        return 1;
    }

    for (i=0;i<nrows;i++){

        (*grid)[i] = (int*)malloc( sizeof(int) * ncols);
        if ((*grid)[i] == NULL){
            printf("ERROR: out of memory\n");
            return 1;
        }
    }
    printf("Allocated!\n");
    return 0;
}

int main(int argc, char ** argv)
{
    int ** grid;

    malloc2d(&grid, 10, 10);
    grid[5][6] = 15;
    printf("%d\n", grid[5][6]);
    return 0;
}

Note the function now recieves an int*** and you pass the address of your int** to the function. The function then dereferences the int*** to put the address of the allocated memory block in it.

C is pass by value. And to sum up the error you are doing, this example should be helpful -

void foo( int *temp )
{
     temp = malloc(sizeof(int)) ;
     // temp is assigned to point to new location but the actual variable
     // passed from main do not point to the location temp is pointing to.

     *temp = 10 ;
}

int main()
{
     int *ptr ;
     foo( ptr ) ;

     // ptr is still unintialized
     *ptr = 5 ; // Segmentation fault or Undefined behavior

     return 0;
}

So, instead you should do -

void foo( int **temp )
{
    *temp = malloc(sizeof (int) );
    // ...
}

And now call the function as foo(&ptr); in the main() function.

If you still want malloc2d to return a status code, the parameter needs to be of type int*** :

int malloc2d(int *** grid, int nrows, int ncols){

And you need to use *grid to refer to the supplied buffer:

    int i;
    *grid = malloc( sizeof(int *) * nrows);

    if (*grid == NULL){
        printf("ERROR: out of memory\n");
        return 1;
    }

    for (i=0;i<nrows;i++){
        (*grid)[i] = malloc( sizeof(int) * ncols);
        if ((*grid)[i] == NULL){
            printf("ERROR: out of memory\n");
            return 1;
        }
    }
    printf("Allocated!\n");
    return 0;
}

Then, when calling malloc2d , pass it the address of an int** to fill in:

int ** grid;

malloc2d(&grid, 10, 10);

C is actually always pass by value, so when you pass in 'grid' you are passing in a value and the function is modifying its own local copy. Try passing in '&grid' instead and modify malloc2d appropriately.

That's not your about your segmentation fault problem, but you should really consider to use a single malloc call to allocate all necessary memory space need by grid.

grid = malloc (nrows * ncols * sizeof(int *))

Look at Bill ONeary answer regarding pointer of pointer of pointer.

Shorter should be below, with free (:) and initialized values for each grid element:

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

#define NROWS 10
#define NCOLS 10

int main()
{
    int (* grid)[NCOLS] = calloc(NROWS,sizeof*grid);

    /* no more needed here malloc2d(grid, 10, 10); */
    grid[5][6] = 15;
    printf("%d\n", grid[5][6]);
    free(grid); /* every c/malloc need a free */
    return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM