【发布时间】:2014-07-22 14:42:14
【问题描述】:
我在调试一些代码时偶然发现了一个与接口有关的特定问题,其中被调用的子例程有一个 2 级的虚拟参数,但有一个 1 级的实际参数。由此产生的参数差异导致读取无效。
为了重现我创建了一个小程序(暂时忽略 cmets ! <>):
PROGRAM ptest
USE mtest ! <>
IMPLICIT NONE
REAL, ALLOCATABLE, DIMENSION(:) :: field
INTEGER :: n
REAL :: s
n = 10
ALLOCATE(field(n))
CALL RANDOM_NUMBER(field)
CALL stest(n, field, s)
WRITE(*,*) s
DEALLOCATE(field)
END PROGRAM
和一个模块
MODULE mtest ! <>
IMPLICIT NONE ! <>
CONTAINS ! <>
SUBROUTINE stest(n, field, erg)
INTEGER :: n
REAL, DIMENSION(n,n) :: field
REAL :: erg
erg = SUM(field)
END SUBROUTINE
END MODULE ! <>
据我了解,这个子例程通过放置在模块中而获得一个自动(显式?)接口。问题是,实际的field 的长度为10,而子例程对长度为10x10=100 的字段求和,这在valgrind 中作为无效读取清晰可见。
然后我在没有模块的情况下测试了相同的代码,即所有标有! <> 的行都被删除/注释了。结果,gfortran 的-Wimplicit-interface 抛出了一个警告,但是代码和以前一样工作。
所以我的问题是:处理这种情况的最佳方法是什么?我应该总是放置一个通用接口吗
INTERFACE stest
MODULE PROCEDURE stest
END INTERFACE
在模块中?还是应该用延迟形状的数组(即REAL, ALLOCATABLE, DIMENSION(:,:) :: field)替换字段的定义?
编辑:为了更准确地说明我的问题,我不想解决这个特定问题,但想知道该怎么做才能从编译器获得更好的诊断输出。
例如给定的代码不会给出错误消息,并且原则上会产生分段错误(尽管代码没有注意到它)。放置一个通用接口至少会产生一个错误,抱怨没有找到stest 的匹配定义,这也不是很有帮助,尤其是在你没有源代码的情况下。只有延迟形状的数组会导致可以理解的错误消息(排名不匹配)。
如果我想知道,这就是为什么自动模块界面没有给出类似的警告/错误消息。
【问题讨论】:
-
最简单的解决方案是在主程序中将字段声明为二阶数组。 (即
REAL, ALLOCATABLE, DIMENSION(:,:) :: field。)并将其分配给子例程中使用的大小。那么实际和虚拟参数是一致的。您是否有不想这样做的理由? -
也可以通过field(1)获取“序列关联”。
-
可以使用序列关联,但元素不匹配的数量也必须固定。 rank-1 100 个元素可以匹配 rank-2 10x10。