Ничего себе.Я не знаю точно, с чего начать очистку, поэтому я попытаюсь начать с нуля.
Из вашего кода кажется, что вы хотите, чтобы все строки и все столбцы были одинаковымиразмер - то есть никакие два ряда не будут иметь разные размеры.Если это не так, дайте мне знать, но это сделать гораздо сложнее.
Теперь давайте сначала определим struct
для хранения количества строк, количества столбцов и самих данных массива.
struct Matrix {
size_t width;
size_t height;
double **data;
};
Существуют различные способы хранения данных, но мы можем рассмотреть их позже.
size_t
- целое число без знака (не с плавающей запятой - нет плавающих без знакатипы точек) тип, определенный в stddef.h
(среди других мест), чтобы быть достаточно большим, чтобы хранить любой допустимый размер объекта или индекс массива.Поскольку нам нужно хранить размеры массивов, это именно то, что нам нужно для хранения высоты и ширины нашей матрицы.
double **data
- это указатель на указатель на double
, который (в данном случае) сложный способ сказать двумерный массив double
s, который мы выделяем во время выполнения с помощью 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->data
является double **
, оно указывает на double *
s, поэтому мы должны выделить количество объектов double *
размера для хранения в нем.Если мы хотим, чтобы это была высота нашего массива, мы выделяем height
число double *
с, то есть 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
s для хранения в этом указателе.
Код для обработки ошибки malloc
довольно сложен: мы должны вернуться к каждой ранее добавленной строке и free
it, , затем 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);
}
Это делает (в основном) то, что мы должны были сделать в случае ошибки выделения в конструкторе (давайте продолжим и назовем его a), так что если вы получите все этоэто должен быть торт.
Следует отметить, что это не обязательно лучший способ реализации матрицы.Если вам требуется, чтобы пользователи вызывали функцию get(matrix, i, j)
для доступа к массиву вместо прямой индексации данных через matrix->data[i][j]
, вы можете сжать (сложное) распределение double **
в плоский массив и вручную выполнить индексацию с помощью умножения в вашемфункции доступа.Если у вас есть C99 (или вы хотите перепрыгнуть через несколько циклов для поддержки C89), вы даже можете сделать плоские матричные данные частью вашего struct Matrix
выделения объектов с помощью гибкого элемента массива, что позволит вам освободить свой объект однимпозвоните по номеру free
.Но если вы понимаете, как работает вышеперечисленное, вы должны быть на пути к внедрению любого из этих решений.