user1225606
user1225606

Reputation: 1153

error: initializer element is not constant

#include <stdio.h>

typedef struct {
  int a;
  int b;
  int c;
} FIRST_T;

typedef struct {
  int x;
  int y;
  int z;
  FIRST_T *p;
} SECOND_T;

typedef struct {
  int a1;
  int a2;
  int a3;
  FIRST_T *q;
}THIRD_T;

const FIRST_T p1[]={{1,2,3},{3,4,5},{6,7,8}};
const FIRST_T p2[]={{4,5,12},{7,8,9}};
const SECOND_T my_second[]=
{
  {1,2,3,p1},
  {4,5,6,p2}
};
const THIRD_T my_third[] = {{1,2,3,my_second[1].p},{4,5,6,my_second[0].p}};

int main() {
  //const THIRD_T my_third[] = {{1,2,3,my_second[1].p},{4,5,6,my_second[0].p}};
  printf("%d %d %d %d \n", 
         my_third[0].a1,
         my_third[0].a2,
         my_third[0].a3,
         my_third[0].q[1].c);
}

I know if I initialize my_third in the function scope it works fine as "In C, objects with static storage duration such as objects declared at file scope can only be initialized with constant expressions" else it gives me:

error: initializer element is not constant new.c:41: error: (near initialization for `my_third[0].q')

Now my question is: Is there any workaround for me without moving the expression inside function. I can't move as these structures are used in many places in my code.

Please let me know if you need more information.

Upvotes: 1

Views: 5373

Answers (3)

Morpfh
Morpfh

Reputation: 4093

An ugly way:

#include <stdio.h>

struct first_t {
    int a;
    int b;
    int c;
};

struct second_t {
    int x;
    int y;
    int z;
    const struct first_t *p;
};

struct third_t {
    int a1;
    int a2;
    int a3;
    const struct first_t *q;
};

const struct first_t p1[] = {
    {1, 2, 3},
    {3, 4, 5},
    {6, 7, 8}
};
const struct first_t p2[] = {
    {4, 5, 12},
    {7, 8, 9}
};
const struct second_t my_second[] = {
    {1, 2, 3, p1},
    {4, 5, 6, p2}
};
const struct third_t  my_third[] = {
    {1, 2, 3, (const struct first_t*)&my_second[1].p},
    {4, 5, 6, (const struct first_t*)&my_second[0].p}
};

int main(void) {
    fprintf(stdout,
        "PRNT: %d %d %d %d\n",
        my_third[0].a1,
        my_third[0].a2,
        my_third[0].a3,
        my_third[0].q[1].c
    );

    return 0;
}

Gives:

/* gcc -Wall -Wextra -pedantic -std=c89 -ggdb -o bad fmts.c && ./bad
 *
 * PRNT: 1 2 3 4
 *
 * */

Upvotes: 1

Lundin
Lundin

Reputation: 213799

typedef struct {
    int x;
    int y;
    int z;
    FIRST_T *p;
} SECOND_T;

When you declare a variable of this struct const, all its members become const, as in they get stored in ROM. int const x;, int const y; etc. For the pointer, this means that it turns FIRST_T * const p;. That is, a const pointer to non-constant data. The pointed-to type is still not const however! I suspect this is why you get problems.

{1,2,3,p1},

p1 is of array type and decays into a const FIRST_T p1* const (constant pointer to constant data). You try to assign this to a FIRST_T * const p; (constant pointer to non-constant data). To fix this, try to declare the types as

typedef struct {
  int x;
  int y;
  int z;
  const FIRST_T *p;
} SECOND_T;

typedef struct {
  int a1;
  int a2;
  int a3;
  const FIRST_T *q;
} THIRD_T;

Tested with MinGW, GCC 4.6.2.

  • -std=c99 -pedantic compiles fine.
  • -std=c89 -pedantic gives warnings. "warning: initializer element is not computable at load time [enabled by default]".

(I'm not sure of the difference between the standards for this case so I'd rather not speculate why it works in C99 and not C89. Might be related to VLA?)

Upvotes: 0

glglgl
glglgl

Reputation: 91017

what about

const THIRD_T my_third[] = {{1,2,3,p2},{4,5,6,p1}};

as it is exactly the value you'll get?

In order to make changes easier, you could wrap a layer such as

const FIRST_T p1[]={{1,2,3},{3,4,5},{6,7,8}};
const FIRST_T p2[]={{4,5,12},{7,8,9}};

#define first_p p1
#define second_p p2

const SECOND_T my_second[]=
{
{1,2,3,first_p},
{4,5,6,second_p}
};
const THIRD_T my_third[] = {{1,2,3,second_p},{4,5,6,first_p}};

and then just change the #defines in order to keep my_second and my_third in sync.

Another option could be to change THIRD_T to contain a SECOND_T instead of a FIRST_T. and then (maybe) have a #define do simplify access:

typedef struct {
int a1;
int a2;
int a3;
SECOND_T *qq;
}THIRD_T;

#define q qq->p

so that

my_third->q

becomes effectively

my_third->qq->p

.

Upvotes: 0

Related Questions