Ошибка "элемент инициализатора не является константой" при попытке инициализировать переменную с помощью const

Я получаю сообщение об ошибке в строке 6 (инициализация my_foo для foo_init) следующей программы, и я не уверен, что понимаю почему.

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

Имейте в виду, что это упрощенная версия большого многофайлового проекта, над которым я работаю. Цель состояла в том, чтобы в объектном файле была одна константа, которую несколько файлов могли бы использовать для инициализации структуры состояний. Так как это встроенная цель с ограниченными ресурсами и структура не такая маленькая, я не хочу иметь несколько копий исходного кода. Я бы предпочел не использовать:

#define foo_init { 1, 2, 3 }

Я также пытаюсь написать переносимый код, поэтому мне нужно решение, которое действительно C89 или C99.

Связано ли это с ORG в объектном файле? Что инициализированные переменные входят в одну ORG и инициализируются копированием содержимого второй ORG?

Может быть, мне просто нужно изменить свою тактику и сделать так, чтобы функция инициализации делала все копии при запуске. Разве есть другие идеи там?

8 ответов

Решение

В языке C объекты со статической длительностью хранения должны быть инициализированы с помощью константных выражений или с помощью агрегатных инициализаторов, содержащих константные выражения.

"Большой" объект никогда не является константным выражением в C, даже если объект объявлен как const,

Более того, в языке C термин "константа" относится к буквальным константам (например, 1, 'a', 0xFF и т. д.), перечислимых членов и результатов таких операторов, как sizeof, Константные объекты (любого типа) не являются константами в терминологии языка Си. Их нельзя использовать в инициализаторах объектов со статической продолжительностью хранения независимо от их типа.

Например, это НЕ константа

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

Выше N будет константой в C++, но не является константой в C. Так что, если вы попытаетесь сделать

static int j = N; /* ERROR */

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

Это причина, почему в языке Си мы преимущественно используем #define объявить именованные константы, а также прибегнуть к #define создать именованные агрегатные инициализаторы.

Это ограничение языка. В разделе 6.7.8/4:

Все выражения в инициализаторе для объекта со статической продолжительностью хранения должны быть константными выражениями или строковыми литералами.

В разделе 6.6 спецификация определяет, что должно считаться константным выражением. Нет, где говорится, что переменная const должна считаться константным выражением. Это допустимо для компилятора, чтобы расширить это (6.6/10 - An implementation may accept other forms of constant expressions) но это ограничит мобильность.

Если вы можете изменить my_foo так что у него нет статического хранилища, все будет в порядке:

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

2021: Для тех, кто дошел до этого поста из-за ошибки компиляции на микроконтроллерах STM32:
измените свою цепочку инструментов на.

Начиная с GCC V8.1+, поддерживается инициализатор вложенных констант, и приведенный ниже код будет скомпилирован.

      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 в gnu-tools-for-stm32.7-2018-q2-update основан на gcc v7.3.1и приведенный выше код не будет компилироваться! Но gnu-tools-for-stm32.9-2020-q2-update использует gcc v9.3.1 и будет компилировать.

Для получения дополнительной информации см. Следующее:
Почему «элемент инициализатора не является константой» ... больше не работает?
и
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69960#c18

Просто для иллюстрации путем сравнения и контрастирования Код взят с http://www.geeksforgeeks.org/g-fact-80//Код не работает в gcc и передается в 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;
    }

Это немного устарело, но я столкнулся с похожей проблемой. Вы можете сделать это, если вы используете указатель:

#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;
}
  1. В основном существует два вида инициализации: во время компиляции и во время выполнения.
  2. Инициализация переменной статического хранилища относится к инициализации времени компиляции. Обратите внимание, что переменная static-storage включает:
  • глобальная переменная без ключевого слова static
  • глобальная переменная со статическим ключевым словом
  • локальная переменная с ключевым словом static

Но какой принцип стоит за этим правилом? На мой взгляд, это просто объяснить. Перед завершением компиляции значения этих переменных будут сохранены в исполняемый файл. И в это время ни один код не может работать!

Gcc 7.4.0 не может компилировать коды, как показано ниже:

#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: ошибка: элемент инициализатора не является постоянным const char * str2 = str1;

Фактически, строка "const char *" не является константой времени компиляции, поэтому она не может быть инициализатором. Но строка "const char * const" является константой времени компиляции, она должна быть инициализатором. Думаю, это небольшой недостаток CLang.

Имя функции, конечно же, является константой времени компиляции, поэтому этот код работает:

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

У меня была эта ошибка в коде, которая выглядела так:

      int A = 1;
int B = A;

Исправление состоит в том, чтобы изменить его на это

      int A = 1;
#define B A

Компилятор назначает переменной место в памяти. Второй пытается назначить вторую переменную тому же месту, что и первая, что не имеет смысла. Использование препроцессора макросов решает проблему.

Другие вопросы по тегам