arrays - 将动态分配的字符串数组从 C 传递到 Fortran

标签 arrays c string fortran fortran-iso-c-binding

我试图将动态分配的字符串数组(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 的十六进制转储显示字符串存在

enter image description here

相关帖子

How to pass arrays of strings from both C and Fortran to Fortran?

Passing an array of C-strings to Fortran (iso_c_binding)

最佳答案

在 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/

相关文章:

java - 如何在 Selenium Webdriver 中比较数组列表与列表 Web 元素

javascript - 将数字数组平均分配为 x 个数字子集

arrays - YAML 中的二维数组

C程序运行时卡住

c - 为什么一个位域结构表达式在赋值过程中表现不同?

c - 如何访问由双指针表示的二维数组的元素作为 C 中的一维数组

java - 检查字符串是否为回文(使用 char[] 输入而不是字符串)

c - C 中的 if 语句不稳定

windows - 使用 awk 将多行文件转换为 TSV

c - 了解返回值函数 C