我有一个复杂的数据结构(有很多不完整的数组类型/异构长度的结构数组和指向结构数组结构的指针......)
我想将它们放在闪存中,所以我想把它们放在静态存储的const对象中(它们将存储在flash中)并让编译器完成它的工作。
我正在使用嵌入式环境,其中ROM == flash ==我无法实际更改的数据。我的内存很少,当然还不足以存储我的所有数据。可以告诉GCC将存储静态const数据放到ROM中没有问题。
无法动态构建数据,因为它应保留在闪存中。
我目前正在使用C(不是C ++),使用4.8 gcc,并且不介意使用GCC。
但是我一直在纠结错误消息,如:
与不同的非常近期的gcc版本不同,暗示此功能(混合复合文字,指定的初始化程序,...)是最近或不是主线。
请注意,此代码由程序(脚本)生成。
除了我一直在犯的错误(我可能更具体,并在那里寻求帮助),你会建议什么策略:
构建一个大的uint32_t datablob []并适当地投射我的结构(由于我的链接器将指定它最终会在何处结束,因此无法在对象之间存储指针)
任何其他选项?
(编辑:添加细节)
嗯,我的问题更多是关于通用策略,但这是一个例子:
struct A
{
int id;
int codes[];
};
struct B
{
int b_member;
struct A *a[]; // array of ptr to A objects
};
struct C
{
int c_member;
struct B *objects[]; // array of ptrs on B
};
const struct A rom_data = { .id=4, .codes = {1,2,3,4}}; // this is in ROM
int main(void) {}
我想像我为A一组C结构所做的那样声明。 (这意味着我不想复制数据,从磁盘或malloc中读取数据,只需声明一个包含数据的const。)
我对文字的所有例子都非常简单。
我的平台的细节是一个ARM微控制器,但我只想考虑一个const。
答案 0 :(得分:1)
您应该使用const
- 限定指针而不是灵活的数组成员。
有一些示例代码:
#include <stddef.h>
struct A
{
int id;
const int *codes;
};
struct B
{
int b_member;
const struct A *const *a;
};
struct C
{
int c_member;
const struct B *const *objects;
};
static const struct C ROOT = { 0, (const struct B *[]){
&(const struct B){ 0, (const struct A *[]){
&(const struct A){ 0, (const int []){ 1, 2, 3 } },
&(const struct A){ 1, (const int []){ 0 } },
} },
&(const struct B){ 42, NULL },
} };
正如评论中所提到的,似乎没有必要通过指针引用结构。这简化了代码:
#include <stddef.h>
struct A
{
int id;
const int *codes;
};
struct B
{
int b_member;
const struct A *a;
};
struct C
{
int c_member;
const struct B *objects;
};
static const struct C ROOT = { 0, (const struct B []){
{ 0, (const struct A []){
{ 0, (const int []){ 1, 2, 3 } },
{ 1, (const int []){ 0 } },
} },
{ 42, NULL },
} };
如果您想要或需要C90兼容性,您可以展平树并让生成脚本跟踪相应数组中的偏移量:
static const int ARRAY_OF_INT[] = {
1, 2, 3,
0,
};
static const struct A ARRAY_OF_A[] = {
{ 0, ARRAY_OF_INT + 0 },
{ 1, ARRAY_OF_INT + 3 },
};
static const struct B ARRAY_OF_B[] = {
{ 0, ARRAY_OF_A + 0 },
{ 42, NULL },
};
static const struct C ROOT = { 0, ARRAY_OF_B + 0 };
答案 1 :(得分:1)
假设每种类型struct
都有大量数据,那么编写代码生成器可能值得花时间。基本上,您定义的语法有点人性化,并且很容易解析。然后编写一个代码生成器,将该语法转换为完全不可读的C代码。最后,将C代码编译到项目中。 (您还应该将该C代码编译成验证程序,以确保您的代码生成器中没有任何错误。)
让我举例说明。首先,这是结构定义。请注意,我已在count
和A
中添加了const
到B
和C
关键字。
struct A
{
int id;
int count; // number of entries in the codes array
int codes[];
};
struct B
{
int b_member;
const struct A *a[];
};
struct C
{
int c_member;
const struct B *objects[];
};
这里是代码生成器的输入可能是什么
C hello 333
B 11
A 55 1 2 3
A 56 4 5 6 7
B 12
A 57 1 8
A 58 9
X
C world 444
B 17
A 73 20
A 74 21 22
A 75 23 24 25
X
以字母C
开头的行定义了顶级结构。 C
之后的字符串是结构的名称,后跟c_member
初始值设定项。以B
开头的行包含b_member
初始值设定项。以A
开头的行的id
后跟任意数量的codes
。带有X
的行表示C
结构的结束。
这是代码生成器将生成的C代码
const struct A A1 = { 55, 3, { 1, 2, 3 } };
const struct A A2 = { 56, 4, { 4, 5, 6, 7 } };
const struct A A3 = { 57, 2, { 1, 8 } };
const struct A A4 = { 58, 1, { 9 } };
const struct A A5 = { 73, 1, { 20 } };
const struct A A6 = { 74, 2, { 21, 22 } };
const struct A A7 = { 75, 3, { 23, 24, 25 } };
const struct B B1 = { 11, { &A1, &A2, NULL } };
const struct B B2 = { 12, { &A3, &A4, NULL } };
const struct B B3 = { 17, { &A5, &A6, &A7, NULL } };
const struct C hello = { 333, { &B1, &B2, NULL } };
const struct C world = { 444, { &B3, NULL } };
显然,困难的部分是编写解析器。如果您熟悉lex和yacc,它们可以让您的生活更轻松。就个人而言,我总是手工编写解析器/代码生成器。
一旦你编写了代码生成器,验证就成了下一个问题,因为很明显一个错误的代码生成器将是一个永无止境的噩梦。幸运的是,验证很容易。将自动生成的代码编译为打印结构的测试程序。除了空白区别之外,测试程序的输出应与原始输入相同。
以下代码演示了如何打印结构,以便输出与此帖子顶部的输入相匹配。 (虽然代码有点难以阅读,但这主要是由于通用的A B C
结构名称。更多描述性的结构名称会使代码更容易阅读。)
void ShowStruct( const struct C *cptr, const char *name )
{
int i;
const struct B * const *bptr;
const struct B *bEntry;
const struct A * const *aptr;
const struct A *aEntry;
printf( "C %s %d\n", name, cptr->c_member );
for ( bptr = cptr->objects; *bptr != NULL; bptr++ )
{
bEntry = *bptr;
printf( "B %d\n", bEntry->b_member );
for ( aptr = bEntry->a; *aptr != NULL; aptr++ )
{
aEntry = *aptr;
printf( "A %d ", aEntry->id );
for ( i = 0; i < aEntry->count; i++ )
printf( " %d", aEntry->codes[i] );
printf( "\n" );
}
}
printf( "X\n\n" );
}
int main( void )
{
ShowStruct( &hello, "hello" );
ShowStruct( &world, "world" );
}
PS。感谢您提醒我为什么我总是试图避免使用const
关键字;)
答案 2 :(得分:0)
我会考虑使用JSON / BSON进行此类配置任务。
或任何其他类似设置的格式。包括proto-buffers等内容。