【问题标题】:How to correctly read a C-string into a Fortran string of unspecified length?如何正确地将 C 字符串读入未指定长度的 Fortran 字符串?
【发布时间】:2021-08-13 11:06:57
【问题描述】:

以下函数应该将 C 字符串转换为 Fortran 字符串,并且在 Release 版本中可以正常工作,但在 Debug 中不能:

! Helper function to generate a Fortran string from a C char pointer
function get_string(c_pointer) result(f_string)
    use, intrinsic :: iso_c_binding
    implicit none
    type(c_ptr), intent(in)         :: c_pointer
    character(len=:), allocatable   :: f_string

    integer(c_size_t)               :: l_str
    character(len=:), pointer       :: f_ptr

    interface
        function c_strlen(str_ptr) bind ( C, name = "strlen" ) result(len)
        use, intrinsic :: iso_c_binding
            type(c_ptr), value      :: str_ptr
            integer(kind=c_size_t)  :: len
        end function c_strlen
    end interface

    l_str = c_strlen(c_pointer)
    call c_f_pointer(c_pointer, f_ptr)

    f_string = f_ptr(1:l_str)
end function get_string

但是,c_f_pointer 似乎没有告诉 Fortran 指向字符串的指针 f_ptr 它指向的字符串的长度。在调试版本中,边界检查处于活动状态,这会导致

Fortran runtime error: Substring out of bounds: upper bound (35) of 'f_ptr' exceeds string length (0)

我正在使用gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0 并将标准设置为 2008。

我的问题:有没有办法告诉f_ptr 它的长度而不更改声明,或者我在这里做的事情根本上是错误的?


如果我指定形状,它似乎可以正确运行,但为此 f_ptr 需要是一个数组:

character(len=:), allocatable   :: f_string
character(len=1), dimension(:), pointer :: f_ptr
...
call c_f_pointer(c_pointer, f_ptr, [l_str])

但是,我找不到将排名为 1 的字符串转换为显然排名为 0 的 character(len=:), allocatable :: f_string 的方法。

我的第二个问题:在这个例子中,有什么办法可以将f_ptr的数据转入f_string

【问题讨论】:

    标签: fortran fortran-iso-c-binding


    【解决方案1】:

    你不能使用c_f_pointer来设置Fortran字符指针的长度(F2018, 18.2.3.3):

    FPTR 应为指针,不应有延迟类型参数 [...]

    因此不能使用延迟长度的字符标量(或数组)(长度是类型参数)。

    您确实可以使用延迟shape 字符数组作为fptr,然后使用任何number of techniques 将该数组的元素复制到标量(如前所述,它是rank-0) .

    例如,使用子字符串赋值(在显式分配延迟长度标量之后):

    allocate (character(l_str) :: f_string)
    do i=1,l_str
      f_string(i:i)=fptr(i)
    end
    

    或者考虑是否可以简单地使用字符数组而不是复制到标量。

    【讨论】:

    • 可能,使用固定长度的字符串(在一个块内)也有效,例如...l_str = c_strlen(c_pointer); block; character(len=l_str), pointer :: f_ptr; call c_f_pointer(c_pointer, f_ptr); f_string = f_ptr; end block
    • 是的,@roygvib,这是可能的。
    • @roygvib,我不确定我是否喜欢这种方法,但我无法告诉你原因。我会让它通过审查,但我对 cme​​ts 的要求可能比我对一些替代品的要求更高。也许它触动的原因是,如果你有一个 C 字符数组,那么中间 Fortran 对象也更自然地是一个字符数组,而不是与存储相关的标量(然后都被丢弃)。
    • @roygvib,或者你不能block; character(len=l_str), pointer :: p1, p2; call c_f_pointer(cptr, p1); call c_f_pointer(cptr, p2); end block(因为你不能有两个这样的Fortran字符指针指向同一个C目标)。
    • 是的,这一定是我不喜欢它的原因。您习惯了这种方法,然后有一天您会发现自己以一种类似的方式使用它,实际上不允许您这样做,并且您的宇宙飞船爆炸了。
    猜你喜欢
    • 1970-01-01
    • 2022-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-14
    • 1970-01-01
    • 2021-12-04
    相关资源
    最近更新 更多