编辑:我现在知道我在数组和指针之间造成的困惑。我感谢您的发言,但不会使问题更准确,因为它会丢失我写它的部分原因。
我正在尝试初始化由每个包含数组的结构组成的结构。更清楚地说,我有这个矩阵结构:
typedef struct
{
uint16_t numRows; /**< number of rows of the matrix. */
uint16_t numCols; /**< number of columns of the matrix. */
float32_t *pData; /**< points to the data of the matrix. */
} arm_matrix_instance_f32;
我需要将这些矩阵对象中的一些放到更大的结构中
typedef struct
{
arm_matrix_instance_f32 A;
arm_matrix_instance_f32 B;
arm_matrix_instance_f32 C;
} dataset;
作为参考,此矩阵定义和稍后使用的初始化函数来自CMSIS中的arm_math
库。
我在理解如何创建dataset
变量时遇到困难。遵循此question page中的答案和讨论之后,我了解到我无法神奇地期望C知道使用dataset d
之类的声明分配多少内存。
仅遵循链接问题的解决方案,我想出了一个函数来初始化dataset
的足够空间,并想出一个函数来创建类型为dataset
的变量。我现在有这样的东西:
dataset* create_dataset( void ) {
uint8_t n_matrices = 3;
uint8_t n_elements = 9;
dataset* d= malloc( n_matrices * (sizeof(float32_t)*n_elements + sizeof(uint16_t)*2));
memset(d, 0, sizeof(*d));
const float32_t zeros33_f32[9] =
{
0.0, 0.0, 0.0,
0.0, 0.0, 0.0,
0.0, 0.0, 0.0,
};
const float32_t zeros31_f32[3] =
{
0.0,
0.0,
0.0,
};
const float32_t zeros13_f32[3] =
{
0.0, 0.0, 0.0,
};
arm_mat_init_f32( &(d->A), 3, 3, (float32_t *)zeros33_f32);
arm_mat_init_f32( &(d->B), 3, 1, (float32_t *)zeros31_f32);
arm_mat_init_f32( &(d->C), 1, 3, (float32_t *)zeros13_f32);
return d;
}
基本上,我从这样一个假设开始,即矩阵的数量和它们包含的元素的最大数量都是已知的,因此保留了足够的内存。
我有以下问题:
dataset
结构的空间分配正确吗? arm_matrix_instance_f32
结构中包含的所有dataset
元素都有足够的空间容纳所有元素吗? A, B, C
。如果我以其他顺序初始化它们会怎样?例如,如果仍未声明A
,结构将如何知道C
和B
之间还有多少空间?答案 0 :(得分:1)
我认为您应该对此采取更精细的方法,并从分别为每个arm_matrix_instance_f32
分配空间开始。考虑为这些实例创建工厂功能。它将产生更具可读性的代码,并使您可以简单地用其他实例替换arm_matrix_instance_f32
中的dataset
。
另一方面,如果您始终知道矩阵的数量以及它们包含的元素的最大数量,则可以使用复合文字来生成数据集:
dataset create()
{
return (dataset) {
.A = {
3, 3, (float32_t []) {
1.0, 2.0, 3.0,
4.0, 5.0, 6.0,
7.0, 8.0, 9.0,
}
},
.B = {
3, 3, (float32_t []) {
2.0, 0.0, 0.0,
0.0, 0.0, 0.0,
0.0, 0.0, 0.0,
}
},
.C = {
3, 3, (float32_t []) {
2.0, 0.0, 0.0,
0.0, 0.0, 0.0,
0.0, 0.0, 0.0,
}
},
};
}
这将使您从堆分配/取消分配中解放出来。
答案 1 :(得分:1)
要创建类型为dataset
(或与此相关的任何struct
)的变量:
dataset d;
就是这样。没什么了。
要在堆上分配类型为dataset
(或任何struct
)的对象:
dataset* dp = malloc(sizeof(dataset));
就是这样。没什么了。
现在正确地初始化这样的对象是另一个问题。但是,为了初始化某些内容,您需要先创建该内容。最好在精神上将创建和初始化这两个过程分开。
因此您手上有未初始化的struct
。如何初始化?逐场。
每个字段都是一个矩阵,可能需要自己复杂的初始化,因此编写专用的矩阵初始化函数会很有帮助。让我们先使用一个,然后再编写。假设您必须在堆上分配数据集。
dataset* allocate_dataset() {
dataset* dp = malloc(sizeof(dataset));
if (dp == NULL) { /* report out-of-memory error */ }
init_matrix(&dp->A, 3, 3);
init_matrix(&dp->B, 3, 1);
init_matrix(&dp->C, 1, 3);
return dp;
}
无论在堆上分配的是什么,最终都必须释放,因此我们编写了一个对称的释放函数:
void free_dataset(dataset* dp) {
destroy_matrix(&dp->A);
destroy_matrix(&dp->B);
destroy_matrix(&dp->C);
free(dp);
}
开始矩阵初始化。有一个库函数可以执行此操作,但是它需要一个指向其数据数组的指针,该数组应该分配到某个地方。我认为它存在于堆中。
void init_matrix(arm_mat_init_f32* mp, int rows, int cols) {
float32_t* data = malloc(sizeof(float32_t * rows * cols);
if (data == NULL) { /* report out-of-memory error */ }
arm_mat_init_f32(mp, rows, cols, data);
}
销毁矩阵几乎是微不足道的:
void destroy_matrix(arm_mat_init_f32* mp) {
free (mp->pData);
}
同样,这假设您需要在堆上分配矩阵数据。不一定是这种情况。也许您在内存有限的嵌入式设备上。现在让我们假设相反:没有堆。您不需要分配数据集,您仍然需要初始化它:
void init_dataset (dataset* dp);
现在init_matrix除了调用arm_mat_init_f32
外什么也不做,因此我们可以直接使用后者:
void init_dataset (dataset* dp) {
arm_mat_init_f32(&dp->A, 3, 3, (float32_t[]){0,0,0, 0,0,0, 0,0,0});
arm_mat_init_f32(&dp->B, 3, 1, (float32_t[]){0,0,0});
arm_mat_init_f32(&dp->C, 1, 3, (float32_t[]){0,0,0});
}
不需要销毁,但是您可能仍希望保留不做任何事的销毁功能,以防万一,并在适当的时候调用它们。
void destroy_dataset(dataset* dp) {
destroy_matrix(&dp->A);
destroy_matrix(&dp->B);
destroy_matrix(&dp->C);
}
void destroy_matrix(arm_mat_init_f32* mp) {
(void)mp; // suppress compiler warning
}
为什么?因为一旦您改变主意(或切换到其他设备)并决定在堆上分配矩阵,就不想重做代码的 all 。您只需修改init
和destroy
函数。
答案 2 :(得分:0)
当心,指针和数组是不同的动物!
在这里,arm_matrix_instance_f32
不包含数组,而是一个指针。 dataset
包含3个。句号。
这意味着这一行是完全错误的:
dataset* d= malloc( n_matrices * (sizeof(float32_t)*n_elements + sizeof(uint16_t)*2));
您应该改为分别分配结构和数组:
dataset* create_dataset( void ) {
dataset* d = malloc(sizeof(*d)); // allocate memory for the structs
if (d == NULL) return NULL; // could not allocate
// allocate memory for the arrays (9 + 3 + 3)
float32_t *array = malloc(15 + * sizeof(*array));
if (array == NULL) {
free(d); // free d if array not allocated
return NULL;
}
for (int i=0; i<15; i++) array[i] = 0.; // zeroes the arrays
arm_mat_init_f32( &(d->A), 3, 3, array); // pass 9 first elements to A
arm_mat_init_f32( &(d->B), 3, 1, array + 9); // pass following 3 to B
arm_mat_init_f32( &(d->C), 1, 3, array + 12); // pass last 3 to C
return d;
}
使用malloc
分配了所有内容后,您将需要稍后释放它:
void destroy_dataset(dataset *d) {
free(d->A.pData); // free the array (A got the beginning of the allocated array)
free(d); // and the struct
}