Error "el elemento inicializador no es constante" al intentar inicializar la variable con const


187

Recibo un error en la línea 6 (inicializar my_foo en foo_init) del siguiente programa y no estoy seguro de entender por qué.

typedef struct foo_t {
    int a, b, c;
} foo_t;

const foo_t foo_init = { 1, 2, 3 };
foo_t my_foo = foo_init;

int main()
{
    return 0;
}

Tenga en cuenta que esta es una versión simplificada de un proyecto de varios archivos más grande en el que estoy trabajando. El objetivo era tener una sola constante en el archivo de objeto, que múltiples archivos podrían usar para inicializar una estructura de estado. Como es un objetivo incrustado con recursos limitados y la estructura no es tan pequeña, no quiero varias copias de la fuente. Prefiero no usar:

#define foo_init { 1, 2, 3 }

También estoy tratando de escribir código portátil, así que necesito una solución que sea válida C89 o C99.

¿Tiene esto que ver con los ORG en un archivo de objeto? ¿Que las variables inicializadas entran en un ORG y se inicializan copiando el contenido de un segundo ORG?

Tal vez solo necesite cambiar mi táctica y tener una función de inicialización para hacer todas las copias al inicio. ¿A menos que haya otras ideas por ahí?

Respuestas:


269

En lenguaje C, los objetos con una duración de almacenamiento estático deben inicializarse con expresiones constantes o con inicializadores agregados que contengan expresiones constantes.

Un objeto "grande" nunca es una expresión constante en C, incluso si el objeto se declara como const .

Por otra parte, en el lenguaje C, el término "constante" se refiere a constantes literales (como 1, 'a', 0xFFy así sucesivamente), miembros de enumeración, y los resultados de dichos operadores como sizeof. Los objetos calificados const (de cualquier tipo) no son constantes en la terminología del lenguaje C. No se pueden usar en inicializadores de objetos con una duración de almacenamiento estático, independientemente de su tipo.

Por ejemplo, esto NO es una constante

const int N = 5; /* `N` is not a constant in C */

Lo anterior Nsería una constante en C ++, pero no es una constante en C. Entonces, si intentas hacer

static int j = N; /* ERROR */

obtendrá el mismo error: un intento de inicializar un objeto estático con una no constante.

Esta es la razón por la cual, en lenguaje C, utilizamos predominantemente #definepara declarar constantes con nombre, y también recurrimos #definepara crear inicializadores agregados con nombre.


2
+5 por la buena explicación, pero sorprendentemente este programa se compila bien en ideone: ideone.com/lx4Xed . ¿Es el error del compilador o la extensión del compilador? Gracias
Destructor

2
@meet: No sé qué combinación de opciones de compilación utiliza ideone debajo del capó, pero sus resultados son a menudo extraños más allá de toda descripción. Intenté compilar este código en Coliru ( coliru.stacked-crooked.com/a/daae3ce4035f5c8b ) y obtuve el error esperado independientemente de la configuración del dialecto del lenguaje C que utilicé. No veo nada como eso en el sitio web de GCC como una extensión de lenguaje C. En otras palabras, no tengo idea de cómo y por qué se compila en ideone. Incluso si se compila como una extensión de idioma, aún debería generar un mensaje de diagnóstico en C.
ANT

15
enum { N = 5 };es una forma poco apreciada de declarar constantes sin tener que recurrir a ellas #define.
MM

2
@PravasiMeet "ideone" simplemente no muestra muchos de los mensajes de diagnóstico que produce el compilador, por lo que no es un sitio muy bueno para determinar si el código es correcto o no.
MM

1
He descubierto algo interesante. si ptr es un puntero estático definido dentro de una función, esto es un error: static int* ptr = malloc(sizeof(int)*5);pero NO es un error static int* ptr; ptr = malloc(sizeof(int)*5);:: D
aderchox

74

Es una limitación del idioma. En la sección 6.7.8 / 4:

Todas las expresiones en un inicializador para un objeto que tiene una duración de almacenamiento estático serán expresiones constantes o literales de cadena.

En la sección 6.6, la especificación define lo que debe considerarse una expresión constante. En ningún lugar indica que una variable constante debe considerarse una expresión constante. Es legal que un compilador extienda esto (6.6/10 - An implementation may accept other forms of constant expressions ) pero eso limitaría la portabilidad.

Si puede cambiar my_foopara que no tenga almacenamiento estático, estaría bien:

int main()
{
    foo_t my_foo = foo_init;
    return 0;
}

Me gusta que hayas citado la especificación, pero esto no me ayuda a entender lo que se supone que debemos hacer o por qué las cosas son como son.
Evan Carroll

1
Parece que GCC 8.1 (y posterior) ha implementado alguna extensión como se describe en esta respuesta; se acepta static const int x = 3; static int y = x;.
Eric Postpischil

5

Solo para ilustración mediante comparación y contraste El código es de http://www.geeksforgeeks.org/g-fact-80/ / El código falla en gcc y pasa en g ++ /

#include<stdio.h>
int initializer(void)
{
    return 50;
}

int main()
{
    int j;
    for (j=0;j<10;j++)
    {
        static int i = initializer();
        /*The variable i is only initialized to one*/
        printf(" value of i = %d ", i);
        i++;
    }
    return 0;
}

2

Esto es un poco viejo, pero me encontré con un problema similar. Puede hacer esto si usa un puntero:

#include <stdio.h>
typedef struct foo_t  {
    int a; int b; int c;
} foo_t;
static const foo_t s_FooInit = { .a=1, .b=2, .c=3 };
// or a pointer
static const foo_t *const s_pFooInit = (&(const foo_t){ .a=2, .b=4, .c=6 });
int main (int argc, char **argv) {
    const foo_t *const f1 = &s_FooInit;
    const foo_t *const f2 = s_pFooInit;
    printf("Foo1 = %d, %d, %d\n", f1->a, f1->b, f1->c);
    printf("Foo2 = %d, %d, %d\n", f2->a, f2->b, f2->c);
    return 0;
}

55
No veo una variable con una duración de almacenamiento estático que se inicializa por una no constante aquí.
Adiós SE

0

gcc 7.4.0 no puede compilar códigos de la siguiente manera:

#include <stdio.h>
const char * const str1 = "str1";
const char * str2 = str1;
int main() {
    printf("%s - %s\n", str1, str2);
    return 0;
}

constchar.c: 3: 21: error: el elemento inicializador no es constante const char * str2 = str1;

De hecho, una cadena "const char *" no es una constante de tiempo de compilación, por lo que no puede ser un inicializador. Pero una cadena "const char * const" es una constante de tiempo de compilación, debería ser un inicializador. Creo que este es un pequeño inconveniente de CLang.

El nombre de una función es, por supuesto, una constante de tiempo de compilación, por lo que este código funciona:

void func(void)
{
    printf("func\n");
}
typedef void (*func_type)(void);
func_type f = func;
int main() {
    f();
    return 0;
}

En el código que publicó, str1no es una expresión según 6.7.9 Inicialización , párrafo 4 : "Todas las expresiones en un inicializador para un objeto que tiene una duración de almacenamiento estático o de subproceso serán expresiones constantes o literales de cadena".
Andrew Henle
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.