尝试使用const初始化变量时出现错误“初始化元素不恒定”


186

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

也许我只需要改变自己的策略,并具有初始化功能即可在启动时完成所有副本。除非那里还有其他想法?

Answers:


269

在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创建命名聚合初始化器的原因。


2
+5是很好的解释,但令人惊讶的是,该程序可以在ideone上编译良好:ideone.com/lx4Xed。是编译器错误还是编译器扩展?谢谢
破坏者

2
@meet:我不知道ideone在幕后使用哪种编译器选项组合,但是它们的结果通常很奇怪,无法描述。我尝试在Coliru(coliru.stacked-crooked.com/a/daae3ce4035f5c8b)上编译此代码,无论使用什么C语言方言设置,都得到了预期的错误。我看不到GCC网站上列出的类似C语言扩展的内容。换句话说,我不知道它如何以及为什么在ideone中编译。即使它编译作为语言的扩展,它仍然应该产生在C的诊断消息
ANT

15
enum { N = 5 };是在不必诉诸的情况下声明常量的一种未被充分理解的方法#define
MM

2
@PravasiMeet“ ideone”根本不显示编译器生成的许多诊断消息,因此它不是一个很好的站点来确定代码是否正确。
MM

1
我发现了一些有趣的东西。如果ptr是在函数内部定义的静态指针,则为错误:static int* ptr = malloc(sizeof(int)*5);但这不是错误static int* ptr; ptr = malloc(sizeof(int)*5);::D
aderchox

74

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

我喜欢您引用的规范,但这无助于我理解我们应该做的事情或事物的现状。
埃文·卡罗尔

1
看来GCC 8.1(及更高版本)已实现了此答案中所述的某些扩展;它接受static const int x = 3; static int y = x;
埃里克·波斯特皮希尔

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

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

5
我看不到一个具有静态存储持续时间的变量,该变量在此处由非常量初始化。
再见SE

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

在您发布的代码,str1不是一个表情6.7.9初始化,第4款:“在为具有静态或线程存储时间应是常量表达式或字符串文本对象的初始化所有的表达式。”
安德鲁·亨利
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.