2010-03-26 10 views
14

¿Cuál es la mejor manera de lograr lo siguiente en C?Inicializando una estructura global en 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; 
} 

Cuando intento compilar el código anterior, el compilador informa el siguiente error:

"elemento inicializador no es constante"

La mala línea es ésta:

struct A a = createA(42); 

¿Alguien puede explicar lo que está mal? No tengo mucha experiencia en C. ¡Gracias!

Respuesta

12

¿Por qué no utilizar la inicialización estática?

struct A a = { 42 }; 
+1

Más: No puede llamar a funciones para inicializar estáticamente los datos. Eso es lo que el error de tu compilador intenta decirte. – BjoernD

+0

Está bien, pero ¿qué ocurre si necesito anidar las inicializaciones? Como, digamos que una estructura B tiene una A en ella. ¿Puedo inicializar estáticamente B con un A estático inicializado? es decir struct B b = createB (createA (42)) – Scott

+2

Ah, no importa. Me lo imaginé. Aparentemente puedo hacer: struct B b = {{42}}; ¡Agradable! – Scott

1

No puede llamar a una función como un iniciador. Necesitas llamarlo adentro de main.

3

El problema aquí es que las variables globales/de archivos estáticos en C deben tener un valor conocido en tiempo de compilación. Esto significa que no puede usar una función definida por el usuario para inicializar el valor. Debe ser una expresión constante

2

No puede invocar funciones en inicialización estática como esa. En su ejemplo, puede simplemente usar:

 
struct A a = {42}; 

Si usted tiene una configuración más complicada, que se necesitan para proporcionar una construcción de biblioteca y la función destrucción biblioteca que obligar a los usuarios de la biblioteca de llamar (asumiendo que usted quiere ser portátil), o tendrá que usar C++ y aprovechar los constructores/destructores, o tendrá que aprovechar el __attribute __ ((constructor) no estándar y no portátil para crear una función que se ejecuta al inicio para inicializarlo

Si usted tiene una configuración más complicada, creo firmemente estaría a favor de que utiliza C++:

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

A a; 

Sin embargo, si necesita seguir con C puro, lo portátil que hacer es usar algo como:

 
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 
// ... 

Básicamente, en lo anterior, inicializaría su biblioteca con mylibrary_init y desmontaría su biblioteca utilizando mylibrary_destroy. Las funciones que utilizan su biblioteca requerirían una instancia inicializada de mylibrary_t, por lo que la persona que creó la función principal sería responsable de invocar mylibrary_init. También es bueno hacer que la función de inicialización dependa de un parámetro de "atributos" que se puede reemplazar por 0 o NULL como valor predeterminado. De esta forma, si extiende su biblioteca y necesita aceptar opciones de configuración, estará disponible para usted. Sin embargo, es más un diseño que un enfoque técnico.

15
struct A a = { .x = 42 }; 

Más miembros:

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

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

También podría hacer

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

Lo mismo funciona para los sindicatos, y usted no tiene que incluir todos los miembros o incluso ponen ellos en el orden correcto.

2

Para las personas curiosas que también utilizan MSVC:

En C es posible ejecutar funciones de inicialización antes de principal al igual que es posible en C++ (por supuesto que es, cómo sería C++ hacerlo si no fuera posible en C), sin embargo puede ser algo confuso si no ha leído cómo funciona su biblioteca de tiempo de ejecución.

Para resumir:

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

así que no es tan compacta como fuente de texto en C++, pero se puede hacer. Además, antes de usar, recomiendo primero entender el formato PE, luego lea crt \ src \ crt0.c y crt \ src \ crt0dat.c (busque _cinit en ambos archivos) en su directorio de instalación de MSVC para que sepa qué está pasando.

+0

¿Hay un equivalente de gcc? – rkellerm

+0

@rkellerm respuesta fácil: no lo creo. respuesta difícil: hay más de un libcs ​​para gcc. Creo que la forma más fácil de averiguarlo sería buscar "_start" en el repositorio de código fuente de tu libc. Esa es la primera función llamada en el duende. Por lo general, el archivo que contiene este símbolo se llama crt0something.c. newlib, por ejemplo, no tiene dicha inicialización, establece argc, argv y va para main. Pero no sería tan difícil agregar un código para hacer eso. La compilación de libc suele ser bastante dolor de cabeza. – Pyjong

+0

@rkellerm ok bud. Hoy realmente necesitaba esta característica, así que husmeé en glibc 2.4. Resulta que es incluso más fácil que con el compilador de MS. En caso de que todavía desee saber, simplemente declare su función como nula __atributo __ ((constructor)) initializer_fn() y allí está su constructor C premain;) – Pyjong

Cuestiones relacionadas