C语言 尝试使用常量初始化变量时出现错误“初始化项元素不是常量

sczxawaw  于 2023-01-16  发布在  其他
关注(0)|答案(8)|浏览(220)

我在下面程序的第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的内容来初始化?
也许我只需要改变我的策略,让一个初始化函数在启动时完成所有的复制,除非有其他的想法?

wqlqzqxt

wqlqzqxt1#

在C语言中,具有静态存储持续时间的对象必须用 * 常量表达式 * 初始化,或者用包含常量表达式的聚合初始化器初始化。
在C语言中,“大型”对象从来不是常量表达式,即使该对象声明为const
此外,在C语言中,术语“constant”指的是 * 文字常量 *(如1'a'0xFF等)、枚举成员以及sizeof等运算符的结果。Const-qualified object(任何类型)在C语言术语中是 * 非常量 *。它们不能用于具有静态存储持续时间的对象的初始化器,无论其类型如何。
例如,这是 NOT 常量

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

上面的N在C++中是常量,但在C中不是常量。因此,如果您尝试

static int j = N; /* ERROR */

你会得到同样的错误:尝试使用非常数初始化静态对象。
这就是为什么在C语言中,我们主要使用#define来声明命名常量,并且也借助于#define来创建命名聚合初始化式的原因。

azpvetkf

azpvetkf2#

这是语言的局限性。在第6.7.8/4节中:
具有静态存储持续时间的对象的初始值设定项中的所有表达式都应该是常量表达式或字符串文本。
在第6.6节中,规范定义了什么是常量表达式。没有说明常量变量必须被认为是常量表达式。编译器可以法律的地扩展这个(6.6/10 - An implementation may accept other forms of constant expressions),但这会限制可移植性。
如果您可以更改my_foo,使其不具有静态存储,则不会有问题:

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

k2fxgqgv3#

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

mzsu5hc0

mzsu5hc04#

仅通过比较和对比进行说明代码来自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;
}
sigwle7e

sigwle7e5#

这是一个有点老的问题,但我遇到了类似的问题。如果你使用一个指针,你可以做到这一点:

#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;
}
3ks5zfa0

3ks5zfa06#

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

常数c:3:21:错误:初始化项元素不是常量const char * str 2 = str 1;
事实上,“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;
}
8e2ybdfx

8e2ybdfx7#

1.基本上有两种初始化:编译时和运行时。
1.静态存储变量的初始化属于编译时初始化,注意静态存储变量包括:

  • 不带static关键字的全局变量
  • 带有static关键字的全局变量
  • 带有static关键字的局部变量

但是这个规则背后的原理是什么呢?在我看来解释起来很简单,在编译完成之前,这些变量的值会被存储到可执行文件中,而那时代码是无法运行的!

xxhby3vn

xxhby3vn8#

我在代码中遇到了这样的错误:

int A = 1;
int B = A;

修复方法是将其更改为

int A = 1;
#define B A

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

相关问题