Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C Programming: malloc() for a 2D array (using pointer-to-pointer)

yesterday I had posted a question: How should I pass a pointer to a function and allocate memory for the passed pointer from inside the called function?

From the answers I got, I was able to understand what mistake I was doing.

I'm facing a new problem now, can anyone help out with this?

I want to dynamically allocate a 2D array, so I'm passing a Pointer-to-Pointer from my main() to another function called alloc_2D_pixels(...), where I use malloc(...) and for(...) loop to allocate memory for the 2D array.

Well, after returning from the alloc_2D_pixels(...) function, the pointer-to-pointer still remains NULL, so naturally, when I try accessing or try to free(...) the Pointer-to-Pointer, the program hangs.

Can anyone suggest me what mistakes I'm doing here?

Help!!!

Vikram


SOURCE:

main()
{


 unsigned char **ptr;
 unsigned int rows, cols;

 if(alloc_2D_pixels(&ptr, rows, cols)==ERROR)      // Satisfies this condition
  printf("Memory for the 2D array not allocated"); // NO ERROR is returned

 if(ptr == NULL)                    // ptr is NULL so no memory was allocated
  printf("Yes its NULL!");          

 // Because ptr is NULL, with any of these 3 statements below the program HANGS
 ptr[0][0] = 10;                    
 printf("Element: %d",ptr[0][0]);

 free_2D_alloc(&ptr);

}


signed char alloc_2D_pixels(unsigned char ***memory, unsigned int rows, unsigned int cols)
{
        signed char status = NO_ERROR;

        memory = malloc(rows * sizeof(unsigned char** ));

        if(memory == NULL)
        {
            status = ERROR;
            printf("ERROR: Memory allocation failed!");

        }
        else
        {
            int i;

            for(i = 0; i< cols; i++)
            {
                memory[i] = malloc(cols * sizeof(unsigned char));

                if(memory[i]==NULL)
                {
                    status = ERROR;
                    printf("ERROR: Memory allocation failed!");
                }
            }

        }

    // Inserted  the statements below for debug purpose only
        memory[0][0] = (unsigned char)10;     // I'm able to access the array from
        printf("\nElement %d",memory[0][0]);  // here with no problems


        return status;
}


void free_2D_pixels(unsigned char ***ptr, unsigned int rows)
{
     int i;

     for(i = 0; i < rows; i++)
     {
          free(ptr[i]);
     }

     free(ptr);
}
like image 847
HaggarTheHorrible Avatar asked May 15 '10 23:05

HaggarTheHorrible


2 Answers

One mistake is posting code that won't compile :). Below is corrected code with my comments in
/* this style */:

/* Next four lines get your code to compile */
#include <stdio.h>
#include <stdlib.h>
#define NO_ERROR 0
#define ERROR    1

/* prototypes for functions used by main but declared after main
   (or move main to the end of the file */
signed char alloc_2D_pixels(unsigned char*** memory, unsigned int rows, unsigned int cols);
void free_2D_pixels(unsigned char** ptr, unsigned int rows);

/* main should return int */
int main()
{
    unsigned char** ptr;
    /* need to define rows and cols with an actual value */
    unsigned int rows = 5, cols = 5;

    if(alloc_2D_pixels(&ptr, rows, cols) == ERROR)    // Satisfies this condition
        printf("Memory for the 2D array not allocated"); // ERROR is returned

    if(ptr == NULL)                    // ptr is NULL so no memory was allocated
        printf("Yes its NULL!");
    else
    {
        /* Added else clause so below code only runs if allocation worked. */
        /* Added code to write to every element as a test. */
        unsigned int row,col;
        for(row = 0; row < rows; row++)
            for(col = 0; col < cols; col++)
                ptr[0][0] = (unsigned char)(row + col);

           /* no need for &ptr here, not returning anything so no need to pass
              by reference */
           free_2D_pixels(ptr, rows);
    }

    return 0;
}

signed char alloc_2D_pixels(unsigned char*** memory, unsigned int rows, unsigned int cols)
{
    signed char status = NO_ERROR;

    /* In case we fail the returned memory ptr will be initialized */
    *memory = NULL;

    /* defining a temp ptr, otherwise would have to use (*memory) everywhere
       ptr is used (yuck) */
    unsigned char** ptr;

    /* Each row should only contain an unsigned char*, not an unsigned
       char**, because each row will be an array of unsigned char */
    ptr = malloc(rows * sizeof(unsigned char*));

    if(ptr == NULL)
    {
        status = ERROR;
        printf("ERROR: Memory allocation failed!");
    }
    else
    {
        /* rows/cols are unsigned, so this should be too */
        unsigned int i;

        /* had an error here.  alloced rows above so iterate through rows
           not cols here */
        for(i = 0; i < rows; i++)
        {
            ptr[i] = malloc(cols * sizeof(unsigned char));

            if(ptr[i] == NULL)
            {
                status = ERROR;
                printf("ERROR: Memory allocation failed!");
                /* still a problem here, if exiting with error,
                   should free any column mallocs that were
                   successful. */
            }
        }
    }

    /* it worked so return ptr */
    *memory = ptr;
    return status;
}


/* no need for *** here. Not modifying and returning ptr */
/* it also was a bug...would've needed (*ptr) everywhere below */
void free_2D_pixels(unsigned char** ptr, unsigned int rows)
{
    /* should be unsigned like rows */
    unsigned int i;

    for(i = 0; i < rows; i++)
    {
        free(ptr[i]);
    }

    free(ptr);
}
like image 189
Mark Tolonen Avatar answered Oct 04 '22 22:10

Mark Tolonen


In your alloc_2D_pixels function, you need another level of indirection when accessing memory. As it is now, you only modify the parameter, not the pointer pointed to by the parameter. For example,

memory = malloc(rows * sizeof(unsigned char** ));
// becomes
*memory = malloc(rows * sizeof(unsigned char** ));

// and later...
memory[i] = malloc(cols * sizeof(unsigned char));
// becomes
(*memory)[i] = malloc(cols * sizeof(unsigned char));

(basically, anywhere you are using memory, you need to use (*memory); the parentheses are only needed when you are using subscripts to ensure that the operators are applied in the correct order)

like image 31
James McNellis Avatar answered Oct 04 '22 21:10

James McNellis