如何创建已知最大大小的结构的结构

时间:2018-07-18 09:04:38

标签: c memory-management struct arm cmsis

编辑:我现在知道我在数组和指针之间造成的困惑。我感谢您的发言,但不会使问题更准确,因为它会丢失我写它的部分原因。

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

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

基本上,我从这样一个假设开始,即矩阵的数量和它们包含的元素的最大数量都是已知的,因此保留了足够的内存。

我有以下问题:

  1. 整体方法是否适用于此类嵌套结构?
  2. dataset结构的空间分配正确吗?
  3. 我真的要确保我创建的arm_matrix_instance_f32结构中包含的所有dataset元素都有足够的空间容纳所有元素吗?
  4. 我声明该结构包含A, B, C。如果我以其他顺序初始化它们会怎样?例如,如果仍未声明A,结构将如何知道CB之间还有多少空间?

3 个答案:

答案 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 。您只需修改initdestroy函数。

答案 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
}