【问题标题】:How can I pass a dynamic allocated arrays to subroutines如何将动态分配的数组传递给子例程
【发布时间】:2016-12-26 08:31:42
【问题描述】:

我需要摆脱一些 Fortran 程序使用的库,所以我必须在程序中使用的库中创建所有子例程。

其中一个子例程是由 C 语言完成的内存分配。

我为分配创建了自己的子程序

MODULE ARRAY_ALLOCATION
  CONTAINS
     SUBROUTINE ARRAY_ALLOCATE (ARR, ARR_SIZE, ARR_IDX, CODE_RET)
            ! DECLARE AN ALLOCATABLE PARAMETER
            INTEGER, ALLOCATABLE, INTENT (INOUT) :: ARR(:)
            INTEGER, INTENT (IN) :: ARR_SIZE,ARR_IDX,CODE_RET
            INTEGER :: IDX_END
            IDX_END = ARR_IDX + ARR_SIZE -1
            ALLOCATE (ARR(ARR_IDX:IDX_END))
            RETURN
     END SUBROUTINE ARRAY_ALLOCATE

     SUBROUTINE ARRAY_DEALLOCATE (ARR)
            INTEGER, ALLOCATABLE, INTENT (INOUT) :: ARR(:)
            DEALLOCATE (ARR)
            RETURN
       END SUBROUTINE ARRAY_DEALLOCATE

END MODULE 

但我需要保留像这样integer alist(1) 没有属性allocatable 的数组声明。

例子

    program test1

USE ARRAY_ALLOCATION
implicit none

integer alist(1)

call ARRAY_ALLOCATE(alist,5,3,1)
CALL Test(ALIST)
CALL ARRAY_DEALLOCATE(alist)

错误

error #7976: An allocatable dummy argument may only be argument associated with an allocatable actual argument

有没有办法在声明后创建我的数组allocatable? 我正在考虑将其设为allocatable(声明后)在子例程中分配内存,这样就不必更改旧程序中的声明。

【问题讨论】:

  • 错误信息究竟是什么?关于explicit interfaces
  • 嗨,是的,这是关于显式接口错误:过程的虚拟参数有一个属性需要此过程的显式接口
  • @tsumey,请将您的代码放入问题中。例程声明以及您如何调用它就足够了。
  • 顺便说一句,您知道不必在每个子程序的 and 处使用 RETURN 并在每个程序的 and 处使用 STOP 吗?它们完全没有必要。
  • “编译器说了一些关于接口的事情……”不要这样做。如果编译器说了一些重要的话,您必须在此处准确复制消息。

标签: c arrays fortran


【解决方案1】:

您可能不想摆脱这些库,而是让代码在其中工作。

通常是这样的: 在图书馆...

MODULE A_Style   !(_*_)
IMPLICIT NONE
PUBLIC: Aye

  CONTAINS
  SUBROUTINE Aye(A)
  REAL, DIMENSION(:), ALLOCATABLE, INTENT(INOUT) :: A
  !...
  IF(ALLOCATED(A)) THEN
    WRITE(*,*)' I see A as A(',LBOUND(A,1),':',UBOUND(A,1),')'
  ELSE
    WRITE(*,*)' I see A as "UNALLOCATED"'
  ENDIF
  !...
  RETURN
  END SUBROUTINE Aye

  SUBROUTINE MakeAye(A, SizeA)
  REAL, DIMENSION(:), ALLOCATABLE, INTENT(INOUT) :: A
  !...
  IF(ALLOCATED(A)A) DEALLOCATE(A)
  ALLOCATE(A(SizeA))
  !...
  RETURN
  END SUBROUTINE MakeAye

END MODULE A_Style

使用库:

PROGRAM TestA
USE A_Style  !(_*_)
IMPLICIT NONE
REAL, DIMENSION(:), ALLOCATABLE :: A
INTEGER                         :: Size_A = 100
...
CALL Aye(A)
ALLOCATE(A(Size_A))
CALL Aye(A)
...
IF(ALLOCATED(A)) DEALLOCATE(A)
END PROGRAM TestA

有时,有趣的是,子例程需要在模块中才能看到 BOUNDS。 INTENT( OUT) 将看不到,因此需要使用 (INOUT) 或 (IN )。

【讨论】:

  • 谢谢霍尔姆兹。我无法使用这些实验室,因为它们只存在于 unix 中,而我的程序我试图在 Windows 中运行它,所以我必须创建自己的....你认为有一种方法可以使用 fortran decalred 进行分配吗像这样 DOUBLE PRECISION VREAL(1) 吗?我试图避免在我的 huge 程序中编辑所有数组声明
  • @tsumey 你儿子不必改变这些。 VREAL(1) 几乎可以在任何地方停留。您仍然没有显示错误消息,但我想它是关于 TEST,而不是关于 ALLOCATE。 你读过我的cmets吗?
  • @VladimirF 好的,看看下面答案中的错误信息。
  • 为什么你的代码都是大写的?从 Fortran 90 开始,正式允许使用小写字母,并且所有已知的编译器供应商都支持它们。全部小写字母拼写的单词比仅使用大写字母拼写的单词更易读。此外,字母 i 与数字 1, 字母 B 与数字 8, 等混淆的可能性很小,这可能是某些字体的问题。
  • 自从 50 年代的 fortran 以来,情况就不再重要了。 F90 并没有改变这一点。因此使用了多种方案。就我个人而言,我将大写字母用于固有的 Fortran 内容以及一些库调用。通常用 CamelCase 表示变量。如果您与其他人一起工作,那么每个人都在同一个页面上工作。
【解决方案2】:

有没有办法让我的数组在声明后可分配?我正在考虑使其可分配(在声明后)在子例程中分配内存,这样就不必更改旧程序中的声明。

没有。

错误信息非常明确。您不能将不可分配数组作为参数传递给需要可分配数组的函数。

以下声明:

integer alist(1)

正在创建一个大小为 1 的 1 维数组。

如果您希望您的数组是动态分配的,您必须将它们更改为可分配的或使用指针。

编辑

从这里开始:http://web.stanford.edu/class/me200c/tutorial_77/12_arrays2.html

大多数程序员更喜欢使用星号符号来强调“实际数组长度”是未知的。一些旧的 Fortran 77 程序可能会像这样声明可变长度数组:

  real x(1), y(1)

即使数组长度大于一,这也是合法的语法! 但这是一种糟糕的编程风格,强烈建议不要这样做。

附:此表示法在 INSIDE 子例程中用于定义可变长度数组参数。

编辑 2

以下代码基于您的代码,演示了 allocatable 的使用。

MODULE ARRAY_ALLOCATION
CONTAINS
 SUBROUTINE ARRAY_ALLOCATE (ARR, ARR_SIZE, ARR_IDX, CODE_RET)
        ! DECLARE AN ALLOCATABLE PARAMETER
        INTEGER, ALLOCATABLE, INTENT (INOUT) :: ARR(:)
        INTEGER, INTENT (IN) :: ARR_SIZE,ARR_IDX,CODE_RET
        INTEGER :: IDX_END
        IDX_END = ARR_IDX + ARR_SIZE -1
        ALLOCATE (ARR(ARR_IDX:IDX_END))
        ARR = 1
        RETURN
 END SUBROUTINE ARRAY_ALLOCATE

 SUBROUTINE ARRAY_DEALLOCATE (ARR)
        INTEGER, ALLOCATABLE, INTENT (INOUT) :: ARR(:)
        DEALLOCATE (ARR)
        RETURN
   END SUBROUTINE ARRAY_DEALLOCATE

subroutine Create (arr)
    INTEGER, ALLOCATABLE, INTENT (INOUT) :: ARR(:)
    call ARRAY_ALLOCATE(arr,5,3,1)
end subroutine Create

subroutine Destroy (arr)
    INTEGER, ALLOCATABLE, INTENT (INOUT) :: ARR(:)
    CALL ARRAY_DEALLOCATE(arr)
end subroutine Destroy 

END MODULE 

program Console1

USE ARRAY_ALLOCATION
implicit none

integer, allocatable :: alist(:)
if (allocated(alist)) then
    print *, "is allocated"
else
    print *, "is not allocated"
endif
call Create(alist)
if (allocated(alist)) then
    print *, "is allocated"
else
    print *, "is not allocated"
endif    
print *, alist
CALL Destroy(alist)
if (allocated(alist)) then
    print *, "is allocated"
else
    print *, "is not allocated"
endif
end program Console1

预期的结果是这样的:

is not allocated
is allocated
          1           1           1           1           1
is not allocated

编辑 3

关于可分配数组,关于为什么有人想要解除分配的简单解释可以在这里找到https://www.phy.ornl.gov/csep/pl/node17.html

可分配数组是那些显式声明的 ALLOCATABLE。可分配数组可以是过程本地的,也可以放置在模块中,对应用程序的所有过程有效。可分配数组使用 ALLOCATE 语句显式分配,并使用 DEALLOCATE 语句显式释放,或者如果它是未指定 SAVE 的本地数组,则在退出过程时自动释放。 (如果指定了 SAVE,局部可分配数组可以从一个过程执行持续到下一个执行 - 它们必须使用 DEALLOCATE 语句显式释放。)全局可分配数组一直存在,直到它被显式释放,这可能发生在一个过程中不同于分配它的那个。

可以看出,在某些情况下,如有必要,必须手动解除分配可分配数组。

【讨论】:

  • 好的,那么如何使用指针进行分配?附带问题:我不能在另一个子例程中取消分配,为什么error #7976: An allocatable dummy argument may only be argument associated with an allocatable actual argument.
  • 要使用指针,您必须将您的数组声明为指针数组:int, pointer :: array(:)。您比使用 allocate 分配它的方式与用于可分配数组的方式相同。但在大多数情况下,allocatable 具有优势。使用指针,系统将永远不会在数组超出范围时释放内存,如果不手动执行此操作,则会发生内存泄漏。
  • @tsumey,关于 allocatable 的使用,请参阅答案中的编辑 2 :)
  • “即使数组长度大于 1,这也是合法的语法!但这是一种糟糕的编程风格,强烈建议不要这样做。” 不,据我所知在 FORTRAN 77 中是不合法的。它在引入假定大小的数组之前使用,但引用除第一个元素之外的任何其他元素都是不合法的。它只是滥用了缺乏边界检查。
  • 感谢您的回答,当我在 subroutineA 中分配并尝试在 subroutineB 中释放时,释放不起作用,您是否认为我只是不释放它们并且释放会自动完成?
猜你喜欢
  • 2012-10-15
  • 1970-01-01
  • 2019-11-06
  • 2011-03-25
  • 1970-01-01
  • 2017-03-21
  • 2018-01-15
  • 1970-01-01
  • 2023-02-06
相关资源
最近更新 更多