【问题标题】:Fortran 77 handling C++ memory allocationsFortran 77 处理 C++ 内存分配
【发布时间】:2011-04-28 19:14:46
【问题描述】:

我正在尝试编写一个使用数万行 Fortran 77 代码的 C++ 程序,但遇到了一些奇怪的错误。我将三个坐标 (x,y,z) 和三个向量的地址从 C++ 传递到 fortran,然后让 fortran 在初始点上运行一些计算并在三个向量中返回结果。

我在一个 C++ 函数中这样做了几百次,离开那个函数,然后再回来做。它第一次完美运行,但第二次停止返回有用的结果(返回 nan),用于具有正 x 分量的点。

最初看起来像是一个算法问题,除了三件事:

  1. 前 200 次运行它运行良好
  2. 如果我从 fortran 中调用它并完全消除 C++,它就可以工作(对于最终程序不可行)
  3. 我曾尝试将打印语句添加到 fortran 以调试出错的地方,但结果表明,如果我将打印语句添加到特定子例程(甚至像 PRINT *、'Here' 这样简单的子程序),程序开始返回 NaN即使是第一次运行。

这就是为什么我认为这与 C 和 fortran 函数/子例程调用之间的内存分配和释放方式有关。基本设置如下所示: C++:

void GetPoints(void);

外部“C” { void getfield_(float*,float*,float*,float[],float[],float[],int*,int*); }

int main(void) { 获取点(); //作品 获取点(); //没有 }

void GetPoints(void) { 浮动 x,y,z; 整数 i,n,l; l=50; n=1; x=y=z=0.0; 浮动 xx[l],yy[l],zz[l] 对于(i=0;i<l;i++) getfield_(&x,&y,&z,xx,yy,zz,&n,&l); //将当前的xx,yy,zz存储在大的全局数组中 }

Fortran:

      SUBROUTINE GETFIELD(XI,YI,ZI,XX,YY,ZZ,IIN,NP)
      DIMENSION XX(NP),YY(NP),ZZ(NP)
      EXTERNAL T89c
      T89c(XI,YI,ZI,XX,YY,ZZ)
      RETURN
      END
!In T89c.f SUBROUTINE T89c(XI,YI,ZI,XX,YY,ZZ) COMMON /STUFF/ ARRAY(100) !Lots of calculations !Calling ~20 other subroutines RETURN END

你们有没有看到我正在制造的任何明显的内存问题?也许 fortran 认为存在但实际上被 C++ 释放的常见块?由于无法使用 print 语句进行调试,也没有时间尝试理解别人的几千行 Fortran 77 代码,我愿意尝试任何你们可以建议或想到的任何事情。

我使用 g++ 4.5.1 编译 C++ 代码和最终链接,使用 gfortran 4.5.1 编译 fortran 代码。

谢谢

**编辑:**

我已将错误追溯到我出生之前编写的一些晦涩难懂的代码片段。它似乎正在寻找多年来在更新中删除的一些常见变量。我不知道为什么它只影响一个维度,也不知道为什么通过添加打印语句可以复制该错误,但我仍然消除了它。谢谢大家的帮助。

【问题讨论】:

  • l 是否应该未初始化?这不是真正的 C++——在 C++ 中的堆栈上创建的数组必须在编译时指定它们的大小,而不是通过变量——你使用的是 C/g++ 扩展。但我不认为这是你的问题。
  • 啊,对不起,应该初始化(并且在真实代码中)。我也尝试过使用整数大小进行初始化,没有任何变化。
  • 您确定您已将 Fortran 编译为使用 C 风格的函数调用协议吗? Fortran77 没有固有的堆栈概念。
  • 我对此并不肯定。是否有特定的编译标志?我有 -lg2c,但那是因为如果我没有它会抱怨,我不完全确定它涵盖的所有内容。
  • 1.你试过调试器吗? 2. 您是否在启用所有运行时检查的情况下编译了 fortran 代码 (-fcheck=all)?

标签: c++ g++ memory-management gfortran fortran77


【解决方案1】:

您可能会遇到“off-by-one”错误。 Fortran 数组是从 1 开始的,而 C 数组是从 0 开始的。确保传入 Fortran 的数组大小不小于应有的 1。

编辑:

我猜它看起来不错...不过,我还是会尝试在 C++ 函数中分配 51 个元素,看看会发生什么。

顺便说一下float xx[l]; 不是标准的。这是一个 gcc 功能。通常你应该在这里使用new分配内存,或者你应该使用std::vector

另外,我对循环中对getfield_ 的调用感到困惑。你不应该将i 传递给getfield_ 吗?

【讨论】:

  • 但是声明为 float XX[50] 的 C 数组应该与声明为 DIMENSION XX(50) 的 fortran 数组相同 不应该吗?
  • 回答编辑:尝试 51 分并没有改变任何东西。至于循环,我只是没有显示我使用返回值的部分。但是,是的,返回值确实取决于 i(或更准确地说,初始 x、y 和 z 取决于当前迭代和一些数学卷积,但为了便于阅读,我将其省略了)
  • 接下来要检查的是你在那里拥有的大型全局数组
  • Fortran 永远不会看到全局数组,发送到 Fortran 的内容绝不依赖于全局数组。它只是用来存储结果的,结果在返回到 C++ 之前就已经很糟糕了。
【解决方案2】:

您应该在子例程T89c 中将XXYYZZ 声明为数组,如下所示:

REAL*4 XX(*)    
REAL*4 YY(*)    
REAL*4 ZZ(*)

C/C++ 通常不应该释放任何 Fortran 公共块。这些类似于 C 中的 structs(即内存在编译时保留,而不是在运行时保留)。

出于某种原因,即使没有上述声明,gfortran 似乎也接受T89c 中的以下内容:

print *,XX(1)

在编译期间,但在执行时出现分段错误。

【讨论】:

  • 我经常在运行时在C中分配structs,所以你的说法似乎不清楚。
  • 由于实际项目使用了 100 多个子例程,并且它们都使用隐式声明的变量,我只能在 C-Fortran 链接中执行此操作吗?因为我尝试过没有运气。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-09
  • 2012-05-04
  • 2014-05-30
  • 2017-03-30
  • 1970-01-01
  • 2015-05-29
相关资源
最近更新 更多