释放从 c_f_pointer 定义的数组

Don*_*nna 5 fortran pointers gfortran fortran-iso-c-binding intel-fortran

以下代码可在 GNU gfortran 和 Intel ifort 中编译。但只有gfortran编译版本才能成功运行。

    program fort_tst
        use iso_c_binding

        INTEGER, POINTER :: a(:) 
        TYPE(C_PTR) :: ptr 

        INTEGER, POINTER :: b(:) 

        ALLOCATE(a(5)) 

        ptr = c_loc(a) 

        CALL c_f_pointer(ptr,b,[5]) 

        DEALLOCATE(b) 
    end program fort_tst
Run Code Online (Sandbox Code Playgroud)

英特尔编译代码中的错误是:

forrtl: severe (173): A pointer passed to DEALLOCATE points to an object that cannot be deallocated
Image              PC                Routine            Line        Source             
fort_tst           000000000040C5A1  Unknown               Unknown  Unknown
fort_tst           0000000000403A17  Unknown               Unknown  Unknown
fort_tst           0000000000403812  Unknown               Unknown  Unknown
libc-2.17.so       00002AAAAB20F555  __libc_start_main     Unknown  Unknown
fort_tst           0000000000403729  Unknown               Unknown  Unknown
Run Code Online (Sandbox Code Playgroud)

gfortran 代码运行完成。快速 valgrind 检查没有发现任何泄漏。

有人可以确认上面的代码是否有效/合法吗?

我在跑步

    ifort (IFORT) 2021.2.0 20210228
Run Code Online (Sandbox Code Playgroud)

    GNU Fortran (GCC) 9.2.0
    Copyright (C) 2019 Free Software Foundation, Inc.
Run Code Online (Sandbox Code Playgroud)

更新 :

有趣的是,gfortran 做了正确的事情(即仅释放已分配的内存),即使用户试图将其与不正确的索引重新映射或虚假的形状参数混淆。因此内部数组描述符被 gfortran 的 c_f_pointer 正确复制。

Don*_*nna 1

上面的帖子启发了以下解决方案。这个想法是创建一个包装实际数据数组的类型。然后,c_loc/c_f_pointer 序列可以与指向标量对象的指针一起正常工作。可以安全地分配存储在类型中的数据数组以及数组类型本身。

MODULE arraytype_m
    TYPE, PUBLIC :: arraytype
        INTEGER, ALLOCATABLE :: data(:)
    END TYPE arraytype  
END MODULE arraytype_m


PROGRAM fort_tst
    USE iso_c_binding
    USE arraytype_m

    TYPE(arraytype), POINTER  :: a, b
    TYPE(C_PTR) :: ptr 

    ALLOCATE(a)
    ALLOCATE(a%data(5))

    !! Set to C-style pointer, and then copy back to Fortran pointer.
    ptr = c_loc(a) 
    CALL c_f_pointer(ptr,b)

    DEALLOCATE(b%data)
    DEALLOCATE(b) 
END PROGRAM fort_tst
Run Code Online (Sandbox Code Playgroud)

这适用于英特尔和 gfortan,并且确实是比我尝试做的更好的解决方案。

特别感谢 @Federico 发布 C++/Fortran 代码,使这个解决方案变得显而易见。

更新:完整的代码,展示了如何ptr将上述内容存储在 C 中。

// C code
typedef void* arraytype;

void allocate_array(arraytype *ptr);
void deallocate_array(arraytype *ptr);
void do_something(arraytype *ptr);

int main()
{
    arraytype ptr;
    allocate_array(&ptr);    
    do_something(&ptr);
    deallocate_array(&ptr);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

以及相应的 Fortran :

!! Fortran code
MODULE arraytype_mod
    TYPE, PUBLIC :: arraytype
        DOUBLE PRECISION, POINTER :: data(:)
    END TYPE arraytype  
END MODULE arraytype_mod

SUBROUTINE allocate_array(ptr) BIND(C,name='allocate_array')
    USE iso_c_binding
    USE arraytype_mod
    TYPE(c_ptr) :: ptr
    TYPE(arraytype), POINTER :: a
    ALLOCATE(a)
    ALLOCATE(a%data(5))
    ptr = c_loc(a)
END

SUBROUTINE deallocate_array(ptr) BIND(C,name='deallocate_array')
    USE iso_c_binding
    USE arraytype_mod
    TYPE(C_PTR) :: ptr
    TYPE(arraytype), pointer :: a
    CALL c_f_pointer(ptr,a)
    DEALLOCATE(a%data)
    DEALLOCATE(a)
END

SUBROUTINE do_something(ptr) BIND(C,name='do_something')
    USE iso_c_binding
    USE arraytype_mod
    TYPE(c_ptr) :: ptr
    TYPE(arraytype), POINTER :: a
    CALL c_f_pointer(ptr,a)
    a%data = 2.5
    WRITE(6,*) a%data
END 
Run Code Online (Sandbox Code Playgroud)