I experienced yet another difference in behavior between ifort
(19.0.8.324) and gfortran
(8.5.0); I passed a Fortran string whose memory I allocated on the Fortran side via C_PTR
to C successfully. But when I attempted to free
that memory on the C side, it worked for gfortran
but didn’t for ifort
. For the latter, the actual beginning of the allocated memory was 32 bytes before the address of the char *
. Also I noticed that ifort
allocate 40 bytes more than required for the stored characters (45).
For gfortran
the char*
pointed to the start of the allocated memory and only the number of chars required was allocated (5).
Does anyone have an idea why the char *
is shifted 32 bytes forward with respect to the start of the allocated memory and why 40 bytes in excess are allocated when using ifort
?
N.B.: The way I solved it was to write a Fortran routine that receives the char*
back, converts it to a Fortran CHARACTER
and deallocates that (FREE_STRING
). This works for both compilers.
main.c
:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *f_get_string();
void f_free_string(char *c_string);
void main(int argc, char* argv) {
char *c_string = f_get_string();
printf("Here's the fetched string: %s len: %d address: %x\n", c_string, strlen(c_string), c_string);
free(c_string);
}
mod_string.f90
MODULE MOD_STRING
USE ISO_C_BINDING
CONTAINS
FUNCTION CONSTRUCT_STRING() RESULT(my_string)
USE ISO_C_BINDING
IMPLICIT NONE
CHARACTER(LEN=:), ALLOCATABLE :: my_string
my_string = "asdf" // C_NULL_CHAR
END FUNCTION
FUNCTION GET_STRING_SUB() RESULT(c_string_ptr) BIND(C, name='f_get_string')
USE ISO_C_BINDING
IMPLICIT NONE
TYPE(C_PTR) :: c_string_ptr
CHARACTER(LEN=:), POINTER :: c_string
CHARACTER(LEN=:), ALLOCATABLE :: f_string
f_string = CONSTRUCT_STRING()
ALLOCATE(c_string, SOURCE=f_string)
WRITE(*,'(A,I0)') "f_string_len: ", LEN(f_string)
WRITE(*,'(A,I0)') "c_string_len: ", LEN(c_string)
c_string_ptr = C_LOC(c_string)
WRITE(*,'(A, Z0)') "LOC(C_STRING): ", LOC(c_string)
END FUNCTION GET_STRING_SUB
SUBROUTINE FREE_STRING(c_string_ptr) BIND(C, name='f_free_string')
USE ISO_C_BINDING
IMPLICIT NONE
TYPE(C_PTR), VALUE :: c_string_ptr
CHARACTER, POINTER :: f_string
CALL C_F_POINTER(c_string_ptr, f_string)
WRITE(*,*) "FREE ", f_string
DEALLOCATE(f_string)
END SUBROUTINE FREE_STRING
END MODULE MOD_STRING
CMakeLists.txt
:
cmake_minimum_required (VERSION 3.0)
enable_language(C Fortran )
project(c_calls_fortran C)
set(CMAKE_C_COMPILER "gcc")
set(CMAKE_Fortran_COMPILER "ifort")
set(CMAKE_Fortran_FLAGS_RELEASE "-O3 -w -g")
set(CMAKE_Fortran_FLAGS_DEBUG "-g")
set(CMAKE_C_FLAGS_DEBUG "-g")
add_library(mod_string STATIC mod_string.f90)
set_property(TARGET mod_string PROPERTY LINKER_LANGUAGE Fortran)
set_target_properties(mod_string PROPERTIES LINK_FLAGS -static-intel )
target_link_libraries(mod_string ifcoremt irc pthread imf dl)
link_directories(.)
link_directories("path/to/fortran/libs")
add_executable(c_calls_fortran main.c)
set_property(TARGET c_calls_fortran PROPERTY LINKER_LANGUAGE C)
target_link_libraries(c_calls_fortran mod_string ifcoremt irc pthread imf dl)