尝试使用const初始化变量时,错误“初始化元素不是常量”

时间:2010-06-11 17:55:27

标签: c initialization

我在第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的内容进行初始化?

也许我只需要改变我的策略,并在启动时使用初始化功能完成所有副本。除非有其他想法吗?

7 个答案:

答案 0 :(得分:241)

在C语言中,具有静态存储持续时间的对象必须使用常量表达式或使用包含常量表达式的聚合初始值设定项进行初始化。

“大”对象永远不会是C中的常量表达式,即使该对象声明为const

此外,在C语言中,术语“常量”是指文字常量(如1'a'0xFF等等),枚举成员和sizeof等运算符的结果。在C语言术语中,const限定对象(任何类型)都是非常量。它们不能用于具有静态存储持续时间的对象的初始值设定项,无论其类型如何。

例如,这是 NOT 常量

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

上面的N在C ++中是常量,但它在C中不是常数。所以,如果你尝试做

static int j = N; /* ERROR */

您将得到相同的错误:尝试使用非常量初始化静态对象。

这就是为什么在C语言中我们主要使用#define来声明命名常量,并且还使用#define来创建命名聚合初始化器。

答案 1 :(得分:72)

这是语言的限制。在第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;
}

答案 2 :(得分: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;
}

答案 3 :(得分: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;
}

答案 4 :(得分:2)

2021 年:对于由于 STM32 MCU 上的 arm-none-eabi-gcc.exe 编译错误而到达此帖子的人:
将您的工具链更改为 gnu-tools-for-stm32.9-2020-q2-update

从 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 并且会编译。

有关更多信息,请参阅以下内容:
Why "initializer element is not a constant" is... not working anymore?

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69960#c18

答案 5 :(得分: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;
}

答案 6 :(得分:-2)

我在代码中遇到了这个错误,如下所示:

int A = 1;
int B = A;

解决办法是改成这个

int A = 1;
#define B A

编译器将内存中的位置分配给变量。第二个是尝试将第二个变量分配到与第一个相同的位置 - 这是没有意义的。使用宏预处理器解决了这个问题。