尝试使用 const 初始化变量时出现错误“初始化元素不是常量”
Posted
技术标签:
【中文标题】尝试使用 const 初始化变量时出现错误“初始化元素不是常量”【英文标题】:Error "initializer element is not constant" when trying to initialize variable with const 【发布时间】:2011-03-02 18:56:03 【问题描述】:我在以下程序的第 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 的内容来初始化?
也许我只需要改变我的策略,并让一个初始化函数在启动时完成所有副本。除非有其他想法?
【问题讨论】:
已在 gcc-8.1 及更高版本中修复,有关详细信息,请参阅下面的@Zaman 答案。 【参考方案1】:在 C 语言中,具有静态存储持续时间的对象必须使用 常量表达式 或包含常量表达式的聚合初始化器进行初始化。
“大”对象在 C 中绝不是常量表达式,即使该对象被声明为 const
。
此外,在 C 语言中,术语“常量”是指文字常量(如1
、'a'
、0xFF
等)、枚举成员以及此类的结果运算符为sizeof
。 const 限定的对象(任何类型)在 C 语言术语中不是常量。它们不能用于具有静态存储持续时间的对象的初始化程序,无论其类型如何。
例如,这是不是常量
const int N = 5; /* `N` is not a constant in C */
上面的N
在 C++ 中是一个常量,但在 C 中它不是一个常量。所以,如果你尝试这样做
static int j = N; /* ERROR */
你会得到同样的错误:试图用一个非常量初始化一个静态对象。
这就是为什么在 C 语言中,我们主要使用 #define
来声明命名常量,并使用 #define
来创建命名聚合初始值设定项。
【讨论】:
+5 很好的解释,但令人惊讶的是,这个程序在 ideone 上编译得很好:ideone.com/lx4Xed。是编译器错误还是编译器扩展?谢谢 @meet:我不知道 ideone 在后台使用了哪些编译器选项组合,但它们的结果通常很奇怪,无法描述。我尝试在 Coliru (coliru.stacked-crooked.com/a/daae3ce4035f5c8b) 上编译此代码并得到预期的错误,无论我使用什么 C 语言方言设置。我没有看到 GCC 的网站上列出的任何类似 C 语言扩展的内容。换句话说,我不知道它是如何以及为什么在 ideone 中编译的。即使它编译为语言扩展,它仍应在 C 中生成诊断消息。enum N = 5 ;
是一种被低估的声明常量的方式,而无需求助于#define
。
@PravasiMeet "ideone" 根本不显示编译器生成的许多诊断消息,因此它不是一个用于确定代码是否正确的好网站。
我发现了一些有趣的东西。如果 ptr 是在函数内部定义的静态指针,这是错误:static int* ptr = malloc(sizeof(int)*5);
但这不是错误:static int* ptr; ptr = malloc(sizeof(int)*5);
:D【参考方案2】:
这是语言的限制。在第 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;
【讨论】:
我喜欢你引用的规范,但这并不能帮助我理解我们应该做什么或为什么事情会这样。 看来 GCC 8.1(及更高版本)已经实现了一些扩展,如本答案中所述;它接受static const int x = 3; static int y = x;
。【参考方案3】:
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;
gnu-tools-for-stm32.7-2018-q2-update
中的arm-none-eabi-gcc.exe
是基于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
【讨论】:
【参考方案4】:仅通过比较和对比来说明 代码来自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;
【讨论】:
【参考方案5】:这有点旧,但我遇到了类似的问题。如果你使用指针,你可以这样做:
#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;
【讨论】:
我没有看到一个具有静态存储持续时间的变量在此处由非常量初始化。【参考方案6】: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 Initialization, paragraph 4 的表达式:“具有静态或线程存储持续时间的对象的初始化程序中的所有表达式应该是常量表达式或字符串文字。"【参考方案7】:
我的代码中出现了如下错误:
int A = 1;
int B = A;
解决办法是把它改成这个
int A = 1;
#define B A
编译器将内存中的位置分配给变量。第二个是尝试将第二个变量分配给与第一个变量相同的位置 - 这是没有意义的。使用宏预处理器解决了这个问题。
【讨论】:
将一个变量的值分配给另一个是完全正常的。您说正在分配一个内存位置但这里没有指针(如果您有int*
或 &A
会有所不同),除非您将指针存储为 int
s(即 1
存储in A 指的是内存地址),这是一个完全独立的问题。以上是关于尝试使用 const 初始化变量时出现错误“初始化元素不是常量”的主要内容,如果未能解决你的问题,请参考以下文章
尝试初始化 iOS AppsFlyer SDK 时出现 SIGABRT 错误
尝试在 AWS S3 上使用客户端 API 初始化 Datomic Cloud 时出现“无法读取密钥文件”错误