How to set a pointer to an integer in a compound literal that is initialized in a function?

79 Views Asked by At

I tried to initialise a structure via a compound literal in a function. After solving a part of my problem (see Use of compound literal inside a function to init a variable in C), I have a new problem with one member (ptr_mb) that must be a pointer to a variable.

This is the code:

typedef struct {
  int i_mb;
  int * ptr_mb;
} my_struct_t;

void init_struct(my_struct_t **my_struct, int * const ptr) {
  *my_struct=&(static my_struct_t) {.i_mb=3, .ptr_mb= ptr};
}

int main() {
  int a;
  my_struct_t *my_struct;
  
  // my_struct = &(my_struct_t) {.i_mb=3, .ptr_mb=&a}; It is OK

  // Try to initialize it in a function
  init_struct(&my_struct, &a);
  
}

In main(), it works (cf commented line in the program)

A compilation error was emitted. In the function init_struct, ptr is not a constant (line : *my_struct=&(static my_struct_t) {.i_mb=3, .ptr_mb= ptr};).

a is a variable declared in main(). Thus, its address is constant. I tried to cast the affectation with (int const *), (int * const), (int const * const), .... Sometimes it was nonsense, but just to try and nothing worked.

Is it possible to do such initialisation in a function, the way it works in main()?

(Rq. static is used. I know it is a C23 standard and that standard is not yet published; for detail, see this answer: Use of compound literal inside a function to init a variable in C.)

2

There are 2 best solutions below

4
gulpr On BEST ANSWER

Simply do not do it during the initialisation. Add one assignment.

typedef struct {
  int i_mb;
  int * ptr_mb;
} my_struct_t;

void init_struct(my_struct_t **my_struct, int * const ptr) {
  *my_struct = &(static my_struct_t){.i_mb=3};
  (*my_struct) -> ptr_mb = ptr;
}

int a = 10;
int main() {

  my_struct_t *my_struct;
  
  // Try to initialize it in a function
  init_struct(&my_struct, &a);  

  /* ... */ 
}

4
Lundin On

It doesn't work for the same reason as this doesn't work:

int main()
{
  int a;
  static int* ptr = &a;
}

One has to realize that initialization of static storage duration variables happens before main() is called and not at the line where the declaration is located. And since a has automatic storage duration, it doesn't have an address until main() is called. On mainstream systems, a gets allocated on the stack at that point.


The next thing we have to realize is that it doesn't matter if a has a fixed address, not even if we change it to static int a;. Because int * const ptr is not an address constant, which is a formal term for a certain type of constant expression. C23 6.6:

An address constant is a null pointer, a pointer to an lvalue designating an object of static storage duration, or a pointer to a function designator; it shall be created explicitly using the unary & operator or an integer constant cast to pointer type, or implicitly using an expression of array or function type.

Now wait a minute... int a in main() or &a is none of the above either. So the line

my_struct = &(my_struct_t) {.i_mb=3, .ptr_mb=&a}; // It is OK

is actually not OK either. The code works because of implementation-defined extensions in gcc/clang, but it is not strictly conforming C and not portable.