Получение указателя на созданную структуру на этапе компиляции

0shn1x

Требуется создать на этапе компиляции односвязный список из структур, имеющих следующий тип:

typedef struct word{
    struct word *previous;
    char *name;
    ******* flag;
    void(*xt)();
}word;

То есть, первая структура создается как {0, "plus", 0, add), то вторая уже должна инициализироваться как {previous, "minus", 0, sub} , где previous - адрес созданной до этого структуры. При этом хранить адрес структуры в переменной невыгодно, т.к. на каждую структуру придётся создавать свою переменную, а количество памяти сильно ограничено. Поэтому хочется каким-то образом сохранить адрес в памяти, чтобы затем его использовать, при этом не выделяя под него дополнительную SRAM.

Попробовал сделать что-то вроде:

#define STRUCT {0, "+", 0, add}
word last_word = {&(STRUCT), "-", 0, sub};

Но компилятор, ожидаемо, ругается на эту строку, поскольку вместо STRUCT подставляется выражение, а не полученный адрес. Каким образом я могу получить доступ к памяти напрямую, чтобы занести в неё объекты?

3 ответа

0shn1x

Если заранее известно количество структур, а с памятью напряженка - я бы делал это массивом:

typedef struct word{
    // struct word *previous;
    char *name;
    ******* flag;
    void(*xt)();
} word;

word w[] = {
    { "plus", 0, add };
    { "minus", 0, sub };
    ...
};

При этом тратится меньше памяти, выше скорость обращения, все операции быстрее - словом, одни выгоды :)

Если позарез нужен список - то чтоб не плодить имя для каждой структуры, я бы опять же работал через массив:

typedef struct word{
    struct word *previous;
    char *name;
    unsigned char flag;
    void(*xt)();
} word;

word w[] = {
    { 0, "plus", 0, add },
    { &w[0], "minus", 0, minus },
    { &w[1], "product", 0, prod  }
    ...
};

По крайней мере, не придется придумывать новое имя для каждой из... сколько вы там сказали? 1024? структур.


0shn1x

Окей, если вы хотите выделить структуры во время компиляции, проще всего, наверное, будет такой код:

typedef struct word {
    struct word *previous;
    char *name;
    ******* flag;
    void(*xt)();
} word;

word w1 = { 0, "plus", 0, add };
word w2 = { &w1, "minus", 0, sub };

и так далее.

Это разместит данные в области для глобальных переменных. Привязка имени w1, w2 не создаёт проблем по идее, т. к. привязывает имя w1 к самой выделенной области памяти.

Если вы хотите выделить переменные в куче, вы не можете этого сделать на этапе компиляции (потому что вам нужен malloc). В этом случае вам нужно что-то такое:

typedef struct word {
    struct word *previous;
    char *name;
    ******* flag;
    void(*xt)();
} word;
word* root = malloc(sizeof(word));
*root = (word){ 0, "plus", 0, add };

word* prev = root;
root = malloc(sizeof(word));
*root = (word){ prev, "minus", 0, sub };

prev = root;
root = malloc(sizeof(word));
*root = (word){ prev, "times", 0, mult };

// ...

Проверка.

Если экономить память, то имеет смысл завести массив, как в ответе @Harry, и заменить указатель на индекс в массиве. Это будет в зависимости от размера таблицы один или два байта. Но нужно подобрать правильный порядок полей, минимизирующий sizeof(word).

typedef struct word
{
    void(*xt)();
    char *name;
    ******* index;
    ******* flag;
} word;

word words[] =
{
    { add,  "plus",  0, 0 },
    { sub,  "minus", 1, 0 },
    { mult, "times", 2, 0 },
    // ...
};

const ******* number_of_words = sizeof(words) / sizeof(word); // не занимает памяти

Имея индекс, легко переходить к предыдущему/следующему элементу.


0shn1x

Запросто

word *list = &(word) { &(word) { NULL, "plus", 0, add }, "minus", 0, sub };

Все.

Будучи расположенным в file scope такой список живет по правилам статических переменных (т.е. "вечно"). Будучи расположенным в block scope он живет по правилам обычных локальных переменных.

С точки зрения языка С, при расположении в file scope, все инициализаторы в данном примере являются константами (иначе бы код не скомпилировался вообще), т.е. значениями времени компиляции. Разумеется, с точки зрения деталей реализации, значение адресной константы может не сформироваться даже на этапе линковки, т.е. стать известным только в момент загрузки. А уж соответствует ли все это вашему "на этапе компиляции" - вам решать.

licensed under cc by-sa 3.0 with attribution.