C中二维数组的内存分配

时间:2011-02-02 07:13:44

标签: c memory-management

我有以下结构来创建我的代码:

struct Mtrx {
       unsigned double h;
       struct MtrxRows** mtrxrows;
}
struct MtrxRows {
       unsigned double w;
       double* row;
}

我正在尝试创建一个名为mtrxCreate的方法,它接受参数高度和宽度,这就是我在下面的内容:

Mtrx* mtrxCreate(unsigned double height, unsigned double width){
       Mtrx* mtrx_ptr = malloc(sizeof(double)*height);
       int i;
       mtrx_ptr->mtrxrows = malloc(sizeof(double)*height);
       for(i = 0; i < height; ++i){
              mtrx_ptr->mtrxrows[i]->row = malloc(sizeof(double) * width);
              mtrx_ptr->mtrxrows[i]->w = width;
       }
       mtrx_ptr->h = height;
       return mtrx_ptr;
}

GCC编译器告诉我我有分段错误,所以我相信我没有正确分配内存。我不确定我还需要分配什么内存,如果我将当前金额分配给上面的矩阵部分,我们将不胜感激!

3 个答案:

答案 0 :(得分:3)

您没有为某些事情分配适量的内存。首先,Mtrx结构本身:

Mtrx* mtrx_ptr = malloc(sizeof(double)*height);

应该是:

Mtrx* mtrx_ptr = malloc(sizeof(struct Mtrx));

接下来,我不确定为什么你的mtrxrows字段是双指针。我认为它应该是一个单指针,一行的一行数组(其中每行中也有一些元素)。如果将其更改为单个指针,则可以按以下方式分配行:

mtrx_ptr->mtrxrows = malloc(sizeof(struct MtrxRows)*height);

编辑:对不起我在这个示例中注意到了一些事情,所以我稍微调整了一下这个答案。

答案 1 :(得分:3)

哇。我不知道从哪里开始清理它,所以我将尝试从头开始。

从您的代码中,您似乎希望所有行和所有列的大小相同 - 也就是说,没有两行具有不同的大小。如果这是错的,请告诉我,但要做得更难。

现在,首先让我们定义一个struct来保存行数,列数和数组本身。

struct Matrix {
  size_t width;
  size_t height;
  double **data;
};

存储数据有不同的方法,但我们可以稍后查看。

size_tstddef.h(以及其他地方)中定义的无符号整数(非浮点 - 没有无符号浮点类型)类型,足以存储任何有效的对象大小或数组指数。由于我们需要存储数组大小,因此我们需要存储矩阵的高度和宽度。

double **data是一个指向double指针的指针,这是(在这种情况下)一个复杂的方式来表示我们分配的double的二维数组运行时malloc

让我们开始定义一个函数。所有这些代码行都在一起,但是我将它们分开以确保你理解所有不同的部分。

struct Matrix *make_Matrix(size_t width, size_t height, double fill)

请注意,您必须说struct Matrix,而不仅仅是Matrix。如果你想放弃struct,你必须使用typedef,但这并不重要恕我直言。 fill参数将允许用户为矩阵的所有元素指定默认值。

{
    struct Matrix *m = malloc(sizeof(struct Matrix));
    if(m == NULL) return NULL;

此行分配足够的内存来存储struct Matrix。如果它无法分配任何内存,我们将返回NULL

    m->height = height;
    m->width  = width;
    m->data   = malloc(sizeof(double *) * height);
    if(m->data == NULL)
      {
        free(m);
        return NULL;
      }

所有这一切都应该有意义。由于m->datadouble **,因此它指向double *,因此我们必须分配一些double *大小的对象来存储在其中。如果我们希望它是我们的数组高度,我们会分配heightdouble *个数,即sizeof(double *) * height。请记住:如果您的指针是T *,则需要分配T个大小的对象。

如果分配失败,我们不能只返回NULL - 那会泄漏内存!在我们返回free之前,我们必须NULL我们之前分配但不完整的矩阵。

    for(size_t i = 0; i < height; i++)
      {
        m->data[i] = malloc(sizeof(double) * width);
        if(m->data[i] == NULL)
          {
            for(size_t j = 0; j < i; j++) free(m->data[j]);
            free(m->data);
            free(m);
            return 0;
          }

现在我们循环遍历每一列并分配一行。请注意,我们分配sizeof(double) * width空格 - 因为m->data[i]double *(我们已经取消引用double **一次),我们必须分配double来存储指针。

处理malloc失败的代码非常棘手:我们必须遍历以前添加的每一行,然后free然后 free(m->data),然后free(m),然后返回NULL。你必须以相反的顺序释放所有内容,因为如果你先释放m,那么你就无法访问所有m的数据(你必须释放所有这些,否则你会泄漏内存) )。

        for(size_t j = 0; j < width; j++) m->data[i][j] = fill;

这将循环遍历行的所有元素,并使用fill值填充它们。与上述相比并不算太差。

      }
    return m;
}

完成所有操作后,我们只返回m对象。用户现在可以访问m->data[1][2]并获取第2列第3行中的项目。但在我们完成之前,由于需要花费很多精力才能创建,因此当我们进行清理时,此对象需要花费一些精力来清理完成。让我们做一个清理功能:

void free_Matrix(struct Matrix *m)
{
    for(size_t i = 0; i < height; i++) free(m->data[i]);
    free(m->data);
    free(m);
}

这是(基本上)我们必须做的事情,如果分配失败(让我们继续并称之为)构造函数,所以如果你得到所有这一切应该是蛋糕。

应该注意,这不一定是实现矩阵的最佳方式。如果您要求用户调用get(matrix, i, j)函数进行数组访问而不是通过matrix->data[i][j]直接索引数据,则可以将(复杂)double **分配压缩为平面数组,并手动执行通过访问函数中的乘法索引。如果您有C99(或者愿意为C89支持跳过一些箍),您甚至可以使用灵活的数组成员将平面矩阵数据作为struct Matrix对象分配的一部分,从而允许您释放对象只需拨打一次free即可。但是,如果您了解上述方法的工作原理,那么您应该很好地实现其中任何一种解决方案。

答案 2 :(得分:0)

如@Chris Lutz所述,从头开始更容易。从其他答案中可以看出,通常应该使用整数类型(例如size_t)来指定数组长度,并且不仅应该分配指针,还应该分配它们存储的结构。还有一件事:您应该始终检查分配结果(如果malloc返回NULL)。总是

一个想法:将2D数组存储在一维数组

我想补充一点:将整个矩阵存储为连续的元素块通常会好得多,并且只进行一次数组分配。所以矩阵结构就像这样:

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

/* allocate a single contiguous block of elements */
typedef struct c_matrix_t {
    size_t w;
    size_t h;
    double *elems;  /* contiguos block, row-major order */
} c_matrix;

好处是:

  • 你只需要分配一次内存(通常是缓慢且不可预测的操作)
  • 处理分配错误更容易(如果没有分配最后一行,你只需要一个指向要检查的指针就不需要释放所有以前分配的行)
  • 你会得到一个有意义的记忆块,这可能有助于有效地编写一些矩阵算法

可能它也更快(但应首先测试)。

缺点:

  • 您无法使用m[i][j]表示法,并且必须使用特殊访问功能(请参阅下面的getset)。

获取/设置元素

这里是他们操纵这样一个矩阵的功能:

/* get an element pointer by row and column numbers */
double* getp(c_matrix *m, size_t const row, size_t const col) {
    return (m->elems + m->w*row + col);
}

/* access elements by row and column numbers */
double get(c_matrix *m, size_t const row, size_t const col) {
    return *getp(m, row, col);
}

/* set elements by row and column numbers */
void set(c_matrix *m, size_t const row, size_t const col, double const val) {
    *getp(m, row, col) = val;
}

内存分配

现在看看如何分配它,请注意这种分配方法有多简单:

/* allocate a matrix filled with zeros */
c_matrix *alloc_c_matrix(size_t const w, size_t const h) {
    double *pelems = NULL;
    c_matrix *pm = malloc(sizeof(c_matrix));
    if (pm) {
        pm->w = w;
        pm->h = h;
        pelems = calloc(w*h, sizeof(double));
        if (!pelems) {
            free(pm); pm = NULL;
            return NULL;
        }
        pm->elems = pelems;
        return pm;
    }
    return NULL;
}

我们首先分配一个矩阵结构(pm),如果这个分配成功,我们分配一个元素数组(pelem)。由于最后一次分配也可能失败,我们必须回滚我们已经完成的所有分配。幸运的是,使用这种方法只有其中一种(pm)。

最后,我们必须编写一个函数来释放矩阵。

/* free matrix memory */
void free_c_matrix(c_matrix *m) {
    if (m) {
    free(m->elems) ; m->elems = NULL;
    free(m); m = NULL;
    }
}

由于原始free (3)在收到NULL指针时未采取任何操作,因此我们free_c_matrix都没有。

测试

现在我们可以测试矩阵了:

int main(int argc, char *argv[]) {
    c_matrix *m;
    int i, j;
    m = alloc_c_matrix(10,10);
    for (i = 0; i < 10; i++) {
    for (j = 0; j < 10; j++) {
        set(m, i, j, i*10+j);
    }
    }
    for (i = 0; i < 10; i++) {
    for (j = 0; j < 10; j++) {
        printf("%4.1f\t", get(m, i, j));
    }
    printf("\n");
    }
    free_c_matrix(m);
    return 0;
}

有效。我们甚至可以run it through Valgrind memory checker看到它似乎没问题。没有内存泄漏。