2014-09-03 6 views
0

I имеют сложную структуру данных (с большим количеством неполных типов массивов/гетерогенные массивы длины структур и указатель на структуры в массивы структур ...)Стратегия объявления сложных C-структурированных данных const?

Я хотел бы поставить тех, во флэш-памяти, так что я думал о том, чтобы поместить их в объекты const статического хранилища (они будут храниться на флэш-памяти), и пусть компилятор выполнит свою работу.

Я работаю над встроенной средой, где ROM == flash == данные, которые я не могу физически изменить. У меня мало оперативной памяти и, конечно, недостаточно для хранения всех моих данных. GCC можно сказать, чтобы статические данные const const хранить в ROM без проблем.

Данные не могут быть построены динамически, так как они должны оставаться во вспышке.

В настоящее время я использую C (не C++) с 4,8 gcc и не против использования GCCisms.

Однако я продолжаю encoutering сообщения об ошибках, как:

  • инициализатор элемент не является постоянная
  • несовместимых типов указателей
  • ...

по-разному с разными самым последними релизами 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) {} 

Я хотел бы заявить, что я сделал для Массив структур C. (Это означает, что я не хочу копировать данные, читать их с диска или malloc, просто объявлять константу с данными на месте.)

Все примеры, которые я имею о литералах, очень просты.

Специфика моей платформы - микроконтроллер ARM, но просто подумайте, хочу ли я объявить const.

+1

Не видя какого-либо примера кода, сложно комментировать определенные ошибки, которые вы видите ... –

+0

Предлагаю собрать упрощенный пример, демонстрирующий, что у вас проблемы. В его нынешнем виде вопрос слишком широк. – user3386109

+0

уверен, что мой вопрос касался скорее стратегии использования, чем конкретных ошибок, но я мог бы предоставить специфику. – makapuf

ответ

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

Как уже упоминалось в комментариях, это кажется излишним ссылаться на структуры с помощью указателей. Это упрощает код:

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

wow, lotsa consts! возможно, я пропустил структуру X * [] decls. – makapuf

+0

@makapuf: вам действительно нужно хранить указатели на структурах, или мы можем отказаться от этого уровня косвенности? т. е. появляется ли одна структура в нескольких списках? – Christoph

+0

Нет, они не разделены, поэтому я использовал массивы – makapuf

0

Я хотел бы рассмотреть JSON/BSON для таких задач конфигурации.

Или любые другие настройки, аналогичные форматам. Включая такие вещи, как proto-buffers.

+0

Обратите внимание, что у меня нет файлов или ОЗУ, поскольку я работаю во встроенном пространстве (следовательно, данные ПЗУ) – makapuf

+0

@makapuf BSON, хранящийся в ПЗУ, представляет собой структурированные данные, к которым вы можете обращаться так же, как JSON: получить элемент массив, получить значение по имени в карте имени/значения и т. д. –

+0

уверен, но размер библиотек C (и стоимость в памяти) следует сравнить с размером доступа к структуре и массиву C, т. е. никому (кроме времени разработчика, но баланс в моем случае намного выше). – makapuf

1

Если предположить, что у вас есть большое количество каждого типа struct, возможно, стоит потратить время на создание генератора кода. В принципе, вы определяете синтаксис, который читается человеком и очень легко разбирается. Затем напишите генератор кода, который принимает этот синтаксис, преобразует его в полностью нечитаемый код 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;)

+0

, это хорошая идея ... и где проект исходит из;). Я твой хорошо написанный ответ и идея - это то, что я имел в виду ... ну, не так тщательно - «Обратите внимание, что этот код генерируется программой (скриптом)». Однако сгенерированный код был дерьмом, поэтому мне нужно было сделать простой пример. (мой код генерируется парсером lexer/hand рекурсивного спуска python). и код имеет имя ... байт-код. – makapuf

+0

@ makapuf А, я так или иначе пропустил эту линию. Ну, я останусь в любом случае. Может быть, кто-то, кто еще не рассмотрел генератор кода, наткнется на него. – user3386109