Помилка "елемент ініціалізатора не є постійним" при спробі ініціалізації змінної з 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?

Можливо, мені просто потрібно змінити свою тактику і мати функцію ініціалізації робити всі копії при запуску. Якщо немає інших ідей там?

Відповіді:


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 потрібно вважати постійним виразом. Компілятор законно розширити цю функцію ( 6.6/10 - An implementation may accept other forms of constant expressions), але це обмежить переносимість.

Якщо ви можете змінити, my_fooщоб він не мав статичного сховища, вам буде добре:

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

Мені подобається, що ти цитував специфікацію, але це не допомагає мені зрозуміти, що ми маємо робити або чому все так, як вони є.
Еван Керролл

1
Здається, GCC 8.1 (і пізніші версії) впровадив деяке розширення, як описано в цій відповіді; це приймає static const int x = 3; static int y = x;.
Ерік Postpischil

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

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

5
Я не бачу змінної aa зі статичною тривалістю зберігання, яка тут ініціалізується непостійною.
Прощай 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;
}

У розміщеному вами коді str1не є виразом у 6.7.9 ініціалізації , параграф 4 : "Усі вирази в ініціалізаторі для об'єкта, що має статичну тривалість або тривалість зберігання, повинні бути постійними виразами або рядковими буквами".
Ендрю Генле
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.