我有以下结构来创建我的代码:
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编译器告诉我我有分段错误,所以我相信我没有正确分配内存。我不确定我还需要分配什么内存,如果我将当前金额分配给上面的矩阵部分,我们将不胜感激!
答案 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_t
是stddef.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->data
是double **
,因此它指向double *
,因此我们必须分配一些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
来存储指针。
处理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
)。总是
我想补充一点:将整个矩阵存储为连续的元素块通常会好得多,并且只进行一次数组分配。所以矩阵结构就像这样:
#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]
表示法,并且必须使用特殊访问功能(请参阅下面的get
和set
)。这里是他们操纵这样一个矩阵的功能:
/* 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看到它似乎没问题。没有内存泄漏。