通过动态分配的字符串从C到Fortran

发布于 2025-02-10 09:09:38 字数 3210 浏览 3 评论 0 原文

我正在尝试将动态分配的字符串阵列( 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接口。
  • C str_len 实际上是可变的,因此字符串的数组被锯齿状。据我所知,唯一没有定义自定义类型的解决方案是使用足够大的 len = 字符的字符。
  • 我相对确信,我的错误是我如何在检查 cptr 的十六进制以来,说明字符串存在

“在此处输入映像说明”

相关帖子

如何通过C和Fortran的字符串传递到Fortran?

通过一系列C- fortran的字符串(iso_c_binding)

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 enough len= 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

enter image description here

Related posts

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

怎会甘心 2025-02-17 09:09:38

在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

On the C side you have an array of pointers, which is NOT the same as a two-dimensional Fortran array. Fortran doesn't have the "array of pointers" concept, so you have to have an array of derived type with a pointer component.

First, the cptr returned from the C code has to be converted to an array of the derived type, each element of which contains a pointer to the string. You then iterate through each of these, converting the subcomponent C_PTR to a Fortran pointer, which is then your array of characters.

Second, you can't DEALLOCATE something that Fortran didn't allocate. You would have to call back into C to free those strings.

Here is a revised version of your Fortran code that works. The C code didn't change.

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

When I build and run this I get:

 Output C-str array from fortran
 012346789|012346789
 012346789|012346789
 012346789|012346789
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文