C相同结构大小不同

我的问题与这个问题有关: c在struct中定义具有不同大小的数组

但是,我不想使用动态分配(嵌入式目标)。

  • 问题回顾:

C中 ,我希望有两个相同结构的版本,每个版本的静态数组大小不同。 两个结构都将通过指针参数由相同的函数使用。

typedef struct { short isLarge; //set 0 at initialization short array[SIZE_A]; //more arrays } doc_t; typedef struct { short isLarge; //set 1 at initialization short array[SIZE_B]; //more arrays } doc_large_t; void function( doc_t* document ) { if ( document->isLarge ) { //change document into doc_large_t* [1] } //common code for both doc_t and doc_large_t } 
  • 问题:

(1)上面的描述需要一种方法来动态地将指针doc_t *指针转换为doc_large_t * document [1]。 那可能吗 ? 怎么样 ?

(2)我遇到的另一个解决方案是为两个结构都有一个公共头数据部分,不仅包括isLarge标志,还包括指向以下静态数组的指针。 这有多难啊?

(3)另外,你有一个很好的技巧或工作环节我可以使用吗?

编辑:

  • 更多背景:

我的应用程序是嵌入式MCU上的路径查找。

我有几何对象,比如多边形。 多边形可以描述简单的矩形障碍物,以及更复杂的形状(例如可访问区域)。

复杂多边形可以具有大量顶点,但数量很少。 简单的多边形很常见。

两者都将使用相同的算法。 我事先知道哪个多边形需要更多顶点。

我要做的是优化工作记忆,使其适合MCU。 (即小形状获得小数组;复杂形状获得大数组)

想法类似于你在问题中提到的(指向数组的指针),但只有一个指针:

 typedef struct { short array[SIZE_B - SIZE_A]; // more arrays alike... } Extension; typedef struct { short array[SIZE_A]; //more arrays (all the small ones!) Extension* extraData; } doc_t; 

如果extraData为NULL,则您有一个小的多边形,否则,您会在引用的结构中找到其他数据。 承认,迭代大多边形的所有值会有点讨厌……

如果你可以为每个对象类型使用预定义大小的全局数组(正如Dominic Gibson所提出的那样 – 顺便说一下这是一个好主意),你可以通过用函数替换它来节省isLarge标志:

 int isLarge(void* ptr) { return (uintptr_t)globalLargeArray <= (uintptr_t)ptr && (uintptr_t)ptr < (uintptr_t)globalLargeArray + sizeof(globalLargeArray); } 

当然, 所有多边形(在上面的情况下:至少是大的多边形)都必须存在于这个数组中才能使它工作。 如果你动态创建至少一个或其他地方(堆栈,另一个全局变量) - 我们出去了......

全局创建数组并使用指针指向大或小数组。

您应该尝试保留单个结构,并且对于不同的数组大小,将它们放在一个union 。 我不知道以下结构是否对您的案例有意义。

  typedef struct { short isLarge; //manually set to 0 or 1 after creating structure //and accordingly initialize the arrays in below union union my_varying_arrays { short array_A[SIZE_A]; short array_B[SIZE_B]; }; //more arrays } doc_t; 

如果isLarge0 ,则设置array_A数组的值,如果为1 ,则设置数组array_B的值。

您可以通过对特定数组使用void *来执行此操作。 然后根据结构中的属性,将void *为您需要的值。

当您在运行时需要结构时,它会变得更加复杂。 尤其是嵌入式目标。

 typedef struct { short size; void *array; } doc_t; 

其中array指向由内存管理器分配的内存块。

您现在必须决定是使用C标准malloc还是使用基于最大块大小的池化内存系统。 一个例子是ChibiOS内存池 。 如果您随机分配和释放可变大小的内存块,则存在内存碎片风险。

如果你逐步分配,你不必担心内存。 只需创建一个大块并跟踪您的位置。 有点像堆栈。

在编辑之后,我认为您可以做的最好的事情是分析您的需求,定义目标可以管理的最大简单和复杂多边形,然后声明一个单面和常见多边形池,例如:

 #include  #include  #include  #define MAX_COMPLEX 16 #define MAX_SIMPLE 16 uint16_t g_Simple_Poly_set[MAX_COMPLEX][SIZE_A]; uint16_t g_Complex_Poly_set[MAX_COMPLEX][SIZE_B]; uint16_t g_Simple_Poly_used = 0; uint16_t g_Complex_Poly_used = 0; struct poly { bool isLarge; uint16_t *vetexes; }; bool create_poly_simple (struct poly *p) { bool retVal = false; // default: not more space for poly if (g_Simple_Poly_used < MAX_SIMPLE) { p->isLarge = false; p->vetexes = &g_Simple_Poly_set[g_Simple_Poly_used][0]; g_Simple_Poly_used++; retVal = true; } return retVal; } bool create_poly_compleX (struct poly *p) { bool retVal = false; // default: not more space for poly if (g_Complex_Poly_used < MAX_COMPLEX) { p->isLarge = true; p->vetexes = &g_Complex_Poly_set[g_Complex_Poly_used][0]; g_Complex_Poly_used++; retVal = true; } return retVal; } void your_stuff_with_poly ( struct poly *p) { uint32_t poly_size = (p->isLarge == false) ? SIZE_A : SIZE_B; // your stuff with the correct size } 

这是一个为结构的静态“实例化”而设计的简单实现。 您还可以使用create / destroy函数增强代码,该函数可以跟踪哪些数组可以自由使用。

你的2号解决方案是正确的想法。 我不清楚为什么你认为这很难看。 也许这个美丽的实现会改变你的想法。

您可以通过将基础结构作为inheritance结构的第一个成员来实现单inheritance。 然后可以使用指向基类型的指针来引用inheritance对象。

 typedef struct { short doc_type; short *array_ptr; // more array pointers } doc_base_t; typedef struct { doc_base_t base; // base.doc_type set 0 at initialization short array[SIZE_A]; // base.array_ptr initialized to point here //more arrays } doc_small_t; typedef struct { doc_base_t base; // base.doc_type set 1 at initialization short array[SIZE_B]; // base.array_ptr initialized to point here //more arrays } doc_large_t; void function( doc_base_t* document ) { if ( document->doc_type == 1) { // array size is large } else { // array size is small } //common code referencing arrays through doc_base_t->array_ptr } 

doc_base_tarray_ptr成员对于inheritance机制不是必需的。 但我特意为你的函数的“公共代码”部分添加了。 如果doc_base_t不包含array_ptr那么您可以根据base_type值将通用document转换为doc_small_tdoc_large_t类型。 但是,您可能需要为每个inheritance的类型使用不同的实现。 通过将array_ptr成员添加到doc_base_t我怀疑您可以为所有inheritance的类型编写一个通用实现。

因此,您将静态声明doc_small_tdoc_large_t所有实例。 初始化每个对象时,您将初始化base.doc_typebase.array_ptr成员。 然后,您将在调用function之前将两种类型的对象强制转换为doc_base_t。 (或者传递base成员的地址,这会产生相同的指针值。)

更新示例:

 static doc_small_t doc_small_instances[NUM_SMALL_INSTANCES]; static doc_large_t doc_large_instances[NUM_LARGE_INSTANCES]; // DocInit must be called once at startup to initialize all the instances. void DocInit() { int index; for (index = 0; index < NUM_SMALL_INSTANCES; index++) { doc_small_instances[index].base.doc_type = SMALL; doc_small_instances[index].base.array_ptr = doc_small_instances[index].array; } for (index = 0; index < NUM_LARGE_INSTANCES; index++) { doc_large_instances[index].base.doc_type = LARGE; doc_large_instances[index].base.array_ptr = doc_large_instances[index].array; } } // DocProcess processes one doc, large or small. void DocProcess(doc_base_t *document) { int index; short *array_member_ptr = document->array_ptr; int array_size = SMALL; if (document->doc_type == LARGE) { array_size = LARGE; } for (index = 0; index < array_size; index++) { // Application specific processing of *array_member_ptr goes here. array_member_ptr++; } } // ProcessAllDocs processes all large and small docs. void ProcessAllDocs(void) { int index; for (index = 0; index < NUM_SMALL_INSTANCES; index++) { DocProcess(&doc_small_instances[index].base); } for (index = 0; index < NUM_LARGE_INSTANCES; index++) { DocProcess(&doc_large_instances[index].base); } } 

使用malloc()或类似的动态分配方法很容易。 只需使用灵活的arrays成员:

 typedef struct { short isLarge; //set 0 at initialization . . . short array[SIZE_A]; short largeArray[]; } doc_t; 

分配“小结构”:

 doc_t *small = malloc( sizeof( *small ) ); small->isLarge = 0; 

要分配“大型结构”:

 doc_t *large = malloc( sizeof( *large ) + ( SIZE_B - SIZE_A ) * sizeof( large->largeArray[ 0 ] ); large->isLarge = 1; 

请注意,必须保持largeArray元素的最后一个 ,这意味着array元素必须是倒数第二个才能使其工作。

根据您自己的分配方式,这可能适用也可能不适用。

(它也有点像黑客,因为它依赖于能够通过在array上使用SIZE_A或更高的索引来访问largeArray的数据。这是访问其边界之外的对象…)