I am trying to pass a dynamically allocated array of strings (char ***str
) from C to Fortran but I seem to be missing something when it comes to dereferencing the C pointer in Fortran thus yielding garbage strings as output (see MWE below).
Side Question
Is calling deallocate(fptr)
enough to stop the program below from leaking memory (I would think not)? If not would it be possible to free msgs
(in C) from Fortran?
To Reproduce
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void str_alloc(char*** msgs, size_t * n) {
*n = 3;
int str_len = 10;
(*msgs) = malloc(*n * sizeof(char*));
for (int i = 0; i < *n; i++) {
(*msgs)[i] = malloc((str_len + 1) * sizeof(char));
strcpy((*msgs)[i], "012346789");
}
}
program main
use iso_c_binding
implicit none
interface
subroutine str_alloc(msgs, n) bind(C, name="str_alloc")
use iso_c_binding
type(c_ptr), intent(out) :: msgs
integer(c_size_t), intent(out) :: n
end subroutine
end interface
integer, parameter :: STRLEN = 10
character(kind=c_char, len=STRLEN), allocatable :: names(:)
call str_array_from_c(names)
contains
subroutine str_array_from_c(strs)
character(len=STRLEN), allocatable, intent(out) :: strs(:)
type(c_ptr) :: cptr
integer(c_size_t) :: i, n, lenstr
character(kind=c_char, len=1), pointer :: fptr(:,:)
call str_alloc(cptr, n)
call c_f_pointer(cptr, fptr, [int(STRLEN, kind=c_size_t), n])
allocate(strs(n))
print*, "Output C-str array from fortran"
do i = 1_c_size_t, n
lenstr = cstrlen(fptr(:, i))
strs(i) = transfer(fptr(1:lenstr,i), strs(i))
print*, fptr(1:STRLEN, i), "|", strs(i)
end do
deallocate(fptr)
end subroutine str_array_from_c
!> Calculates the length of a C string.
function cstrlen(carray) result(res)
character(kind=c_char, len=1), intent(in) :: carray(:)
integer :: res
integer :: i
do i = 1, size(carray)
if (carray(i) == c_null_char) then
res = i - 1
return
end if
end do
res = i
end function cstrlen
end program main
Compile
gcc -c -g -Wall -o cfile.o cfile.c
gfortran -g -Wall -o main.o cfile.o main.f90
Output
Output C-str array from fortran
0�P�|0�
p�|
!|
Additional Info
- I cannot edit the C interface.
- The C
str_len
in reality is variable and hence the array of strings is jagged. As far as I know, the only solution, without defining a custom type, is using a large enoughlen=
of characters. - I am relatively convinced that my mistake is how I dereference the pointer since inspecting the hex dump of
cptr
shows the strings are present
Related posts
How to pass arrays of strings from both C and Fortran to Fortran?