В первом примере вы храните только указатель, возвращаемый malloc
, в локальной переменной. Он теряется, когда функция возвращается.
Обычная практика на языке Си - использовать возвращаемое значение функции для передачи указателя на выделенный объект обратно вызывающей стороне. Как указал Армен, вы также можете передать указатель на то, где функция должна хранить свои выходные данные:
void Allocate2D(double*** pMatrix...)
{
*pMatrix = malloc(...)
}
но я думаю, что большинство людей будут кричать, как только увидят ***
.
Вы также можете считать, что массивы указателей не являются эффективной реализацией матриц. Выделение каждой строки по отдельности способствует фрагментации памяти, malloc
накладным расходам (поскольку каждое выделение требует некоторого учета, не говоря уже о дополнительных указателях, которые вы должны хранить), и отсутствию кэширования. И каждый доступ к элементу матрицы включает в себя 2 разыменования указателя, а не только одну, что может привести к задержкам. Наконец, у вас есть намного больше работы, чтобы выделить матрицу, так как вам нужно проверять ошибки каждого malloc
и очищать все, что вы уже сделали, если какой-либо из них потерпит неудачу.
Лучше всего использовать одномерный массив:
double *matrix;
matrix = malloc(nrows*ncols*sizeof *matrix);
затем получите доступ к элементу (i, j) как matrix[i*ncols+j]
. Потенциальными недостатками являются умножение (которое медленно у древних процессоров, но быстро у современных) и синтаксис.
Еще лучший подход - не стремиться к избыточной общности. Большая часть матричного кода в SO предназначена не для продвинутой числовой математики, где могут потребоваться произвольные размеры матриц, а для трехмерных игр, где 2x2, 3x3 и 4x4 являются единственными размерами матриц любого практического использования. Если это так, попробуйте что-то вроде
double (*matrix)[4] = malloc(4*sizeof *matrix);
и затем вы можете получить доступ к элементу (i, j) как matrix[i][j]
с единственной разыменовкой и чрезвычайно быстрым умножением на константу. И если ваш matrix
нужен только в локальной области видимости или внутри структуры, просто объявите его как:
double matrix[4][4];
Если вы не очень хорошо разбираетесь в системе типов C и приведенных выше декларациях, лучше всего в любом случае просто обернуть все ваши матрицы в структуры:
struct matrix4x4 {
double x[4][4];
};
Тогда объявления, приведение указателей, распределение и т. Д. Становятся намного более знакомыми. Единственным недостатком является то, что вам нужно сделать что-то вроде matrix.x[i][j]
или matrix->x[i][j]
(в зависимости от того, является ли matrix
структурой указателя на структуру) вместо matrix[i][j]
.
Редактировать: Я действительно думал об одном полезном свойстве реализации ваших матриц как массивов указателей строк - оно делает перестановку строк тривиальной операцией. Если ваши алгоритмы должны выполнять много перестановок строк, это может быть полезно. Обратите внимание, что выгода не будет большой для маленьких матриц, и перестановка столбцов не может быть оптимизирована таким образом.