【问题标题】:Allocating memory for a structure within a function which returns pointer to struct在返回指向结构的指针的函数中为结构分配内存
【发布时间】:2019-07-08 07:53:02
【问题描述】:

我正在尝试创建一个表示二维数学矩阵的结构。

函数“initMatrix”旨在初始化一个 n 行 x n 列的矩阵,其元素存储在一个动态分配的双精度数组中并设置为零。

但是,当我尝试通过取消引用结构(according to this question) 中的“数据”指针来将零分配给数组时,这会导致程序失败。

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

typedef struct
{
  int rows;
  int cols;
  double *data;
} Matrix;

Matrix *initMatrix(int rows, int cols)
{
  Matrix *ptr;
  ptr = (Matrix *)malloc(sizeof(int) * 2 + sizeof(double) * rows * cols);
  if(ptr == NULL) printf("Couldn't allocate memory");
  for(int i = 0; i < rows; i++) {
    for(int j = 0; j < cols; j++) {
      *(ptr->data) = 0; //error originating here
      ptr->data++;
    }
  }
  return ptr;
}

int main(void) 
{
  Matrix *mptr = initMatrix(2, 2);

  return 0;
}

我做错了什么?

【问题讨论】:

    标签: c


    【解决方案1】:

    您需要分别为数据和矩阵分配内存。此外,您应该释放在 main() 中的 initMatrix 函数内分配的内存。

    除了所有其他非常好的答案提到的关于使用memset()calloc() 而不是自己在 2 个for 循环中执行此操作等等之外,我还想提几件事其他张贴者没有回答,也为了抓住要点,我觉得 OP 在问什么:

    1. 您将需要double **data 的矩阵与包含相同数量元素且可以存储在double *data 中的一维向量混淆了。

      我猜这就是你使用两个for 循环的原因。您不希望 1 行中有 4 个元素。你想要 2 行和 2 列!您可以查看下面的代码以了解如何实现。

    2. 记得释放mptrdata

    3. 此外,在您的代码中,您没有初始化 ptr-&gt;colsptr-&gt;rows

    代码:

    #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct
    {
        int rows;
        int cols;
        double **data;
    } Matrix;
    
    Matrix *initMatrix(int rows, int cols)
    {
        Matrix *ptr;
        ptr = (Matrix *)malloc(sizeof(Matrix));
        ptr->cols = cols;
        ptr->rows = rows;
        ptr->data = (double**)malloc(sizeof(double*) * rows);
        for(int i = 0; i < rows; i++) {
            ptr->data[i] = (double*)malloc(sizeof(double) * cols);
            for(int j = 0; j < cols; j++) {
                ptr->data[i][j] = i*10+j;
                // printf("%d,%d,%lf", i, j, ptr->data[i][j]); // just for debugging
            }
        }
        return ptr;
    }
    
    int main(void)
    {
        Matrix *mptr = initMatrix(2, 2);
        printf("rows:%d, cols:%d\n", mptr->rows, mptr->cols);
    
        for (int i=0; i<2; i++) {
            for (int j=0; j<2; j++) {
                printf("element[%d][%d]:%lf\n",i,j,mptr->data[i][j]);
            }
        }
    
        free(mptr->data);
        free(mptr);
    
        return 0;
    }
    

    产生输出:

    行:2,列:2
    元素[0][0]:0.000000
    元素[0][1]:1.000000
    元素[1][0]:10.000000
    元素[1][1]:11.000000

    我刚刚显示用于调试。您可以更改ptr-&gt;data[i][j] = i*10+j; 行并获得您想要的任何输出,包括全零。

    【讨论】:

    • @Jabberwocky,我编辑了我的答案和代码。我在其中发现了一些其他的谬误,我已经纠正了。另外,关于malloc()失败,如果malloc()失败,那么据我所知和经验,是时候收拾东西回家了:)。除了双关语,这取决于这段代码在哪里运行。如果它是嵌入式设备,那么我们就有严重的问题需要解决。是的 printf 在这种情况下是没有用的(除了可能要记录,因为 malloc 失败的错误消息不会自己进入日志)。
    【解决方案2】:

    如果你想用一个malloc 分配整个结构,你可以使用

    typedef struct
    {
      int rows;
      int cols;
      double data[1];
    } Matrix;
    

    与其他答案相比,这允许使用单个 free

    如果您的编译器支持大小为 0 的数组,您也可以使用 double data[0];

    从 C99 开始,您可以在结构中使用没有维度的数组:double data[];,请参阅 https://stackoverflow.com/a/2061038/10622916

    与其通过添加结构字段类型的大小来计算malloc 的大小,不如将sizeofoffsetof 与结构类型Matrix 一起使用,因为可能会有一些填充。 (可能不是您的情况,但这取决于编译器实现和您的结构字段。)

    #include <stdio.h>
    #include <stdlib.h>
    #include <stddef.h>  // for offsetof
    
    typedef struct
    {
      int rows;
      int cols;
      double data[1];
      // or with C99: double data[];
    } Matrix;
    
    Matrix *initMatrix(int rows, int cols)
    {
      Matrix *ptr;
      double *data;
      ptr = (Matrix *)malloc(
         offsetof(Matrix, data) // = size of Matrix without double data[1];
         // with C99's "double data[];" or with the non-standard 
         // "double data[0];" you can replace offsetof(...) with: sizeof(Matrix)
         + sizeof(double) * rows * cols); // dynamic array size
      if(ptr == NULL) {
        perror("malloc failed");
      } else {
        // save rows and columns for later accessing the matrix
        ptr->rows = rows;
        ptr->cols = cols;
    
        data = ptr->data;
        for(int i = 0; i < rows; i++) {
          for(int j = 0; j < cols; j++) {
            *(data) = 0;
            data++;
          }
        }
      }
    
      return ptr;
    }
    
    int main(void) 
    {
      Matrix *mptr = initMatrix(2, 2);
      // use matrix
    
      // a single free is sufficient with the approach above
      free(mptr); mptr = NULL;
    
      return 0;
    }
    

    你可以计算数组索引,而不是增加指针data

        for(int i = 0; i < rows; i++) {
          for(int j = 0; j < cols; j++) {
            ptr->data[i * cols + j] = 0;
          }
        }
    

    或者对于 0 初始化,你可以使用 memset

        memset(ptr->data, 0, sizeof(double) * rows * cols);
    

    或者使用calloc(1, offsetof(Matrix, data) + sizeof(double) * rows * cols);而不是malloc来获得零初始化内存。

    【讨论】:

    • 对于零初始化,使用calloc() 而不是malloc
    【解决方案3】:

    由于data 是指向某个内存区域的指针,因此您必须单独分配该区域,即

    ptr = (Matrix *)malloc(sizeof(Matrix));
    ptr->data = (double *)malloc(sizeof(double)*rows*cols);
    

    请注意,您还必须在最后调用mptr-&gt;datamptr 上的free 才能返回所有堆分配的内存。

    话虽如此,您可能希望通过从initMatrix 返回Matrix 而不是Matrix * 来简化程序。然后,您只需要关心分配/释放data 成员:

    Matrix initMatrix(int rows, int cols)
    {
      Matrix result;
      result.data = (double *)malloc(sizeof(double) * rows * cols);
    
      if(result.data == NULL) printf("Couldn't allocate memory");
    
      result.rows = rows;
      result.cols = cols;
    
      // initialization of result.data here, consider using memset() ...
      return result;
    }
    

    要将内存初始化为零,您可能需要使用memset 而不是离散循环。

    【讨论】:

    • 您仍然需要将矩阵归零(使用calloc)并初始化其他成员,但 OP 也没有执行最后一部分(忘记了)。
    • BTW if(result.data == NULL) printf("Couldn't allocate memory"); 是相当无意义的,如果malloc 失败,它会显示消息但是会发生什么?
    • @PaulOgilvie:添加了成员​​初始化
    【解决方案4】:
    ptr = (Matrix *)malloc(sizeof(int) * 2 + sizeof(double) * rows * cols);
    

    您应该为ptr-&gt;data 执行此操作,而不是为ptr

    取消引用 data 失败,因为您从未为它分配内存,所以它没有指向任何地方(准确地说是未定义的行为)。

    所以先分配一个Matrix,然后再分配data需要的东西

    ptr = malloc(sizeof(Matrix));
    ptr -> data = malloc(sizeof(int) * 2 + sizeof(double) * rows * cols);
    

    【讨论】:

      猜你喜欢
      • 2015-02-05
      • 2021-12-26
      • 1970-01-01
      • 2015-07-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-21
      相关资源
      最近更新 更多