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;
如果isLarge
为0
,则设置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_t
的array_ptr
成员对于inheritance机制不是必需的。 但我特意为你的函数的“公共代码”部分添加了。 如果doc_base_t
不包含array_ptr
那么您可以根据base_type
值将通用document
转换为doc_small_t
或doc_large_t
类型。 但是,您可能需要为每个inheritance的类型使用不同的实现。 通过将array_ptr
成员添加到doc_base_t
我怀疑您可以为所有inheritance的类型编写一个通用实现。
因此,您将静态声明doc_small_t
和doc_large_t
所有实例。 初始化每个对象时,您将初始化base.doc_type
和base.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
的数据。这是访问其边界之外的对象…)