Инициализация глобальной структуры в C - PullRequest
15 голосов
/ 26 марта 2010

Каков наилучший способ выполнить следующее в C?

#include <stdio.h>

struct A
{
    int x;
};

struct A createA(int x)
{
    struct A a;
    a.x = x;
    return a;
}

struct A a = createA(42);

int main(int argc, char** argv)
{
    printf("%d\n", a.x);
    return 0;
}

Когда я пытаюсь скомпилировать приведенный выше код, компилятор сообщает об ошибке:

«Элемент инициализатора не является постоянным»

Вот плохая строка:

struct A a = createA(42);

Может кто-нибудь объяснить, что не так? Я не очень опытный в C. Спасибо!

Ответы [ 6 ]

16 голосов
/ 26 марта 2010
struct A a = { .x = 42 };

Больше участников:

struct Y {
    int r;
    int s;
    int t;
};

struct Y y = { .r = 1, .s = 2, .t = 3 };

Вы могли бы также сделать

struct Y y = { 1, 2, 3 };

То же самое работает для профсоюзов, и вам не нужно включать всех членов или даже ставить их в правильном порядке.

14 голосов
/ 26 марта 2010

Почему бы не использовать статическую инициализацию?

struct A a = { 42 };
4 голосов
/ 26 марта 2010

Проблема здесь в том, что глобальные / файловые статические переменные в C должны иметь значение, известное во время компиляции. Это означает, что вы не можете использовать пользовательскую функцию для инициализации значения. Это должно быть постоянное выражение

2 голосов
/ 29 апреля 2015

Для любознательных людей, которые также используют MSVC:

В C можно запускать функции инициализации перед main так же, как это возможно в C ++ (конечно, как бы C ++ это делал, если это было невозможно в C), однако это может несколько запутать, если вы Я не читал, как работает ваша библиотека времени выполнения.

Короче говоря:

#pragma section(".CRT$XIU",long,read)

int
init_func ()
{
// initialization

return 0; // return 0 is mandatory
}

__declspec(allocate(".CRT$XIU"))
int (*global_initializer)() = init_func;

Так что это не такой компактный исходный текст, как в C ++, но это можно сделать. Кроме того, перед использованием я рекомендую сначала понять формат PE, а затем прочитать crt \ src \ crt0.c и crt \ src \ crt0dat.c (поиск _cinit в обоих файлах) в каталоге установки MSVC, чтобы вы знали, что происходит.

2 голосов
/ 26 марта 2010

Вы не можете вызывать функции при статической инициализации подобным образом. В вашем примере вы можете просто использовать:

struct A a = {42};

Если у вас более сложная установка, вам потребуется обеспечить функцию создания библиотеки и уничтожения библиотеки, которую вы заставляете вызывать пользователи вашей библиотеки (при условии, что вы хотите быть переносимой), или вам придется использовать C ++ и взять преимущество конструкторов / деструкторов, или вам придется воспользоваться нестандартным и непереносимым __attribute __ ((constructor)), чтобы создать функцию, запускаемую при запуске, для ее инициализации.

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

class A
{
   A(){
      // can do initialization in the constructor
   }
   // ...
};

A a;

Однако, если вам нужно придерживаться чистого C, переносимая вещь, которую нужно сделать, это использовать что-то вроде:

typedef void* mylibrary_attr_t;
typedef void* mylibrary_t;

#ifdef __cplusplus
#   define EXTERNC extern "C"
#else
#   define EXTERNC
#endif

EXTERNC int mylibrary_attr_init(mylibrary_attr_t*);
EXTERNC int mylibrary_attr_setparam1(mylibrary_attr_t,int);
EXTERNC int mylibrary_attr_setparam2(mylibrary_attr_t,double);
// .. more functions for various attributes used by library
EXTERNC void mylibrary_attr_destroy(mylibrary_attr_t*);

EXTERNC int mylibrary_init(mylibrary_t*,mylibrary_attr_t);
EXTERNC void mylibrary_destroy(mylibrary_t*);

// functions that use mylibrary_t
// ...

По сути, в приведенном выше примере вы бы инициализировали вашу библиотеку с помощью mylibrary_init и разобрали бы вашу библиотеку с помощью mylibrary_destroy. Для функций, использующих вашу библиотеку, потребуется инициализированный экземпляр mylibrary_t, и поэтому лицо, создавшее основную функцию, будет отвечать за вызов mylibrary_init. Также полезно сделать функцию инициализации зависимой от параметра «атрибутов», который может быть заменен на 0 или NULL по умолчанию. Таким образом, если вы расширяете свою библиотеку и вам необходимо принять параметры конфигурации, она доступна вам. Это скорее дизайн, чем технический подход.

1 голос
/ 26 марта 2010

Вы не можете вызвать функцию как инициализатор. Вам нужно позвонить в main.

...