1

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

我想把它们放在闪存中,所以我想把它们放在静态存储的 const 对象中(它们将存储在闪存上)并让编译器完成它的工作。

我正在研究嵌入式环境,其中 ROM==flash== 我无法物理更改的数据。我的内存很少,当然不足以存储我的所有数据。可以告诉 GCC 将存储静态 const 数据毫无问题地放入 ROM。

数据不能动态构建,因为它应该保留在闪存中。

我目前正在使用具有 4.8 gcc 的 C(不是 C++),并且不介意使用 GCCisms。

但是,我不断遇到错误消息,例如:

  • 初始化元素不是常量
  • 不兼容的指针类型
  • ...

与最近的 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。

4

3 回答 3

2

您应该使用const-qualified 指针而不是灵活的数组成员。

有一些示例代码:

#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 };
于 2014-09-03T20:48:14.630 回答
1

假设您有大量的每种类型的struct,可能值得您花时间编写代码生成器。基本上,您定义了一种人类可读且非常易于解析的语法。然后编写一个代码生成器,使用该语法将其转换为完全不可读的 C 代码。最后,将该 C 代码编译到项目中。(您还应该将该 C 代码编译为验证程序,以确保您的代码生成器中没有任何错误。)

让我举例说明。首先,这里是结构定义。请注意,我在 and 中添加了必要的counttoAconst关键字。BC

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关键字;)

于 2014-09-03T22:02:49.827 回答
0

对于此类配置任务,我会考虑使用 JSON / BSON 。

或任何其他设置类似的格式。包括像proto-buffers这样的东西。

于 2014-09-03T20:30:24.367 回答