Yes, there is a potential bitness problem. If you want your code to be robust in the face of compiler and platform changes, then there are a number of things that you should do, most of which rely on the C interoperability features of Fortran 2003. These language features are supported by recent gfortran and most actively maintained Fortran compilers.
It is not clear from your example whether the Fortran code really needs to know the value of the pointer to the data
struct as an integer (in your example you print this value, but I suspect that is just for debugging). If the Fortran code just needs to regard the pointer as an opaque handle, then the C_PTR type in the ISO_C_BINDING intrinsic module is the appropriate equivalent to a C pointer. If for some reason the Fortran code does need to know the value of the pointer as an integer, then an integer of kind C_INTPTR_T (again from the ISO_C_BINDING intrinsic module) is appropriate. Going further still, if you want the fortran code to be able to play with the actual structure itself, then you can define a BIND(C) derived type and use that in various ways.
Further, your C code assumes that the Fortran compiler uses a certain calling convention, including the way that procedure names are mangled to form a linker name. You can use the BIND(C,NAME='xxx') attribute in an interface block on the Fortran side to specify that the Fortran compiler use a calling convention that is compatible with its companion C compiler and to specify the C name of a procedure.
Note that the POINTER declaration on the integer and its subsequent allocation is not relevant, given the rest of your example.
All up (in free form, it has been a while since fixed form was appropriate):
PROGRAM this
USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR, C_INTPTR_T
IMPLICIT NONE
! Interfaces required due to BIND(C). Also allows the Fortran
! compiler to do better error checking. Note that the default
! binding label is a lowercase variant of the Fortran name, but
! we specify it explicitly here anyway for clarity.
INTERFACE
SUBROUTINE that_allocate(the_c_ptr) &
BIND(C,NAME='that_allocate')
USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR
IMPLICIT NONE
! Note passing a pointer by reference.
TYPE(C_PTR), INTENT(OUT) :: the_c_ptr
END SUBROUTINE that_allocate
SUBROUTINE that_assemble(the_c_ptr) &
BIND(C,NAME='that_assemble')
USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR
IMPLICIT NONE
! Note passing a pointer by value.
TYPE(C_PTR), INTENT(IN), VALUE :: the_c_ptr
END SUBROUTINE that_assemble
SUBROUTINE that_free(the_c_ptr) &
BIND(C,NAME='that_free')
USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR
IMPLICIT NONE
! Note passing a pointer by value.
TYPE(C_PTR), INTENT(IN), VALUE :: the_c_ptr
END SUBROUTINE that_free
END INTERFACE
TYPE(C_PTR) :: the_c_ptr
CALL that_allocate(the_c_ptr)
! Use transfer to convert the C address to an integer value.
PRINT "('Fortran: ptr address',Z12)", &
TRANSFER(the_c_ptr, 0_C_INTPTR_T)
CALL that_assemble(the_c_ptr)
CALL that_free(the_c_ptr)
END PROGRAM this
and simplifying on the C side:
#include <stdlib.h>
#include <stdio.h>
typedef struct data {
int a;
float b;
} data;
void that_allocate(data** pData)
{
*pData = (data*) calloc(1, sizeof(data));
printf("C: Allocated address %p\n", *pData);
return;
}
void that_assemble(data* pData)
{
pData->a = 42;
pData->b = 3.1415926;
return;
}
void that_free(data* pData)
{
printf("C: Freeing data %d and %g at %p\n", pData->a, pData->b, pData);
free(pData);
return;
}