C编程:使用指向指针的方式为二维数组分配内存(使用malloc()函数)

6
昨天我发布了一个问题:如何在调用的函数内传递指针并为传递的指针分配内存? 从我得到的答案中,我能够理解我犯了什么错误。
现在我面临一个新问题,有人能帮忙吗?
我想动态分配一个二维数组,所以我将一个指向指针的指针从我的main()传递到另一个名为alloc_2D_pixels(...)的函数中,在那里我使用malloc(...)for(...)循环来为二维数组分配内存。
好吧,在从alloc_2D_pixels(...)函数返回后,指向指针的指针仍然为空,因此,当我尝试访问或尝试free(...)指向指针的指针时,程序会挂起。
有人能建议我在这里犯了什么错误吗?
救命啊!
Vikram
来源:
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);
}
4个回答

3

一个常见错误是发布不能编译的代码 :). 下面是已经更正并注释过的代码,注释用 /* 这种样式 */:

/* 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);
}

嘿,马克!!! :) 是的,你说得对,我应该发布一个可用的代码。感谢您详细的回复,我很感激。 - HaggarTheHorrible

2
在您的alloc_2D_pixels函数中,访问memory时需要再增加一层间接性。目前情况下,您只修改了参数,而没有修改由该参数指向的指针。例如,
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));

基本上,无论你在哪里使用 memory,你都需要使用 (*memory);只有当你使用下标时,才需要使用括号以确保运算符按正确的顺序应用。


1
应为sizeof(unsigned char*)而非unsigned char**。 - Mark Tolonen
在我看来,它应该是 *memory = malloc(rows * sizeof **memory)(*memory)[i] = malloc(cols * sizeof *(*memory)[i]),即在 sizeof 下始终多一个 *。这样更加防错。 - AnT stands with Russia

1

在C语言中,以这种方式使用多维数组对性能来说是"次优的"。

毫不含糊地说:请不要使用 - 更不要初始化 - 你所展示的多维数组。 malloc() 的多次调用会创建一批不连续的内存位置,这与实际图形(作为连续的单个缓冲区)的存储方式不匹配。此外,如果您需要执行数百次或数千次这样的操作,malloc() 可能会非常昂贵。

另外,由于您经常使用malloc(),这也会给清理工作带来麻烦(并最终导致错误)。您在代码的注释中甚至已经提到了这一点,但为什么还是要这样呢?

如果您确实需要这种ptr[rows][cols]的结构,请以更好的方式创建它:

signed char alloc_2D_pixels(unsigned char*** memory,
                            unsigned int rows,
                            unsigned int cols)
{
    int colspan = cols * sizeof(char);
    int rowspan = rows * sizeof(char*);
    unsigned char **rowptrs = *memory = malloc(rowspan + rows * colspan));

    /* malloc failure handling left to the reader */

    unsigned char *payload = ((unsigned char *)rowptrs) + rowspan;
    int i;
    for (i = 0; i < rows; payload += colspan, i++)
        rowptrs[i] = payload;
}

这样你只分配了一个内存块,整个内存块可以一次性释放 - 抛弃 free_2D_pixels()


啊,如果过去没有其他人回答这个问题,我会感到惊讶。要归功于原作者:https://dev59.com/l3A75IYBdhLWcg3wuLjD#3144577 请查看。 - FrankH.

1

看起来,您正在使用未初始化的rowscols变量


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接