1

编辑:我现在意识到我在数组和指针之间造成的混淆。我很欣赏这些评论,但不会使问题更准确,因为它会失去我写它的部分原因。

我正在尝试初始化一个由每个包含一个数组的结构组成的结构。更清楚的是,我有这个矩阵结构:

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;

作为参考,这个矩阵定义和后面使用的初始化函数,来自CMSISarm_math中的库。

我很难理解我应该如何创建一个dataset变量。按照这个问题页面中的答案和讨论,我知道我不能神奇地期望 C 知道要分配多少内存,例如dataset d.

只遵循链接问题的解决方案,我想出了一个函数来为 a 初始化足够的空间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;
}

基本上,我假设矩阵的数量和它们包含的最大元素数量都是已知的,因此会保留足够的内存。

我有以下问题:

  1. 对于这种嵌套结构,整体方法是否正确?
  2. 结构的空间分配是否dataset正确?
  3. 我是否真的确保我创建arm_matrix_instance_f32的结构中包含的所有元素dataset都有足够的空间容纳它们的所有元素?
  4. 我将结构声明为包含A, B, C. 如果我以另一个顺序初始化它们会发生什么?例如,结构如何知道之间留有多少空间A以及C是否B仍未声明?
4

3 回答 3

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,
             }
         },

     };
 }

这将使您免于堆分配/释放。

于 2018-07-18T10:05:48.940 回答
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
}

为什么?因为一旦您改变主意(或切换到不同的设备)并决定在堆上分配矩阵,您就不想重做所有代码。您只需修改您的initdestroy功能。

于 2018-07-18T11:06:24.007 回答
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
}
于 2018-07-18T09:42:54.440 回答