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

tom*_*gic 175 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;
}
Run Code Online (Sandbox Code Playgroud)

请记住,这是我正在研究的大型多文件项目的简化版本.目标是在目标文件中有一个常量,多个文件可用于初始化状态结构.由于它是一个资源有限的嵌入式目标,并且结构不是那么小,我不想要源的多个副本.我不想使用:

#define foo_init { 1, 2, 3 }
Run Code Online (Sandbox Code Playgroud)

我也在尝试编写可移植代码,所以我需要一个有效的C89或C99解决方案.

这是否与目标文件中的ORG有关?初始化变量进入一个ORG并通过复制第二个ORG的内容进行初始化?

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

AnT*_*AnT 253

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

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

另外,在C语言中的术语"恒定"是指文字常数(如1,'a',0xFF等等),枚举成员并且这样的运营商的结果sizeof.Const限定对象(任何类型)不是 C语言术语中的常量.它们不能用于具有静态存储持续时间的对象的初始值设定项,无论其类型如何.

例如,这不是常数

const int N = 5; /* `N` is not a constant in C */
Run Code Online (Sandbox Code Playgroud)

上面的内容N在C++中是常量,但在C中不是常数.所以,如果你尝试的话

static int j = N; /* ERROR */
Run Code Online (Sandbox Code Playgroud)

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

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

  • `enum {N = 5};`是一种不被人理解的方式来声明常量而不必求助于`#define`. (12认同)
  • +5 很好的解释,但令人惊讶的是,这个程序在 ideone 上编译得很好:http://ideone.com/lx4Xed。是编译器错误还是编译器扩展?谢谢 (2认同)
  • @meet:我不知道ideone在底层使用的编译器选项的组合,但是它们的结果通常比描述更奇怪.我尝试在Coliru(http://coliru.stacked-crooked.com/a/daae3ce4035f5c8b)上编译此代码,并且无论我使用什么C语言方言设置,它都会得到预期的错误.我不会在GCC的网站上看到类似C语言扩展名的内容.换句话说,我不知道它是如何以及为什么在ideone中编译的.即使它编译为语言扩展,它仍应在C中生成诊断消息. (2认同)
  • @PravasiMeet"ideone"根本不显示编译器产生的许多诊断消息,因此它不是一个非常好的站点,用于确定代码是否正确. (2认同)
  • 我发现了一些有趣的事情。如果 ptr 是在函数内部定义的静态指针,则这是错误:`static int* ptr = malloc(sizeof(int)*5);` 但这不是错误:`static int* ptr; ptr = malloc(sizeof(int)*5);` :D (2认同)

R S*_*hko 73

这是语言的限制.在第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;
}
Run Code Online (Sandbox Code Playgroud)


Zam*_*man 11

2021:对于因arm-none-eabi-gcc.exeSTM32 MCU 上的编译错误而到达此帖子的人:
将您的工具链更改为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;
}
Run Code Online (Sandbox Code Playgroud)

arm-none-eabi-gcc.exeingnu-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


ach*_*ora 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;
}
Run Code Online (Sandbox Code Playgroud)