Инициализация глобальной структуры в C

Каков наилучший способ выполнить следующее в 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. Спасибо!

Ответы

Ответ 1

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

struct A a = { 42 };

Ответ 2

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

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

Ответ 3

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

Ответ 4

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

struct A a = {42};

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

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

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. Также полезно сделать функцию инициализации зависимой от параметра "attributes", который может быть заменен на 0 или NULL по умолчанию. Таким образом, если вы расширяете свою библиотеку и должны принимать параметры конфигурации, она доступна вам. Тем не менее, это больше, чем технический подход.

Ответ 5

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

В C можно запускать функции инициализации до main так же, как это возможно в С++ (конечно же, как С++ сделает это, если это невозможно в 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;

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

Ответ 6

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