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

8 minutos de lectura

avatar de usuario
tomlogic

Recibo un error en la línea 6 (inicializar my_foo a 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 más grande de varios archivos en el que estoy trabajando. El objetivo era tener una sola constante en el archivo de objeto, que varios archivos pudieran usar para inicializar una estructura de estado. Dado que es un destino 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 C89 o C99 válida.

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

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

  • Solucionado en gcc-8.1 y versiones posteriores, consulte la respuesta de @Zaman a continuación para obtener más detalles.

    – bruce

    1 oct 2021 a las 8:07

avatar de usuario
Hormiga

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

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

Además, en lenguaje C, el término “constante” se refiere a constantes literales (me gusta 1, 'a', 0xFF etcétera), miembros de la enumeración y resultados de operadores tales como sizeof. Los objetos calificados const (de cualquier tipo) son no constantes en la terminología del lenguaje C. No se pueden utilizar en inicializadores de objetos con duración de almacenamiento estático, independientemente de su tipo.

Por ejemplo, esto es NO una constante

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

Lo anterior N serí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, usamos predominantemente #define para declarar constantes con nombre, y también recurrir a #define para crear inicializadores agregados con nombre.

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

    – Destructor

    21 de junio de 2015 a las 6:25

  • @meet: no sé qué combinación de opciones de compilador usa ideone debajo del capó, pero sus resultados a menudo son extraños más allá de la 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 usé. No veo nada parecido en el sitio web de GCC como una extensión del 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 lenguaje, aún debería generar un mensaje de diagnóstico en C.

    – Ant

    21 de junio de 2015 a las 6:38


  • enum { N = 5 }; es una forma subestimada de declarar constantes sin tener que recurrir a #define.

    –MM

    10 de diciembre de 2015 a las 2:51


  • @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

    10 de diciembre de 2015 a las 2:51

  • 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 esto NO es un error: static int* ptr; ptr = malloc(sizeof(int)*5); 😀

    – aderchox

    5 de enero de 2019 a las 19:33

avatar de usuario
Samuel Klatchko

Es una limitación del lenguaje. 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ática 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 dice que una variable const debe considerarse una expresión constante. Es legal que un compilador amplíe esto (6.6/10 - An implementation may accept other forms of constant expressions) pero eso limitaría la portabilidad.

si puedes cambiar my_foo por lo que no tiene 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

    23 ago 2018 a las 22:30

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

    –Eric Postpischil

    17 de febrero de 2020 a las 16:24


2021: Para quien llega a este puesto por arm-none-eabi-gcc.exe error de compilación en MCU STM32:
Cambie su cadena de herramientas a gnu-tools-for-stm32.9-2020-q2-update.

A partir de GCC V8.1+, se admite el inicializador constante anidado y se compilará el siguiente código.

const int a = 1;
const int b = a +1;

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

arm-none-eabi-gcc.exe en gnu-tools-for-stm32.7-2018-q2-update está basado en gcc v7.3.1 ¡y el código anterior no se compilará! Pero gnu-tools-for-stm32.9-2020-q2-update usos gcc v9.3.1 y compilará.

Para obtener más información, consulte estos:
¿Por qué el “elemento inicializador no es una constante”… ya no funciona?
y
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69960#c18

avatar de usuario
achora

Solo como ilustración por 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;
}

Esto es un poco viejo, pero me encontré con un problema similar. Puedes hacer esto si usas 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;
}

  • No veo una variable con una duración de almacenamiento estático que se inicialice con una no constante aquí.

    –Kami Kaze

    9 de febrero de 2017 a las 10:29

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 poder 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. Así 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;
}

  • No veo una variable con una duración de almacenamiento estático que se inicialice con una no constante aquí.

    –Kami Kaze

    9 de febrero de 2017 a las 10:29

Tuve este error en el código que se veía así:

int A = 1;
int B = A;

La solución es cambiarlo a esto.

int A = 1;
#define B A

El compilador asigna una ubicación en la memoria a una variable. El segundo es intentar asignar una segunda variable a la misma ubicación que la primera, lo que no tiene sentido. El uso del preprocesador de macros resuelve el problema.

  • Asignar el valor de una variable a otra es perfectamente normal. Usted dice que se está asignando una ubicación de memoria, pero no hay punteros aquí (sería diferente si tuviera int* o &A), a menos que, por supuesto, esté almacenando punteros como ints (es decir, el 1 almacenado en A se refiere a una dirección de memoria), que es un tema completamente diferente.

    – jacobq

    9 dic 2021 a las 16:35


¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad