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

187

Я получаю сообщение об ошибке в строке 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?

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

tomlogic
источник

Ответы:

269

В языке 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 */

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

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

Муравей
источник
2
+5 за приятное объяснение, но на удивление эта программа прекрасно работает на ideone: ideone.com/lx4Xed . Это ошибка компилятора или расширение компилятора? Спасибо
Деструктор
2
@meet: я не знаю, какую комбинацию опций компилятора ideone использует под капотом, но их результаты часто странны за пределами описания. Я попытался скомпилировать этот код на Coliru ( coliru.stacked-crooked.com/a/daae3ce4035f5c8b ) и получил ожидаемую ошибку для него независимо от того, какую настройку диалекта языка C я использовал. Я не вижу ничего похожего на веб-сайте GCC как расширение языка Си. Другими словами, я понятия не имею, как и почему он компилируется в ideone. Даже если он компилируется как расширение языка, он все равно должен выдавать диагностическое сообщение на языке C.
AnT
15
enum { N = 5 };недооценивается способ объявления констант без необходимости прибегать к #define.
ММ
2
@PravasiMeet "ideone" просто не отображает многие диагностические сообщения, которые генерирует компилятор, поэтому это не очень хороший сайт для определения правильности кода или нет.
ММ
1
Я узнал кое-что интересное. если ptr является статическим указателем, определенным внутри функции, это ошибка: static int* ptr = malloc(sizeof(int)*5);но это НЕ ошибка static int* ptr; ptr = malloc(sizeof(int)*5);:: D
aderchox
74

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

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

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

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

int main()
{
    foo_t my_foo = foo_init;
    return 0;
}
R Самуэль Клатчко
источник
Мне нравится, что вы цитировали спецификацию, но это не помогает мне понять, что мы должны делать или почему все так, как есть.
Эван Кэрролл
1
Похоже, что GCC 8.1 (и позже) реализовал некоторое расширение, как описано в этом ответе; он принимает static const int x = 3; static int y = x;.
Эрик Постпишил
5

Просто для иллюстрации путем сравнения и контрастирования Код взят с 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;
}
achoora
источник
2

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

#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;
}
valenumr
источник
5
Я не вижу переменную со статической продолжительностью хранения, которая инициализируется неконстантной здесь.
До свидания, SE
0

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;
}
xjtuecho
источник
В опубликованном вами коде str1нет выражения для пункта 6.7.9 «Инициализация» , пункт 4 : «Все выражения в инициализаторе для объекта, который имеет статическую или потоковую длительность хранения, должны быть константными выражениями или строковыми литералами».
Эндрю Генле