特色技术
专业从事预应力结构体系的设计、施工一体化解决方案
VC++与fortran混合编程中二维数组处理的技术
- 分类:程序技术
- 作者:
- 来源:
- 发布时间:2015-11-22 14:55
- 访问量:
【概要描述】一、概述: 计算机现已经成为结构设计人员必不可少的设计工具。从结构计算到施工图绘制,均可利用计算机完成。计算机的应用不仅大大提高了设计进度和设计质量,而且还使设计人员从繁重的结构计算很枯燥无谓的绘图工作中解脱出来。 高级计算机语言出现以前,所有程序都采用机器语言或汇编语言编写,程序员的大部分精力用于程序排错上,花在程序员身上的经费已经接近计算机本身的投资。1957年4月,Fortran语言之父
VC++与fortran混合编程中二维数组处理的技术
【概要描述】一、概述: 计算机现已经成为结构设计人员必不可少的设计工具。从结构计算到施工图绘制,均可利用计算机完成。计算机的应用不仅大大提高了设计进度和设计质量,而且还使设计人员从繁重的结构计算很枯燥无谓的绘图工作中解脱出来。 高级计算机语言出现以前,所有程序都采用机器语言或汇编语言编写,程序员的大部分精力用于程序排错上,花在程序员身上的经费已经接近计算机本身的投资。1957年4月,Fortran语言之父
- 分类:程序技术
- 作者:
- 来源:
- 发布时间:2015-11-22 14:55
- 访问量:
一、概述:
计算机现已经成为结构设计人员必不可少的设计工具。从结构计算到施工图绘制,均可利用计算机完成。计算机的应用不仅大大提高了设计进度和设计质量,而且还使设计人员从繁重的结构计算很枯燥无谓的绘图工作中解脱出来。
高级计算机语言出现以前,所有程序都采用机器语言或汇编语言编写,程序员的大部分精力用于程序排错上,花在程序员身上的经费已经接近计算机本身的投资。1957年4月,Fortran语言之父John Warner Backs(巴克斯)研发出了Fortran语言编译系统,可以快捷地把含有大量数学公式的代码翻译成目标程序。后来,各种高级语言陆续出现,其中最为出色的无疑是C++。目前,C++与Fortran语言的混合编程应用已经越来越广泛。在结构计算领域,网络资源上有大量免费的Fortran程序集供工程界使用。众所周知,Fortran语言的特长在计算,C++的特长在图形系统,因此将二者有机的结合可以起到一些特殊的效果。尽管混合编程给程序员带来一些额外的挑战,但其现实价值远超过采用该技术所带来的麻烦,主要体现在如下几方面:
(1)充分利用现有的Fortran代码,提高结构计算代码资源的可复用性,从而有效缩短结构计算软件的开发周期。
(2)开发出无法用单一语言实现的功能模块,提高软件的集成度和可操作性。
(3)提高软件的执行速度和可维护性。
(4)多个单位协同开发集成系统时,各协作单位通过约定数据接口,独立开发各自独立的功能模块,实现对技术秘密和数据秘密的保护。
二、混合编程的三种具体实现模式:
1、将各功能模块源代码在各自对应的开发平台上编译连接成可执行代码,然后在主执行程序中通过外壳调用命令调用各个独立执行程序,主模块与功能模块之间的数据交换通过数据文件来完成。
2、利用obj文件将各功能模块在各自对应的开发平台上进行编译,得到各自的obj文件,然后在主模块开发平台上进行集成链接,生成可执行文件。
3、将各功能模块源代码在各自对应的开发平台上编译生成dll动态连接库,然后在主模块中通过约定的接口动态调用该功能模块。
三、VC与Fortran混合编程的接口规范
1、变量、函数(子程序)标识符的命名约定
需要注意的是,Fortran不区分字母的大小写,而C++严格区分大小写。因此Fortran中的子程序(函数)的名字在C语言中声明、调用时是以该名称的全部小写方式实现的。
2、堆栈的调用约定
不同的语言定义了不同的调用约定。对于Fortran语言,缺省的调用约定是STDCALL约定。C++可以使用的调用约定有:CDECL、STDCALL约定,缺省为CDECL约定。在C++于Fortran语言混合编程时,必须使用相同的调用约定,因此使用STDCALL约定。
3、参数传递约定
Fortran语言中所有参数均按照传址方式传递,C++中默认只有数组是以传址方式传递,其他参数都以传值的方式传递。因为C++对指针操作非常方便,所以推荐在C++函数中所有参数都采用传址的方式。参数中需要特别注意的是数组的传递。
本文主要就是解决结构设计程序中经常会出现的二维数组(矩阵)问题如何处理。
四、混合编程的具体实现过程:
本文所用的Windows操作系统下的软件环境是在计算机上同时安装了MicroSoft Visual C++6.0和Compaq VisualFortran 6.6。
以结构设计人员都熟悉的杆件单元刚度矩阵为例,以下是Fortran的源代码,源代码已经被本文作者按照Fortran90进行了一些改进。
1、用C++调用Fortran:
subroutine KE0(E0,AI,F,AL,AKE)
implicit none
real*8::E0,AI,F,AL
real*8::AKE(6,6)
integer I,J
real*8 EI,EFL,EIL1,EIL2,EIL3
real ALFI,ALFF
COMMON/STIFF/ALFI,ALFF
AKE=0
EI=E0*AI*ALFI
EFL=E0*F*ALFF/AL
EIL1=4.0*EI/AL
EIL2=6.0*EI/AL**2
EIL3=12.0*EI/AL**3
AKE(1,1)=EFL
AKE(2,2)=EIL3
AKE(3,2)=-EIL2
AKE(3,3)=EIL1
AKE(4,1)=-EFL
AKE(4,4)=EFL
AKE(5,2)=-EIL3
AKE(5,3)=EIL2
AKE(5,5)=EIL3
AKE(6,2)=-EIL2
AKE(6,3)=0.5*EIL1
AKE(6,5)=EIL2
AKE(6,6)=EIL1
!forall(I=1:6,J=1:6,I<J)AKE(I,J)=AKE(J,I)
write(*,*) "Fortran Is Show"
write(*,"(6f10.3)") AKE
end
BLOCK DATA
COMMON/STIFF/ALFI,ALFF
DATA ALFI,ALFF/1.0,1.0/
end
其中E0为材料弹性模量
AI为截面的惯性矩
F为截面积
AL为杆件长度
AKE为单元刚度矩阵,是二维数组,是输出参数。
这里仅仅是输入了上半角的矩阵,下半角的矩阵处理在注释后面。将以上代码存为bar.f90。
以下是C++语言源程序
#include "stdafx.h"
#include <iostream>
using namespace std;
extern "C" {void _stdcallKE0(double*,double*,double*,double*,double*);}
int main(int argc, char* argv[])
{
double*mytwo;
doublee0=1.0;
doubleai=1.0;
doublef=1.0;
doubleal=1.0;
introw=6;
intcol=6;
mytwo=newdouble[36];
KE0(&e0,&ai,&f,&al,mytwo);
cout<<endl;
cout<<"C++Is Show Now"<<endl;
for(inti=0;i<6;++i)
{
for(intj=0;j<6;++j)
{
cout.setf(ios::fixed);
cout.setf(ios::showpoint);
cout.precision(3);
cout<<mytwo[6*i+j]<<" ";
}
cout<<endl;
}
delete[]mytwo;
mytwo=0;
return0;
}
将这段程序存为KE0.cpp,然后在VC++6.0中进行编译运行,得到如下图结果,可以看出两者的运行结果是一样的。
以上C++程序中使用了一维数组mytwo来传递Fortran中的二维数组AKE。因为Fortran中多维数组的元素在内存的连续模块中排列情况是以一种称为"Column Major"的方式来排列,也就是它的数据是从沿着低维向高维排列的,而C++中顺序正好相反。这也是导致很多Fortran程序与C++程序无法混合编程的重要原因之一。"Column Major"的意义对二维数组来说就是:数组存放在内存中,先放入Column中每个Row的元素,第一个Column放完了再放第二个Column,以A(3,3)为例,如下
A(1,1)=>A(2,1)=>A(3,1) (先放第一个Column中的元素)
A(1,2)=>A(2,2)=>A(3,2) (再放第二个Column中的元素)
A(1,3)=>A(2,3)=>A(3,3) (最后放第三个Column中的元素)
而在C++中则是
A(1,1)=>A(1,2)=>A(1,3) (先放第一个Row中的元素)
A(2,1)=>A(2,2)=>A(2,3) (再放第二个Row中的元素)
A(3,1)=>A(3,2)=>A(3,3) (最后放第三个Row中的元素)
因此需要特别说明的是,若在C++中直接用二维数组来传递Fortran的二维数组是不可能的。
2、用Fortran调用C++
在完成了C++对Fortran调用以后,若需要将C++数组传回Fortran也可以用一维数组的方式。建立ForKe.for的Fortran Console程序,程序代码如下:
programforke
implicitnone
integerrow,col
real*8::KE(6,6)
row=6
col=6
callpasske0(KE,6,6)
write(*,*)
print*, 'Return To Fortran Show'
write(*,"(6F10.3)")KE
endprogram forke
其中passke0为调用的C++函数,参数KE为二维数组。
在CPP文件中将原先的main函数中的内容移植到函数PASSKE0()中,注意此时函数名称应该为大写。
源代码如下:
#include "stdafx.h"
#include <iostream>
using namespace std;
extern "C" {void _stdcallKE0(double*,double*,double*,double*,double*);}
extern "C" {void _stdcallPASSKE0(double*, int*, int*);}
void _stdcall PASSKE0(double*kk, int*row, int*col)
{
doublee0=1.0;
doubleai=1.0;
doublef=1.0;
doubleal=1.0;
KE0(&e0,&ai,&f,&al,kk);
cout<<endl;
cout<<"C++Is Show Now"<<endl;
for(inti=0;i<6;++i)
{
for(intj=0;j<6;++j)
{
cout.setf(ios::fixed);
cout.setf(ios::showpoint);
cout.precision(3);
cout<<kk[6*i+j]<<" ";
}
cout<<endl;
}
}
运行以后得到如下图所示的结果,证明二维数组又从C++传递到了Fortran中。
扫二维码用手机看