【问题标题】:strategy to declare complex C structured const data?声明复杂的 C 结构化 const 数据的策略?
【发布时间】:2014-09-03 20:16:14
【问题描述】:

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

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

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

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

我目前使用的是 C(不是 C++),gcc 为 4.8,不介意使用 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。

【问题讨论】:

  • 没有看到一些示例代码,很难评论你看到的具体错误......
  • 我建议整理一个简化的示例来演示您遇到的问题。就目前而言,这个问题太宽泛了。
  • 当然,我的问题更多是关于使用的策略而不是具体错误,但我可以提供细节。
  • 初始化元素不是常量,至少是由于 char array[int* fromsomewhereelse] = "";
  • 自下而上存储结构并将指针转换为文件偏移量可能是一个合适的解决方案。

标签: c embedded c99 literals compound-literals


【解决方案1】:

您应该使用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 },
} };

正如 cmets 中提到的,似乎没有必要通过指针引用结构。这简化了代码:

#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 };

【讨论】:

  • @makapuf:你真的需要存储指向结构的指针,还是我们可以放弃那个级别的间接?即,单个结构是否出现在多个列表中?
  • @makapuf:您使用了指针数组,这意味着它们可以共享;我稍后会编辑
  • 是的,因为我没有找到将不同长度的数组存储在数组中的方法...
  • 好的,我终于混合了你的实现和我的代码。作为记录,我遇到的一些错误是由于对文字进行了多余的强制转换/限定符,从而产生了非常量错误(可能是通过创建中间文字,这使得封闭结构非常量)
【解决方案2】:

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

让我举例说明。首先,这里是结构定义。请注意,我在A 中添加了count,并根据需要在BC 中添加了const 关键字。

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

【讨论】:

  • 嗯,这是个好主意......以及项目来自哪里;)。我您写得很好的答案和想法就是我的意思——嗯,不是那么彻底——“请注意,此代码是由程序(脚本)生成的。”但是生成的代码很垃圾,所以我需要举一个简单的例子。 (我的代码是由 python 词法分析器/手动递归下降解析器生成的)。并且代码东西被命名为...字节码。
  • @makapuf 啊,我不知何故错过了那条线。哦,好吧,无论如何我都会留下答案。也许还没有考虑过代码生成器的人会偶然发现它。
【解决方案3】:

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

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

【讨论】:

  • 请注意,我在嵌入式空间工作时没有文件或 RAM(因此是 ROM 数据)
  • @makapuf BSON,存储在 ROM 中,是结构化数据,你可以像 JSON 一样访问:获取数组元素,在名称/值映射中按名称获取值等。跨度>
  • 当然,但是 C 库的大小(和内存成本)应该与访问 C 结构和数组的大小进行比较,即没有(除了开发人员时间,但平衡要高得多我的情况)。
  • @makapuf 你可能错过了 BSON 只是一种数据组织方式的事实。编写一个通过数据结构中的路径为您提供数据项的函数非常简单。类似get("A/B/C/12", &amp;data, &amp;datatype )
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多