Выделение памяти для структуры внутри функции, которая возвращает указатель на структуру - PullRequest
0 голосов
/ 14 февраля 2019

Я пытаюсь создать структуру, которая представляет двумерную математическую матрицу.

Функция 'initMatrix' предназначена для инициализации матрицы из n строк по n столбцам, с элементами, которые хранятся в динамическивыделенный массив значений типа double и установка в ноль.

Однако, когда я пытаюсь присвоить массиву ноль путем разыменования указателя «data» в структуре (согласно этому вопросу) , это вызываетпрограмма провалилась.

#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;
}

Что я делаю не так?

Ответы [ 4 ]

0 голосов
/ 14 февраля 2019

Если вы хотите выделить всю структуру одним 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 путем добавления размера типов полей структуры лучше использовать sizeof или offsetof с типом структуры 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, чтобы получить нулевую инициализированную память.

0 голосов
/ 14 февраля 2019

Поскольку data является указателем на некоторую область памяти, вам придется распределять эту область по отдельности, т.е.

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

Обратите внимание, что вам также придется вызывать free на mptr->data а также на mptr в конце, чтобы вернуть всю выделенную кучу память.

При этом, возможно, вы захотите упростить вашу программу, возвращая Matrix вместо Matrix * из initMatrix.Тогда вам нужно будет только позаботиться о выделении / освобождении члена 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 вместо дискретногоцикл.

0 голосов
/ 14 февраля 2019

Необходимо выделить память для данных отдельно и для матрицы отдельно.Также вам следует освободить память, выделенную внутри функции initMatrix в вашем main().

В дополнение к тому, что все другие очень хорошие ответы упоминали об использовании memset() или calloc() вместо этогосебя в 2 for петлях и т. д. Я хочу упомянуть пару вещей, на которые другие авторы не ответили, а также уловить суть того, что, как я чувствую, спрашивал ОП:

  1. Вы путаете матрицу, которая нуждается в double **data с вектором 1-й, который содержит такое же количество элементов и может быть сохранен в double *data.

    Именно поэтому вы используетедве for петли, наверное.Вы не хотите 4 элемента в 1 строке.Вы хотите 2 ряда и 2 столбца!Вы можете посмотреть код ниже, чтобы увидеть, как этого можно достичь.

  2. Не забудьте освободить как mptr, так и data.

  3. Кроме того, в вашем коде вы не инициализируете ptr->cols и ptr->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
element [1] [1]: 11.000000

, который я только что отобразил для отладки.Вы можете изменить строку ptr->data[i][j] = i*10+j; и получить любой желаемый результат, включая все нули.

0 голосов
/ 14 февраля 2019
ptr = (Matrix *)malloc(sizeof(int) * 2 + sizeof(double) * rows * cols);

вы должны сделать это для ptr->data, а не для ptr.

Разыменование data завершается неудачно, потому что вы никогда не выделяли для него память, поэтому он указывает никуда (неопределенное поведение, чтобы бытьточный).

Итак, сначала выделите Matrix, затем выделите то, что нужно для data

ptr = malloc(sizeof(Matrix));
ptr -> data = malloc(sizeof(int) * 2 + sizeof(double) * rows * cols);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...