调用C_F_POINTER时,Fortran指针和可分配的Fortran的区别 [英] Difference in Fortran pointer and Fortran allocatable in calling C_F_POINTER

查看:170
本文介绍了调用C_F_POINTER时,Fortran指针和可分配的Fortran的区别的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题是,"C_F_POINTER"以可分配数组"作为论点成功编译(ifort版本19.0.5.281),并且与使用指针"作为其参数的情况完全相同.

The thing is, 'C_F_POINTER' compiles successfully(ifort version 19.0.5.281) with 'allocatable arrays' as its arguemnt, and it works in the exactly same way with the case in which 'pointer' is used as its argument.

program test1
    use mkl_spblas
    use omp_lib
    use iso_c_binding

    implicit none
    integer, parameter    :: DIM_ = 4, DIM_2 = 6
    integer               :: stat, i
    integer               :: irn(DIM_2), jcn(DIM_2)
    real*8                :: val(DIM_2)
    integer(c_int)        :: indexing
    integer               :: DIM_r, DIM_c
    type(c_ptr)           :: rows_start_c, rows_end_c, col_indx_c, values_c
(*1)!integer,allocatable   :: rows_start_f(:), rows_end_f(:), col_indx_f(:)
    !real*8 ,allocatable   :: values_f(:)
(*2)integer  ,pointer     :: rows_start_f(:), rows_end_f(:), col_indx_f(:)
    real*8   ,pointer     :: values_f(:)
    type(SPARSE_MATRIX_T) :: mat1, mat2

    irn = (/ 2, 2, 3, 4, 0, 0 /)
    jcn = (/ 1, 2, 3, 2, 0, 0 /)
    val = (/ 5, 8, 3, 6, 0, 0 /)

    call omp_set_num_threads(1)

    stat = mkl_sparse_d_create_coo (A=mat1, indexing=SPARSE_INDEX_BASE_ONE, &
                                    rows=DIM_, cols=DIM_, nnz=DIM_,&
                                    row_indx=irn, col_indx=jcn, values=val  )
    if (stat /= 0) stop 'Error in mkl_sparse_d_create_coo'

    stat = mkl_sparse_convert_csr (source=mat1,&
                                   operation=SPARSE_OPERATION_NON_TRANSPOSE, &
                                   dest = mat2 )
    if (stat /= 0) stop 'Error in mkl_sparse_convert_csr'

    stat = mkl_sparse_d_export_csr(mat2, indexing, DIM_r, DIM_c,  &
                                   rows_start_c, rows_end_c, col_indx_c, values_c)

(*3)call c_f_pointer(rows_start_c, rows_start_f, [DIM_r])
    call c_f_pointer(rows_end_c  , rows_end_f  , [DIM_c])
    call c_f_pointer(col_indx_c  , col_indx_f  , [rows_end_f(DIM_r)-1])
    call c_f_pointer(values_c    , values_f    , [rows_end_f(DIM_r)-1])

    stat = mkl_sparse_destroy (A=mat1)
    if (stat /= 0) stop 'Error in mkl_sparse_destroy (mat1)'

    stat = mkl_sparse_destroy (A=mat2)
    if (stat /= 0) stop 'Error in mkl_sparse_destroy (mat2)'

    call mkl_free_buffers

(*4)print *, 'rows_start'
    print *, rows_start_f
    print *, 'rows_end'
    print *, rows_end_f
    print *, 'col_indx'
    print *, col_indx_f
    print *, 'values'
    print *, values_f
    print *, 'indexing'
    print *, indexing
    print *, 'size(values_f,1)'
    print *, size(values_f,1)

end program test1

在上面的测试代码中,我在代码左侧将一些点标记为(* 1),(* 2)等.

In the test code above, I marked some points as (*1), (*2), and so on in the leftside of the code.

(* 1)& (* 2):代码的可分配数组版本和指针版本 (* 3):我称之为"C_F_POINTER" (* 4):打印语句以查看输出

(*1) & (*2) : allocatable array version and pointer version of the code (*3) : where I call 'C_F_POINTER' (*4) : print statements to see the output

在(* 1)和(* 2)情况下,结果完全相同",并且所有值都已正确转换为所需的CSR格式.

The results are 'exactly' the same in both (*1), and (*2) case, and all values are properly converted into desired CSR format.

 rows_start
           1           1           3           4
 rows_end
           1           3           4           5
 col_indx
           1           2           3           2
 values
   5.00000000000000        8.00000000000000        3.00000000000000     
   6.00000000000000     
 indexing
           1
 size(values_f,1)
           4

2年前我在StackOverflow中发现了类似的问题(

I found a similar question in StackOverflow 2 years ago (difference between fortran pointers or allocatable arrays for c_f_pointer call).

这个问题现在正在我脑海中问完全相同的问题.

This question is asking the exactly the same questions in my mind right now.

如果我用我的话来回答问题,

If I rearange questions in my words,

  1. 指针与可分配数组之间的区别?
  1. Difference between pointer and allocatable array?
    • In C, as far as I know, the arrays are stored in contiguous memory and can be represented by the pointer which points its 1st element. And in Fortran90, if I pass a array into a subroutine as 'assumed-size array', the code behaves like it never cares about how it's allocated, how it's size is like, and treates the array as 1D being stored in contiguous site.
    • In below code, the subroutine 'assign_A' just gets the 'tot_array(1,2)' as its starting point, and do its work on contiguous site and seems to do it even out of bound of 'tot_array'!! (tot_array is 2x2 matrix, and assign_A's do loop runs 5 times starting at tot_array(1,2)) I was 'feeling' the pointer and allocatable arrays are similar stuff in this sense. But apparently, as the answers in difference between fortran pointers or allocatable arrays for c_f_pointer call, they are different things. Why arrays acts like pointer when they are passed to subroutine as 'assumed-size' one?

program assumed_size_array_test
  implicit none
  external assign_A
  real*8 :: tot_array(2,2)
  integer:: i

  ! Initially 'tot_array' set to be 1.d0
  tot_array = 1.d0

  write(*,*) 'Before'
  write(*,'(5f5.2)') tot_array

  call assign_A(tot_array(1,2))

  write(*,*) 'After'
  write(*,'(5f5.2)') tot_array

end program

subroutine assign_A(A)
  implicit none
  real*8, intent(inout) :: A(*)
  integer :: i

  do i = 1,5
    A(i) = 2.d0
  enddo

end subroutine

 Before
 1.00 1.00 1.00 1.00
 After
 1.00 1.00 2.00 2.00

  1. 在Fortran90中调用"C_F_POINTER"时使用可分配数组"和指针"有什么区别吗?
    • 我使用的是ifort版本19.0.5.281,据我检查,该编译器似乎给我完全相同的结果.如果可以的话,我更喜欢使用allocatble数组而不是指针.将'allocatable array'和'pointer'与'C_F_POINTER'一起使用有什么区别,这样做有什么我需要注意的吗?
    • 答案在fortran指针之间的差异或用于c_f_pointer调用的可分配数组表示,我应该使用指针,而不是将可分配数组与C_F_POINTER一起使用,但是似乎这是一个持续存在的问题,当时尚未完全解决.为什么为fortran指针设计的'C_F_POINTER'在可分配数组上可以正常工作并且结果是否相同,是否有任何结论?
  1. Is there any difference in using 'allocatable array' and 'pointer' in calling 'C_F_POINTER' in Fortran90?
    • I used ifort version 19.0.5.281, and this compiler seems to give me exactly the same results as far as I checked. If it's okay, I prefer to use allocatble arrays instead of pointers. Is there any difference in using 'allocatable array' and 'pointer' with 'C_F_POINTER', and is there anything that I should be aware of in doing so?
    • The answers in difference between fortran pointers or allocatable arrays for c_f_pointer call says that I SHOULD use pointers, not using allocatable arrays with C_F_POINTER, but it seems it's some ongoing issue that was not concluded exactly at that time. Is there any conclusion in why 'C_F_POINTER', which is designed for fortran pointer, works fine with allocatable arrays and is result is the same?

感谢您阅读此问题.

推荐答案

很明显,Fortran POINTER变量和ALLOCATABLE变量在内部实现上有很多共同之处.其中大多数都在后台,不应直接访问.两者都分配一些内存,并且可能使用相同的操作系统或C运行时库的分配器.例如,malloc().

Obviously, both Fortran POINTER variables and ALLOCATABLE variables have a lot of common in their internal impementation. Most of that is under the hood and should not be accessed directly. Both allocate some memory and probably use the same operating system's or C runtime library's allocator. For example, malloc().

在这两者中,都有一些分配或指向并由简单地址(对于标量)或数组描述符(对于数组)描述的内存.

In both there is some memory allocated or pointed to and described by a simple address (for scalars) or by an array descriptor (for an array).

指针和可分配变量的主要区别在于您可以对它们执行的操作以及编译器将为您执行的操作.您可以将可分配表视为类似于C ++中的std::unique_ptr的一种智能指针".回想一下在C ++中发生的情况,您有newdelete,它们依次调用了mallocfree,但是不允许您混合使用它们.而且当然也不允许您手动修改C ++智能指针中存储的地址.

Pointers and allocatable variables mainly differ in what you can do with them and what the compiler will do with them for you. You can think of allocatables as a sort of "smart pointers" quite similar to std::unique_ptr in C++. Recall what happens in C++ you have new and delete which in turn call malloc and free but you are not allowed to mix them. And you are certainly not allowed to manually modify the address stored in a C++ smart pointer either.

将可分配变量发送给需要指针的过程时,任何事情都可能发生,这是未定义的行为.但是,如果内部隐藏结构具有相似的布局,则可能会发生这种情况,您实际上将可分配内部结构设置为指向未通过可分配内部空间分配的某些内存.然后,您可能会认为一切正常,并且有了新功能.但是,当释放的时间到了,可分配的数据通常会自动释放时,它很可能以非常无法预测的方式失败.它可能会在非常奇怪的代码位置崩溃,结果可能是错误的,依此类推.任何事情都可能发生.

When you send an allocatable variable to a procedure that expects a pointer, anything can happen, it is an undefined behaviour. But, if the internal hidden structure has a similar layout, it may happen that you actually set the allocatable internals to point to some memory that was not allocated through allocatable. You may then think that everything is OK and you have a new feature. However, when the time for deallocation comes, and allocatables are often deallocated automatically, it can easilly fail in very unpredictable ways. It can crash in very strange places of the code, the results can be wrong and so on. Anything can happen.

例如,这个极其丑陋的程序也对我有用(在gfortran中):

For example, this extremely ugly program works for me too (in gfortran):

subroutine point(ptr, x)
  pointer :: ptr
  target :: x
  ptr => x
end subroutine

  interface
    subroutine point(ptr, x)
      allocatable :: ptr
      target :: x
    end subroutine    
  end interface

  allocatable z

  y = 1.0

  call point(z, y)

  print *, z
end

但是您应该从不做这样的事情.这确实是非常非常错误的事情.如果将z用作局部变量,以便将其释放,或者尝试取消分配它,则它将崩溃.那是因为编译器唯一的信息就是地址.在内部,可分配地址实际上看起来与指针相同.它只是一个地址(标量).唯一的区别是您可以使用它做什么,以及编译器将自动为您执行什么操作.

But you should never do stuff like this. It is really something very, very wrong. If you make z a local variable, so that it is deallocated, or if you try to deallocate it, it will crash. That is because the only information the compiler has is the address. Internally, the allocatable really looks the same as a pointer. It is just an address (for a scalar). The only difference is what you are allowed to do with it and what the compiler will do for you automatically.

这甚至不会崩溃,因为我提到了内部实现上的相似之处.但这同样是错误的.

This won't even crash, because the internal implementation similarities I mentioned. but it is no less wrong.

subroutine point(ptr, x)
  pointer :: ptr
  target :: x
  ptr => x
end subroutine     

  interface
    subroutine point(ptr, x)
      allocatable :: ptr
      target :: x
    end subroutine    
  end interface

  allocatable z
  pointer y

  allocate(y)
  y = 1.0

  call point(z, y)

  print *, z

  deallocate(z)
end

它之所以幸存,是因为可分配的指针和指针都在gfortran中使用了相同的内部分配器(malloc),并且它们都被实现为一个简单的地址.

It just survives because both allocatable and pointer use the same internal allocator (malloc) in gfortran and they are both implemented as a simple address.

这篇关于调用C_F_POINTER时,Fortran指针和可分配的Fortran的区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆