0

我是 FORTRAN 的绝对初学者,我煞费苦心地将一个 1000 行的 FORTRAN 源文件转换为 MATLAB 供我自己使用。到目前为止,我通过放入大量PRINT语句来跟踪变量并确保数学正常工作,这一切都很好。我在 FORTRAN 中遇到了一件我无法解决的奇怪事情。

有一个子程序被定义为

subroutine addprc 
complex tmat,b,ci,c1,c2,cim,ab1(50),ab2(50),acans(10,2),fg1(50),fg2(50)
common dtr,rtd,pi 
common /mtxcom/ nrank,nranki,tmat(50,50),b(50,50),cmxnrm(25)
common /cmvcom/ nm,kmv,cmv,twm,prodm 

下面还有更多commondimension分配,但它们不应该是相关的。第一次ab1(50)定义数组是在这个子程序中。对于我的 MATLAB 脚本,我已将其初始化为ab1=zeros(1,50);. 然后在此循环中使用它(在 FORTRAN 中):

do 20 n = 1,nrank 
np = n+nrank 
cn = real(n)
n1 = n+1 
c1 = 4.0*ci**n 
c2 = 4.0*ci**n1 
p1 = cn*costh*pnmllg(n1)-(cn+cmv)*pnmllg(n) 
p2 = cmv*pnmllg(n1) 
ab1(n-ij) = c1*p2*uu1 
ab1(np-ijt) = -c2*p1*uu1 
ab2(n-ij) = -c1*p1*uu2 
ab2(np-ijt) = c2*p2*uu2 

此时我已经检查了所有其他变量的值,它们与我的 MATLAB 脚本一致。向后工作我发现了ab1已经包含值的问题。循环之前的打印命令,例如

PRINT *,'before ab1', SUM(ab1)
do 20 n = 1,1

返回(107.500008, 5.38305187)。它是一个复数,所以这两个值都很好,但它里面有什么东西不好吗?事实上,唯一ab1使用的时间就是在这段代码中。

我在这里有什么遗漏吗?我查看了源文件 (ctrl-f:ab1) 以找到它的任何实例。原始源代码是一个 .for 文件,我在 Eclipse 下使用 gfortran 编译了它们。谢谢你的时间。

4

2 回答 2

2

ab1是小数组(50 * 8 = 400 字节),因此很可能分配在程序堆栈上。堆栈用于存储局部变量以及调用子程序时调用者的返回地址。最初,堆栈页面包含全零,但随着子程序被调用,它会增长并填充一些数字。稍后当子例程返回时,堆栈指针已更改,但值仍保留在堆栈上,稍后会被新调用覆盖。当ab1被分配时,它的内存最初会被来自以前调用的子程序的旧堆栈值填充。大多数 FORTRAN 编译器默认不会生成初始化堆栈变量的指令,因为这可能是一项昂贵的操作。

如果您的程序是完全确定性的,即没有依赖于某些(伪)随机数的值的子例程调用,或者初始 PRNG 种子始终相同,则没有返回地址,堆栈的所有其他内容都将转到在您的程序的许多执行之间保持相同,例如晚上一次和早上一次。

这就是为什么SUM(ab1)总是返回(107.500008, 5.38305187)。它实际上是一个 GoodThing(tm) - 它表明您的计算机是一个确定性设备,即它在给定相同算法和相同输入的情况下再现输出,因此可用于执行编程任务。这也是一个 BadThing(tm),因为可预测的堆栈值是对操作系统安全性的许多攻击的基础,但这超出了您的问题范围。

于 2013-11-14T14:01:03.587 回答
1

只是想知道:你会在那里期待什么?

这些值来自内存。当您调用定义变量的某些内容(函数/程序/子例程)时,一些内存与定义的变量相关联,但是在您为变量分配某些内容之前,它将使用之前内存中存在的任何内容。由于计算机内存不断被重用而不被重置,您可能会从另一个已结束的调用函数/子例程的变量中获取值,来自其他进程的一块内存(除了必须由内核清空的某些页面)它们可能包含私人数据)或其他任何内容。一般规则是,除非他非常需要,否则没有人会执行将内存设置为某个值这样昂贵的操作(例如,在这里您确实将与变量关联的内存设置为您需要的数字,或者当不擦除内存是一个潜在的安全漏洞时) .

总结一下:这些值来自未初始化的内存。确切内容未定义。

访问未初始化的内存不会出错,因为检查内存是否已初始化是一项非常昂贵的操作。但是在这种情况下,有些工具会向您显示错误:

  1. 如果你想知道你的程序是否访问了未初始化的内存,你可以在valgrind. 请注意,这样的检查非常昂贵:在 valgrind 下,您很容易让程序运行得更糟,然后慢一个数量级。
  2. 还有一个 clang MemorySanitizer过滤器声称只需要 3 倍的减速就可以完成这项工作,但是在使用 clang 之前,您需要将 fortran 程序转换为 C(例如使用 f2c),因为它是 C/C++ 编译器。您还需要在启用此过滤器的情况下编译您的程序使用 clang 的所有库。
于 2013-11-09T19:12:22.527 回答