【问题标题】:Error "initializer element is not constant" when trying to initialize variable with const尝试使用 const 初始化变量时出现错误“initializer element is not constant”
【发布时间】:2022-12-07 02:44:58
【问题描述】:

我在以下程序的第 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 的回答。

标签: c initialization


【解决方案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;
}

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

【讨论】:

    【解决方案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
      ", f1->a, f1->b, f1->c);
          printf("Foo2 = %d, %d, %d
      ", 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
      ", str1, str2);
          return 0;
      }
      

      constchar.c:3:21: 错误:初始化元素不是常量 常量字符 * str2 = str1;

      事实上,“const char *”字符串不是编译时常量,所以它不能是初始化器。但是“const char * const”字符串是一个编译时常量,它应该能够成为一个初始化器。我认为这是 CLang 的一个小缺点。

      一个函数名当然是一个编译时常量。所以这段代码有效:

      void func(void)
      {
          printf("func
      ");
      }
      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*&amp;A会有所不同),当然除非你将指针存储为ints(即1存储in A 指的是内存地址),这是一个完全独立的问题。
      • 这个答案毫无意义。我会考虑更好地扩展和解释它。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-03-02
      • 1970-01-01
      • 1970-01-01
      • 2020-01-08
      • 2019-07-25
      • 1970-01-01
      相关资源
      最近更新 更多