策略来声明复杂的C结构化const数据?

时间:2014-09-03 20:16:14

标签: c embedded c99 literals compound-literals

我有一个复杂的数据结构(有很多不完整的数组类型/异构长度的结构数组和指向结构数组结构的指针......)

我想将它们放在闪存中,所以我想把它们放在静态存储的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。

3 个答案:

答案 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代码编译成验证程序,以确保您的代码生成器中没有任何错误。)

让我举例说明。首先,这是结构定义。请注意,我已在countA中添加了constBC关键字。

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等内容。