为fortran程序员在c++中传递多维数组

pass multidimensional array in C++ for a fortran programmer

本文关键字:数组 fortran 程序员 c++      更新时间:2023-10-16

我习惯用Fortran写程序(这是f90),但我需要用c++写一个程序。我对如何从c++函数传递多维数组感到非常困惑。举个例子,我想在子例程中以XYZ格式读取原子坐标列表,然后将坐标传递给主程序。

下面是用Fortran写的:

program findcenterofmass
character*2 atm(1000)
integer ttl
real*8 crd(1000,3)
call getxyz(atm,ttl,crd)
call centerofmass(ttl,crd)
end
subroutine getxyz(element,atomcount,coord)
character*2 element(1000)
integer atomcount
real*8 coord(1000,3)
open(file='test.xyz',unit=1)
read(1,*) atomcount
read(1,*)
do i=1,atomcount
   read(1,*) element(i),(coord(i,j),j=1,3)
enddo
close(unit=1)
end
subroutine centerofmass(atomcount,coord)
integer atomcount
real*8 coord(1000,3)
real*8 x,y,z
do i=1,atomcount
   x=x+coord(i,1)/atomcount
   y=y+coord(i,2)/atomcount
   z=z+coord(i,3)/atomcount
enddo
write(*,*) 'Center of mass is x: ',x,' y:',y,' z:',z
end

这里读取的测试文件是一个非常简单的CO2分子:

3
C  0.0  0.0  0.0
O -1.4  0.0  0.0
O  1.4  0.0  0.0

所以我需要在c++中做同样的过程,这部分看起来最令人困惑的是把坐标读成多维的数组,然后将数组返回给主程序。

这是c++(这有错误)-任何帮助将非常感激!

#include <stdio.h>
#include <iostream>
#include <string>
#include <sstream>
void readxyz(double& x);
int main () {
double x[100][3];
readxyz(double& x);
std::cout << " x " << x[0][0] << "n";
return 0;
}
void readxyz(double& coord[][3])
{
  int i,j,k;
  int ttl;
  int MAXATOM=1000;
  int MAXLINE=72;
  char atoms[MAXATOM][2];
  long double coord[MAXATOM][3];
  char s[MAXLINE];
  const char* filename="test.xyz";
  using namespace std;
  cout.precision(12);
  FILE *fp = fopen(filename,"r");
  fgets(s,MAXLINE,fp);
  std::stringstream stream(s);
  stream >> ttl;
  fgets(s,MAXLINE,fp);
  for (i = 0; i < ttl; i++) {
    fgets(s,MAXLINE,fp);
    std::stringstream stream(s);
    stream >> atoms[i] >> coord[i][0] >> coord[i][1] >> coord[i][2];
    }
}

注意char atoms[MAXATOM][2];,但在循环中写入atoms时没有给出足够的索引:stream >> atoms[i] //...

是否打算从文件中读取数组的实际长度?我是从Fortran中猜出来的:

read(1,*) atomcount
do i=1,atomcount
  read(1,*) element(i),(coord(i,j),j=1,3)
enddo

如果是这样,最好将数组设置为可变长度,而不是猜测最大长度(1000)。如果你的猜测太小会发生什么?这在Fortran>=90或c++中很容易做到。在Fortran中,将数组设置为"可分配的",并在从文件中读取size atomcount后分配它们。也没有必要显式地在各个过程之间传递数组的维度…

double& coord[][3]是一个二维引用数组,有一个固定维度和一个未定义维度,如果它编译的话。可能不是你想要的。被调用的函数不能在运行时确定未知维度的大小。

当你在c++中传递一个多维数组时,在底层传递的是指向第一个元素的指针。因此,为了在任何地方使用任意维度,您可以将指针和任何维度作为额外参数传递:

void readxyz(double* coord, size_t numCoords, size_t numAxes)
{   // ... access array data with:
    coord[coordIndex * numAxes + axisIndex]
    // ... or somewhat optimized in loops:
    for(int coordIndex = 0; coordIndex < numCoords; ++coordIndex) {
        double* thisCoord = coord + coordIndex * numAxes;
        cout << "(";
        for(int axisIndex = 0; axisIndex < numAxes; ++axisIndex) {
            if(axisIndex)
                cout << ", ";
            cout << thisCoord[axisIndex];
        }
        cout << ")" << endl;
   }
}

您没有详细说明在您读取信息后将如何处理它,所以我不知道数据是否必须存储为多维数组以进行进一步处理。就我个人而言,我会将其存储为Atom对象的向量,并且只使用c++ I/O而不是混合使用C(以f开始的函数)和c++(以stream结束的类):

#include <iostream>
#include <iomanip>
#include <fstream>
#include <vector>
struct Atom
// Defined as a `struct` to keep it as a simple "plain old data" (POD) type.
// It doesn't have to be this way.
{   // Store atom names as C strings; they can have up to 3 characters.
    // Possible optimization: store the atomic number (one byte) instead 
    // and use a lookup table (or std::map) to get names.
    char name[4];
    double x, y, z;
};
typedef std::vector<Atom> Atoms;
std::istream& operator >>(std::istream& is, Atom& a)
{   // Always use setw() with char* to avoid buffer overflow.
    is >> std::setw(4) >> a.name >> a.x >> a.y >> a.z;
    return is;
}
void ReadAtoms(const char* filename, Atoms& atoms)
{   std::ifstream ifs(filename);
    size_t numAtoms;
    ifs >> numAtoms;
    for(size_t i = 0; i < numAtoms; i++)
    {   Atom atom;
        ifs >> atom;
        atoms.push_back(atom);
    }
}
int main(int argc, char* argv[])
{   if(argc != 2)
    {   std::cerr << "Usage: " << argv[0] << " <filename>" << 'n';
        return 1;
    }
    Atoms atoms;
    ReadAtoms(argv[1], atoms);
    std::cout << " x " << atoms[0].x << 'n';
    return 0;
}

这并不处理输入文件中的损坏数据,但它是一个起点。