C: инициализация глобальных констант с внешней связью

Я хочу статически инициализировать структуру, которая будет видна из всех файлов, содержащих некоторый заголовок.

Фактическое намерение состоит в том, чтобы запускать списки функций, указанные во время компиляции, поэтому я хочу инициализировать статически. Я хотел бы поместить списки туда, где они должны быть, в файле, в котором они объявлены или определены.

Надуманный пример:

общий.ч:

struct Widget { int (*a)(); };

фу.ч:

int oof1(void);
int oof2(void);

foo.c:

#include "foo.h"

int oof1(void) { return 1; }
int oof2(void) { return 2; }

struct Widget foo_widgets[] = { {oof1}, {oof2} };

бар.с:

#include "foo.h"
#include "baz.h"

struct Widget *foo_widgets;
struct Widget *baz_widgets;

struct WidgetsContainer {
    struct Widget *widget_list;
} wlists[] =
    {
        { foo_widgets },
        { baz_widgets }
    };

void usage(void) { ... ; process (wlists[i].widget_list); ... }

Это, очевидно, не работает, потому что «элемент инициализатора не является константой» - это потому, что когда компилятор переводит bar.c, он думает, что не знает местоположение foo_widgets (или bar_widgets).

Но так как bar.c #includes foo.h в любом случае, он всегда компилируется вместе с foo.c:

gcc foo.c baz.c bar.c

Поэтому я надеюсь, что есть способ отразить это в исходном коде.

Я не могу объявить foo_widgets в foo.h, потому что тогда я не смогу инициализировать его, не определяя его несколько раз (поскольку foo.h включен более чем в один файл).

Неэлегантный обходной путь

фу.ч:

...

Widget *get_foos(void) { return foo_widgets; }

бар.с:

...

struct Widget_lists {
    struct Widget (*widget_list)();
} wlist[] =
    {
        { get_foos },
        { get_bazes }
    };

void usage(void) { ...; process(wlist[i].widget_list()); ... }

Есть ли лучший способ?


person user234461    schedule 31.10.2013    source источник


Ответы (1)


Вы хотите иметь глобальную переменную, доступ к которой можно получить где угодно... Для этого вы должны объявить ее как внешнюю переменную в соответствующем заголовке.

Здесь это следует сделать следующим образом:

В foo.h:

/* ... */
int oof1(void);
int oof2(void);
extern int (*foo_widget)(void)[2];

В файле foo.c:

int (*foo_widget)(void)[2] = {{oof1}, {oof2}};

Таким образом, при включении "foo.h" переменная foo_widget будет известна (и ожидается, что она будет определена где-то еще - здесь в foo.c - ).

Чтобы быть более точным... Любой фрагмент кода, который должен использовать foo_widget, должен где-то содержать строку extern int (*foo_widget)(void)[2];, будь то во включенном заголовке (более разумный способ) или просто строка в начале файла .c.

Конечно, если вы не можете заранее знать количество виджетов, которые у вас могут быть, вам, вероятно, понадобится динамическая структура данных, такая как связанный список или дерево (упорядоченное и сбалансированное, если возможно ;)) для их хранения. Переменная extern, скорее всего, будет обычным указателем, динамически выделяемым при необходимости. Но строка definition по-прежнему необходима, поэтому в этом случае в соответствующем исходном файле может быть что-то вроде struct my_struct *pointer = NULL;.

Примечание. Я взял на себя смелость заменить ваш struct Widget обычным указателем на функцию, чтобы упростить инициализацию.

person Rerito    schedule 31.10.2013
comment
Спасибо :-) Не знал, что вам нужно указать размер массива как для объявления, так и для определения. Структура была необходима для хранения дополнительных данных, связанных с каждой функцией. - person user234461; 31.10.2013
comment
@user234461 user234461, конечно, инициализацию можно выполнить для членов структуры, просто это более утомительно, с этим я оставляю вас;) - person Rerito; 31.10.2013