我试图将动态分配的字符串数组(char ***str
)从 C 传递到 Fortran,但在取消引用 Fortran 中的 C 指针时,我似乎遗漏了一些东西产生垃圾字符串作为输出(参见下面的 MWE)。
附带问题
调用deallocate(fptr)是否足以阻止下面的程序泄漏内存(我认为不会)?如果没有,是否可以从 Fortran 中释放 msgs
(C 语言)?
重现
#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
编译
gcc -c -g -Wall -o cfile.o cfile.c
gfortran -g -Wall -o main.o cfile.o main.f90
输出
Output C-str array from fortran
0�P�|0�
p�|
!|
其他信息
- 我无法编辑 C 接口(interface)。
- 实际上,C
str_len
是可变的,因此字符串数组是锯齿状的。据我所知,在不定义自定义类型的情况下,唯一的解决方案是使用足够大的len=
字符。 - 我相对确信我的错误在于我取消引用指针的方式,因为检查
cptr
的十六进制转储显示字符串存在
相关帖子
How to pass arrays of strings from both C and Fortran to Fortran?
最佳答案
在 C 端有一个指针数组,它与二维 Fortran 数组不同。 Fortran 没有“指针数组”概念,因此您必须拥有带有指针组件的派生类型数组。
首先,从 C 代码返回的 cptr 必须转换为派生类型的数组,其中每个元素都包含一个指向字符串的指针。然后,您迭代其中的每一个,将子组件 C_PTR 转换为 Fortran 指针,该指针就是您的字符数组。
其次,您不能DEALLOCATE
Fortran 未分配的内容。您必须回调 C 来释放这些字符串。
这是您的 Fortran 代码的修订版,可以使用。 C 代码没有改变。
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
type,bind(C) :: c_array_t
type(c_ptr) :: s
end type c_array_t
type(c_array_t), pointer :: c_array(:)
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,c_array, [n])
allocate(strs(n))
print*, "Output C-str array from fortran"
do i = 1_c_size_t, n
call c_f_pointer(c_array(i)%s, fptr, [int(STRLEN, kind=c_size_t)])
lenstr = cstrlen(fptr)
strs(i) = transfer(fptr(1:lenstr), strs(i))
print*, fptr(1:STRLEN), "|", strs(i)
end do
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
当我构建并运行它时,我得到:
Output C-str array from fortran
012346789|012346789
012346789|012346789
012346789|012346789
关于arrays - 将动态分配的字符串数组从 C 传递到 Fortran,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72744199/