Fortran interface to C struct containing array of structs

How do you write an Fortran interface for a C struct that contains other arrays of structs?

I wrote a small test to understand how to interface Fortran to a C struct that contains an array(s) of structs. This was to understand why a library I am wrapping using Fortran keeps seg faulting.

In this particular test, the derived type on the Fortran side does not print the same values as the C side!

This is the C side:
aos.h

#include <stdlib.h>
#include <stdio.h>

struct Foo_ {                
  float pos[2];           
  float cutoff;           
};
typedef struct Foo_ Foo;

struct Bar_ {             
  float pos[2];        
  float centre;        
};
typedef struct Bar_ Bar;

struct FooBar_ {          
  int  maxg;   
  int* gorder; 
  Foo f[2];   
  Bar b[2];   
};
typedef struct FooBar_ FooBar;

void init_default_values(FooBar* fb);

aos.c

#include "aos.h"

void init_default_values(FooBar* fb) {
  fb = malloc( sizeof(FooBar) );

  fb->maxg = 1;
  fb->gorder = malloc( sizeof(int) );
  *(fb->gorder) = 2;

  printf( "fb->maxg = %d\n", fb->maxg );
  printf( "fb->gorder = %d\n", *(fb->gorder) );

  for (int i = 0; i < 2; ++i) {
    for (int j = 0; j < 2; ++j) {
      fb->f[i].pos[j] = i+j*2;
      printf( "fb->f[%d].pos[%d] = %f\n", i, j, fb->f[i].pos[j] );
    }
  }
}

Here is my Fortran attempt:
faos.f90

module mod_aos
  use iso_c_binding
  use iso_fortran_env, only: f32 => real32, f64 => real64
  implicit none

  type, bind(c) :: Foo
    real(f32) :: pos(2)
    real(f32) :: cutoff
  end type Foo

  type, bind(c) :: Bar
    real(f32) :: pos(2)
    real(f32) :: centre
  end type Bar

  type, bind(c) :: FooBar
    integer(c_int) :: maxg
    type(c_ptr) :: gorder
    type(Foo) :: f(2)
    type(Bar) :: b(2)
  end type FooBar

  interface
    subroutine init_default_values( fb ) bind(c, name="init_default_values")
      import:: FooBar
      type(FooBar), intent(inout) :: fb
    end subroutine init_default_values
  end interface
  
contains  
end module mod_aos

program  main
  use iso_c_binding
  use mod_aos
  implicit none

  type(FooBar) :: fb

  print *, "======"
  print *, "C SIDE"
  print *, "======"
  call init_default_values( fb )


  print *, ""
  print *, "============"
  print *, "FORTRAN SIDE"
  print *, "============"
  print *, "fb%maxgeom  = ", fb%maxg       ! Should be 1
  print *, "fb%gorder   = ", fb%gorder     ! Should be 2
  print *, "fb%f%pos    = ", fb%f(1)%pos   ! Should be [0.0, 2.0]
  print *, "fb%f%pos    = ", fb%f(2)%pos   ! Should be [1.0, 3.0]
end program  main

Here is the output. notice that the Fortran side prints the wrong values (random values):

======
C SIDE

fb->maxg = 1
fb->gorder = 2
fb->f[0].pos[0] = 0.000000
fb->f[0].pos[1] = 2.000000
fb->f[1].pos[0] = 1.000000
fb->f[1].pos[1] = 3.000000

============
FORTRAN SIDE

fb%maxgeom = 0
fb%gorder = 0
fb%f%pos = 2.80259693E-45 0.00000000
fb%f%pos = -0.00000000 0.00000000

Retry after removing this instruction, there is no need to malloc the reference to the struct on the C side.

Thanks @FortranFan.

@general_rishkin , you will note a pedantic consideration will be to use the c_float and c_double named constants from iso_c_binding standard intrinsic module:

use iso_c_binding, only: f32 => c_float, f64 => c_double

The above would appear adequate for the simple example you show. For the actual code, perhaps extend the list as needed due to ONLY clause (or other options with the USE statement).