1506-221 (S) Initializer must be a valid constant expression

In AIX during compilation of header file i am facing this issue.

Header file (header1.h) has the structure like this:

struct Str {
  int aa;
  char bb;
};

now in .c file including header1.h

#include<stdio.h>
#include"f1.h"

#define DEF(x) (&sx1.x -&sx1.a)

static struct MyStruct{
    struct Str a;
    struct Str c;
    struct Str b;
}sx1 = {DEF(b),'B','A'};

int main()
{
    printf("%d %c",sx1.a,sx1.b);
}

when i'm compiling the above .c file using xlc compiler it is throwing the error:

header1.h", line xxxx: 1506-221 (S) Initializer must be a valid constant expression.

make: 1254-004 The error code from the last command is 1.

Stop.

Upvotes: 0

Views: 403

Answers (2)

Luis Colorado
Luis Colorado

Reputation: 12668

Well, you have several mistakes, apart from the one you are telling. First of all, you are enclosing (and that's very dangerous, because you are hiding the expressions below macro instantiations and that makes errors invisible to you) in the DEF macro the subtraction of addresses with different pointer types (which is illegal) For example, if you redefine DEF to work for your first struct as:

#define DEF(f) (&sx1.f - &sx1.aa)

then you'll get an error in the expansion of:

DEF(bb) -> (&sx1.bb - &sx1.aa)

where bb is a char and a is an int (you'll be subtracting a int * to a char *). This will lead to compilation problems after the expansion of the DEF macro, but you will not see the actual expansion that produces the compiler error.

Second, an initialiser can only use constant expressions, this means, static expressions that produce only constants or expressions of constants, and & operator has to be resolved at link time most times (this makes the expression not a constant one, but something that can change between compilations) and even at runtime (assume the operand to the first & is a stack automatic variable, and he operand to the second is a global, fixed variable, then each time you initialise, the constant value is different, depending on how did the stack grew at runtime. You cannot use the & operator in a constant expression as a general rule.

Third, if you are trying to emulate the definition of the ANSI offsetof(structure_type, field) macro, then you can use something like (beware that this definition leads to some tricks that can be architecture dependant, and for that reason not be portable):

#define OFFSETOF(type, field)  ((size_t)(char *)(&((type*)0)->field))

as in the following code:

pru.c

#include <stdio.h>
struct my_data_struct {
    int a;
    char b;
    double c;
    char d[100];
    int e;
};

#define OFFSET_OF(type, field) ((size_t)(char *)(&((type*)0)->field))

int main()
{
#define P(f) printf("OFFSET_OF(struct my_data_struct, %s) == %zu\n", #f, OFFSET_OF(struct my_data_struct, f))
    P(a);
    P(b);
    P(c);
    P(d);
    P(e);
}

that produces:

$ run pru
OFFSET_OF(struct my_data_struct, a) == 0
OFFSET_OF(struct my_data_struct, b) == 4
OFFSET_OF(struct my_data_struct, c) == 8
OFFSET_OF(struct my_data_struct, d) == 16
OFFSET_OF(struct my_data_struct, e) == 116
$ _

on my system.

Explanation

(type*)0

is the address of the NULL literal converted to (type*)

((type*)0)->field

is the null pointer dereferenced at its field field (don't worry, as we aren't actually dereferencing the value, only getting a reference to it)

&((type*)0)->field

is its address.

(char *)(&((type*)0)->field)

is the address converted to a (char *) (so pointer arithmetic is byte size). And

((size_t)(char *)(&((type*)0)->field)

is the address converted to a size_t value.

Of course, part (if not most) of this calculation is architecture dependant, and that's the reason the standard today includes some kind of this macro in the standard library (#include <stddef.h>). So don't use my definition (just in case you don't have it in your system) and search your compiler/library documentation for an offsetof macro that shows you the position of fields in a structure.

One last thing... as I've used a constant pointer value (everything derives from the constant 0) and have not subtracted references, it is usable in initialiser that require constant expressions.

#include <stdio.h>
struct my_data_struct {
    int a;
    char b;
    double c;
    char d[100];
    int e;
};

#define OFFSET_OF(type, field) ((size_t)(char *)(&((type*)0)->field))

static size_t my_vector[] = {
    OFFSET_OF(struct my_data_struct, a),
    OFFSET_OF(struct my_data_struct, b),
    OFFSET_OF(struct my_data_struct, c),
    OFFSET_OF(struct my_data_struct, d),
    OFFSET_OF(struct my_data_struct, e),
};

You can get a good explanation of all this stuff here

Upvotes: 0

Kami Kaze
Kami Kaze

Reputation: 2080

Not regarding your strange initialisation of sx1 ( It is a nested struct so you need a nested initialiser).

{{1,'a'},{1,'a'},{1,'a'}};

The expansion of DEF is only available at runtime, as you use (the address of) variables. As the compiler tells you, you can only use constant expressions meaning that the value has to be clear at compile time. Like in the above example.

The offsetof macro might be what you are looking for it evaluates to a constant expression. A constant expression can not contain a variable. Luckiy the definition of a struct is not a variable.

The example below got rid of the second struct is it unnecessarily complicates the problem. You can add it back in for your code.

AFAIK you need to declare the Struct completly before you can use it. That's why the declaration of sx1 is sperate.

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

static struct MyStruct sx1 = {offsetof(struct MyStruct,b),'B','A'};

int main()
{
    printf("%d %c",sx1.a,sx1.b);
}

You may want to check typedef structs

Upvotes: 0

Related Questions