微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

“C 指针技巧”允许不匹配的 Fortran 数组等级

如何解决“C 指针技巧”允许不匹配的 Fortran 数组等级

我正在编写一个 HDF5 包装子例程,它将从/向 HDF5 文件内的数据集读/写 任何 形状的双精度数组。为了实现这一点,我使用了一些 C 指针技巧,这样子例程只接受数组的第一个元素作为 val,但它实际上使用临时缓冲区 buf(1:sz_buf) 读取/写入整个数组。

到目前为止,我对 read 子程序有以下内容(在删除错误检查以保持简洁之后):

SUbroUTINE hdf5_read_array_d( fname,path,name,val,dims )
  USE ISO_C_BINDING,ONLY: C_SIZE_T,C_LOC,C_F_POINTER

  ! Input arguments
  CHaraCTER(LEN=*),INTENT(IN) :: fname,name
  REAL(KIND(1.D0)),TARGET,INTENT(OUT) :: val
  INTEGER,DIMENSION(:),INTENT(IN) :: dims

  ! Internal variables
  INTEGER(KIND=HID_T) :: h5root,h5path,h5dset
  INTEGER(KIND=HSIZE_T),DIMENSION(SIZE(dims)) :: h5dims
  REAL(KIND(1.D0)),POINTER :: buf
  INTEGER(KIND=C_SIZE_T) :: sz_buf
  INTEGER :: dim

  ! Open the file in read-only mode
  CALL h5fopen_f( TRIM(fname),H5F_ACC_RDONLY_F,h5root,ierr )

  ! Open the pre-existing path in the file as a group
  CALL h5gopen_f( h5root,TRIM(path),ierr )

  ! Open the dataset
  CALL h5dopen_f( h5path,TRIM(name),h5dset,ierr )

  ! Convert dims to HSIZE_T
  h5dims(:) = dims(:)

  ! C pointer trickery: cast double -> void* -> double*
  sz_buf = PRODUCT(dims)
  ALLOCATE( buf( sz_buf ) )
  CALL C_F_POINTER( C_LOC(val),buf,(/sz_buf/) )

  ! Read data from dataset through buffer
  CALL h5dread_f( h5dset,H5T_NATIVE_DOUBLE,h5dims,ierr )

  ! Clean up and close HDF5 file
  NULLIFY(buf)
  CALL h5dclose_f( h5dset,ierr )
  CALL h5gclose_f( h5path,ierr )
  CALL h5fclose_f( h5root,ierr )

  RETURN
END SUbroUTINE hdf5_read_array_d

现在,问题是,除了/代替DEALLOCATE(buf)之外,我还需要放入NULLIFY(buf)吗?

任何帮助将不胜感激。

注意:我知道 Fortran 2018 包含假定秩数组 val(..) 可以优雅地解决这个问题。但同样,这是一个较新的功能,可能尚未被所有编译器实现。

编辑:在 C_F_POINTER() 上,这是 Metcalf、Reid 和 Cohen(第 4 版,不是包含 Fortran 2018 内容的最新版本)的屏幕截图:

Metcalf_et_al_C_F_POINTER

解决方法

您可以使用 C 风格的指针技巧来做您想做的事,但您在方法中需要解决一些问题:

  • 您的 allocate(buf) 存在内存泄漏
  • 你(巧妙地)在 val 的标量性质上撒谎
  • 你会让任何阅读你代码的人都感到非常困惑

之所以如此令人困惑,是因为您不需要做这种诡计。这也是为什么我不会向您展示如何去做,或者解决“我需要取消分配和取消分配吗?”的问题。

您知道您有一个数组 val 可以将 n 值塞入一个连续的块中。您担心那不能这样做,因为您(不使用假定等级的虚拟对象)必须匹配数组形状。不用担心。

  integer :: a(2,2,2),b(4,c(4,4)

都是具有 16 个元素的数组。也是

  integer :: d(16)

您可以将实际参数 abc 与虚拟参数 d 相关联。让我们看看它的实际效果:

  implicit none

  integer :: a(2,4)

  call set_them(a,SHAPE(a))
  call set_them(b,SHAPE(b))
  call set_them(c,SHAPE(c))

  print '(16I3)',a,b,c

contains

  subroutine set_them(d,dims)
    integer,intent(in)  :: dims(:)
    integer,intent(out) :: d(PRODUCT(dims))

    integer i
    d=[(i,i=1,SIZE(d))]
  end subroutine

end program

您甚至可以通过这种方式关联数组部分来定义部分。

您可以在此处看到有关此序列关联的其他几个问题,特别是查看数组形状的变化。这个答案更像是一种动机,即在尝试做一些复杂的事情时要寻找什么。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。