如何处理不同的数组大小作为函数的参数?

How do handle varying array sizes as arguments for a function?

本文关键字:函数 参数 数组 处理 何处理      更新时间:2023-10-16

我设法手动将Fortran 77程序的5000多行代码转换为C++,但转换没有按计划进行。所以我正在尝试使用我的Fortran 77程序调试C++程序。在 fortran 中,我开发了一个子例程,它接受一个数组并将数组索引及其值打印到逗号分隔的文件中。我正在尝试在C++做类似的事情。但与"双温度1[tempi]"声明相悖。在对函数的所有调用中,数组的大小不必相同。所以我不能把它编码为"双温度1[21]",因为下一次是25。Fortran 通过引用传递数组。你建议我怎么做。

我设法为 Fortran 程序做到这一点。这个想法是从 c++ 程序中获取变量内存转储,并使用 vba 比较 Excel 中的值,以查看哪个值变化最大,然后将C++程序中的该变量作为起始调试点。

C++ 代码逻辑:

void singlearrayd(double temp1[tempi], int tempi, string str1){
for (int md_i = 1; md_i <= tempi; md_i++){
cout << temp1[md_i] << "," << str1 << "(" << md_i << ")";
}
}
int main(){
double askin[22];
double fm[26];
singlearrayd(askin,22,"askin");
singlearrayd(fm,26,"fm");
return 0;
}

Fortran 77 代码逻辑:

PROGRAM PRINT_MEMORY
real*8 :: ASKIN(21)
real*8 :: FM(25)
CALL SINGLEARRAYD(ASKIN,21,"ASKIN")
CALL SINGLEARRAYD(FM,25,"FM")
END PRINT_MEMORY
SUBROUTINE SINGLEARRAYD(TEMP1,TEMPI,STR1)
IMPLICIT NONE
CHARACTER(LEN=*) :: STR1
INTEGER*4 MD_I,TEMPI
REAL*8, DIMENSION(1:TEMPI) :: TEMP1
DO MD_I = 1, TEMPI
WRITE(51,'(ES25.16E3,A1,A25,A1,I5,A1)') TEMP1(MD_I),',',STR1,'(',
1 MD_I,')'
ENDDO
ENDSUBROUTINE SINGLEARRAYD

您的代码中存在多个问题。

在C++中,本机数组(如main()中的askin)在传递给函数时会转换为指针。 因此,无需在参数列表中的数组上声明维度,但仍然需要传递第二个参数,因为您正在指定大小。

这意味着C++函数应该具有以下形式:

void singlearrayd(double temp1[], int tempi, std::string str1)

或(等效)

void singlearrayd(double *temp1, int tempi, std::string str1)

请注意,在上面,我已通过全名指定了第三个参数的类型std::string。 在很多情况下,最好避免using namespace std

第二个问题是,您假设 Fortran 数组索引和C++数组索引是相同的。 实际上,Fortran 数组索引是从 1 开始的(默认情况下,数组的第一个元素的索引为 1),C++数组索引是从 0 开始的(数组上的第一个元素的索引为零)。 在 C++ 中使用 Fortran 数组索引会导致未定义的行为,因为它将访问有效范围之外的元素。

第三个(潜在)问题是您的函数定义了两个名为md_i的变量(一个在函数中,一个在循环中)。 最好避免这样做。

解决上述所有问题将使您的函数变为(完整)

void singlearrayd(double temp1[], int tempi, std::string str1)
{
for (int md_i = 0; md_i < tempi; ++md_i)    // note the differences here carefully
{
cout << temp1[md_i] << "," << str1 << "(" << md_i << ")";
}
}

第四个问题是C++中的main()返回int,而不是void

第五个问题是main()singlearrayd()打印数组之前不会初始化数组。 在 Fortran 中,函数的本地数组(通常)初始化为零。 在C++中,默认情况下它们是未初始化的,因此访问它们的值(例如打印它们)会产生未定义的行为。

int main()
{
double askin[21] = {0.0};   // initialise the first element.  Other elements are initialised to zero
double fm[21] = {0.0};
singlearrayd(askin,21,"askin");
singlearrayd(fm,25,"fm");
}

这将使您的代码正常工作。 然而,实际上,有一些改进是可能的。 第一个改进是使用标准容器而不是数组。 标准容器知道它们的大小,因此可以简化您的功能。 其次,通过引用传递非平凡参数(如容器或字符串) - 如果没有对参数进行任何更改,最好const引用。 与 Fortran 不同,函数参数通常默认通过引用传递,因此有必要故意在 C++ 中引入引用。

#include <vector>
void singlearrayd(const std::vector<double> &temp1, const std::string &str1)
{
for (std::size_t md_i = 0; md_i < temp1.size(); ++md_i)
{
cout << temp1[md_i] << "," << str1 << "(" << md_i << ")";
}
}
int main()
{
std::vector<double> askin(21);   // askin has 21 elements, initialised to zero
std::vector<double> fm(21);
singlearrayd(askin, "askin");
singlearrayd(fm, "fm");
}

C++容器还支持迭代器 - 在实践中更安全,并且通常更有效 - 比使用数组索引。 我将把它留给你一个练习,让你学习如何使用这些。

然而,一个关键信息是:不要以为从Fortran到C++的简单机械翻译会起作用。 您已经证明了这种假设的陷阱。 在尝试将过多代码从Fortran转换为C++之前,请花时间学习C++。 这对于使C++代码正常工作以及使其高效运行都是必要的。

更现代的实现是

#include <string>
#include <array>
#include <iostream>
template <std::size_t size, class U>
void singlearrayd(const std::array<U, size>& temp1, const std::string& str1){
int i = 0;
for (const auto& x : temp1)
std::cout << x << "," << str1 << "(" << (i++) << ")";
}
int main(){
std::array<double, 21> askin;
std::array<double, 21> fm; 
singlearrayd(askin, "askin");
singlearrayd(fm, "fm");
return 0;
}

请注意,在上面的代码中,askinfm两个数组未初始化。据推测,在实际代码中,您在调用singlarrayd之前已经初始化了它们。 另外,请记住,main必须返回一个int.

感谢您的宝贵见解和评论。我认为最好的方法是使用

void singlearrayd(double *temp1, int tempi, std::string str1)

扩展这个想法并使用谷歌做更多的研究,我能够扩展这个想法来处理 2D 和 3D 数组。

void doublearrayd(double *temp1, int tempi, int tempj, std::string str1){
for (int md_j = 1; md_j<tempj; md_j++){
for (int md_i = 1; md_i<tempi; md_i++){
std::cout << *(temp1 + md_i*tempj + md_j) << "," << str1 << "(" << md_i << ";" << md_j << ")" << std::endl;
}
}
}
void triplearrayd(double *temp1, int tempi, int tempj, int tempk, std::string str1){
for (int md_k = 1; md_k < tempk; md_k++){
for (int md_j = 1; md_j<tempj; md_j++){
for (int md_i = 1; md_i<tempi; md_i++){
std::cout << *(temp1 + md_i*tempj*tempk + md_j*tempk + md_k) << "," << str1 << "(" << md_i << ";" << md_j << ";" << md_k << ")" << std::endl;
}
}
}
}

https://en.wikipedia.org/wiki/Row-_and_column-major_order

如何将动态多维数组传递给函数?