Error "initializer element is not constant" when trying to initialize variable with const
Asked Answered
B

8

239

I get an error on line 6 (initialize my_foo to foo_init) of the following program and I'm not sure I understand why.

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

Keep in mind this is a simplified version of a larger, multi-file project I'm working on. The goal was to have a single constant in the object file, that multiple files could use to initialize a state structure. Since it's an embedded target with limited resources and the struct isn't that small, I don't want multiple copies of the source. I'd prefer not to use:

#define foo_init { 1, 2, 3 }

I'm also trying to write portable code, so I need a solution that's valid C89 or C99.

Does this have to do with the ORGs in an object file? That initialized variables go into one ORG and are initialized by copying the contents of a second ORG?

Maybe I'll just need to change my tactic, and have an initializing function do all of the copies at startup. Unless there are other ideas out there?

Brazenfaced answered 11/6, 2010 at 17:55 Comment(2)
Fixed in gcc-8.1 and later, see the @Zaman's answer below for details.Intangible
Related: Variably modified array at file scope in C.Deutsch
S
339

In C language, objects with static storage duration have to be initialized with constant expressions, or with aggregate initializers containing constant expressions.

A "large" object is never a constant expression in C, even if the object is declared as const.

Moreover, in C language, the term "constant" refers to literal constants (like 1, 'a', 0xFF and so on), enum members, and results of such operators as sizeof. Const-qualified objects (of any type) are not constants in C language terminology. They cannot be used in initializers of objects with static storage duration, regardless of their type.

For example, this is NOT a constant

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

The above N would be a constant in C++, but it is not a constant in C. So, if you try doing

static int j = N; /* ERROR */

you will get the same error: an attempt to initialize a static object with a non-constant.

This is the reason why, in C language, we predominantly use #define to declare named constants, and also resort to #define to create named aggregate initializers.

Schrock answered 11/6, 2010 at 18:4 Comment(18)
+5 for the nice explanation, but surprisingly this program compiles fine on ideone: ideone.com/lx4Xed. Is it compiler bug or compiler extension? ThanksFabulist
@meet: I don't know what combination of compiler options ideone uses under the hood, but their results are often weird beyond description. I tried compiling this code on Coliru (coliru.stacked-crooked.com/a/daae3ce4035f5c8b) and got the expected error for it regardless of what C language dialect setting I used. I don's see anything like that listed on GCC's web site as a C language extension. In other words, I have no idea how and why it compiles in ideone. Even if it compiles as a language extension, it should still produce a diagnostic message in C.Schrock
enum { N = 5 }; is an under-appreciated way of declaring constants without having to resort to #define.Domenicadomenico
@PravasiMeet "ideone" simply does not display many of the diagnostic messages that the compiler produces, so it is not a very good site to use for determining if code is correct or not.Domenicadomenico
Why b is not considered as constant in below case even though b is assigned value during preprocessing? #ifdef VAR1 int b=45; #else int b=67; #endif // M int a=b; // This line leads to error initializer element is not constant int main(void) { }Palaver
@Rajesh: ??? There's no such thing as "b is assigned value during preprocessing" in C.Schrock
OK, So any variable assignment outside the function is not processed during preprocessing even if we assign #define values!!. How conditional compilation or conditional variable declaration happens then? Looks like I have some basic understanding issue. Could you pl provide link to clarify my doubt?Palaver
@Rajesh: I don't really understand the question. Conditional compilation never depends on any "variables". So, for conditional compilation it does not matter at all when variable assignment happens.Schrock
I don't understand. You state multiple times it's "NOT a constant" but don't explain it except to define terms. It's defined as not a constant? Or there is something that excludes it from the realm of objects that are constant? I feel like "why" is left unanswered here.Rudiment
So how do you handle something like static __m128 const a = _mm_set1_ps(12102203.2f); in C?Waves
I've found out something interesting. if ptr is a static pointer defined inside a function, this is error: static int* ptr = malloc(sizeof(int)*5); but this is NOT an error: static int* ptr; ptr = malloc(sizeof(int)*5); :DCulberson
@Moytaba: All as it should be, given the above. Not sure what you find "interesting" here. (If just for the fun of it you considered these version from C++ point of view, you'd see that they do completely different things. But in C the first version is just meaningless.)Schrock
@AnT Yes, I apologize if that seemed offensive, I literally said it for the fun of it. I'm not an expert in C and I actually used your answer and it helped me to fix an error in my code, so I posted it here. Maybe because you're so knowledgeable it's so vivid for you why first one is meaningless but for me: I just understand that I can not initialize static arrays unless I determine constant sizes(constant as you explained it in your answer) OR I can initialize using malloc AFTER I declare the static pointer for my array.Culberson
I used const vars to initialize a struct in one computer, compiled and exe'd fine, and then compiled and exe'd again in another computer, both ubuntus, same options. One gave me problems and the other didn't. What...?Dahlia
@AnT, it is not a error for gcc 9.3.0 on ubuntu 20.04 with your example code: const int N = 5; static int i = N;. Some thing has been changed? Thanks.Exercise
@Kai: That's just a liberty taken by GCC compiler, which is formally allowed within 6.6/10 "An implementation may accept other forms of constant expressions." (port70.net/~nsz/c/c11/n1570.html#6.6p10) But don't expect this to be followed by other implementations.Schrock
@AnT Oh great. ANOTHER GCC extension like allowing void * pointer arithmetic that's going to lead to even more non-portable code. Shades of "embrace, extend, extinguish"...Calendar
I am using gcc for some embedded project. I see the same error. If I use const int x = 12 to initialize a field within a static struct, the error is gone.Croesus
B
85

It's a limitation of the language. In section 6.7.8/4:

All the expressions in an initializer for an object that has static storage duration shall be constant expressions or string literals.

In section 6.6, the spec defines what must considered a constant expression. No where does it state that a const variable must be considered a constant expression. It is legal for a compiler to extend this (6.6/10 - An implementation may accept other forms of constant expressions) but that would limit portability.

If you can change my_foo so it does not have static storage, you would be okay:

int main()
{
    foo_t my_foo = foo_init;
    return 0;
}
Bloodstock answered 11/6, 2010 at 18:9 Comment(2)
I like that you quoted the spec, but this doesn't help me understand what we're supposed to do or why things are the way they are.Rudiment
It appears GCC 8.1 (and later) has implemented some extension as described in this answer; it accepts static const int x = 3; static int y = x;.Cords
R
11

2021: For who reaches this post because of arm-none-eabi-gcc.exe compile error on STM32 MCUs:
Change your toolchain to gnu-tools-for-stm32.9-2020-q2-update.

From GCC V8.1+, nested constant initializer is supported and the code below will be compiled.

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 is based on gcc v7.3.1 and the code above will not compile! But gnu-tools-for-stm32.9-2020-q2-update uses gcc v9.3.1 and will compile.

For more info see these:
Why "initializer element is not a constant" is... not working anymore?
and
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69960#c18

Ripplet answered 8/4, 2021 at 9:5 Comment(2)
My reading of the C2x draft means this should be standard and portable in the next official release of the C standard, almost certainly to be called C23 at this point.Calendar
Actually it rather seems that the ARM32 port of gcc is not C compliant, not even in -std=c17 -pedantic mode. It fails to give a diagnostic for incorrect casts from non-arithmetic types inside arithmetic constant expressions. This behavior has not changed in the C language nor will it change in C23. For embedded systems programming maybe consider dropping gcc entirely, since the general gcc state of affairs has become increasingly shaky in latter years. I used to find one potential bug/conformance issue once per year or so. Nowadays it's more like once per week.Nickinickie
H
5

Just for illustration by compare and contrast The code is from http://www.geeksforgeeks.org/g-fact-80/ /The code fails in gcc and passes in 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;
}
Hammertoe answered 27/11, 2015 at 11:53 Comment(0)
A
1

This is a bit old, but I ran into a similar issue. You can do this if you use a pointer:

#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;
}
Agosto answered 10/12, 2015 at 2:29 Comment(2)
I don't see a a variable with static storage duration that is initialized by a non-constant here.Debra
Very clever solution. Applauds! I feel like many problems in C can be solved through the use of pointers effectively.Filippo
B
1
  1. There are basically two sorts of initialization: at compile time, and at run time.
  2. The initialization for static-storage variable belongs to the compile-time initialization. Note that the static-storage variable includes:
  • global variable without the static keyword
  • global variable with the static keyword
  • local variable with the static keyword

But what is the principle behind this rule? In my mind it's simple to explain. Before the completion of compilation, the values of these variables would be stored into the executable file. And at that time no code can run!

Britney answered 15/1, 2023 at 14:41 Comment(1)
This makes a lot of sense. I had the same issue as the poster and it was because I was initializing my code outside of the main function. So the compiler naturally complained.Stedfast
G
0

gcc 7.4.0 can not compile codes as below:

#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: error: initializer element is not constant const char * str2 = str1;

In fact, a "const char *" string is not a compile-time constant, so it can't be an initializer. But a "const char * const" string is a compile-time constant, it should be able to be an initializer. I think this is a small drawback of CLang.

A function name is of course a compile-time constant.So this code works:

void func(void)
{
    printf("func\n");
}
typedef void (*func_type)(void);
func_type f = func;
int main() {
    f();
    return 0;
}
Godfrey answered 14/1, 2020 at 14:32 Comment(1)
In the code you posted, str1 is not an expression per 6.7.9 Initialization, paragraph 4: "All the expressions in an initializer for an object that has static or thread storage duration shall be constant expressions or string literals."Calendar
N
-6

I had this error in code that looked like this:

int A = 1;
int B = A;

The fix is to change it to this

int A = 1;
#define B A

The compiler assigns a location in memory to a variable. The second is trying a assign a second variable to the same location as the first - which makes no sense. Using the macro preprocessor solves the problem.

Narcotic answered 31/12, 2020 at 18:51 Comment(2)
Assigning the value of one variable to another is perfectly normal. You say that a memory location is being assigned but there are no pointers here (would be different if you had int* or &A), unless of course you are storing pointers as ints (i.e. the 1 stored in A refers to a memory address), which is an entirely separate issue.Kommunarsk
This answer makes very little sense. I would consider expanding and explaining it better.Siderosis

© 2022 - 2024 — McMap. All rights reserved.