gfortran编译过程示例

1. 示例内容

① 创建一个自定义的复数类型complex_os, 包含两个元素: 实部和虚部;
② 创建两个函数sum_c及modu, 分别计算两个复数的和及某个复数的模;
③ 简单的测试函数main.

内容很简单, 但主要目的是以此来试图解释gfortran的编译过程, 希望对各位有所帮助.

2. 示例实现

为实现以上目的, 我们建立三个文件:main.f90, module_test.f90, modu.f90, 分别用来存放测试函数main, 模块module_test(包含类型定义及函数sum_c), 函数modu等内容. 以下是三个源文件及内容(makefile文件仅供编译参考):

module_test.f90 ———|
main.f90 ———————|——fortran
modu.f90 ——————|
makefile ——————–|

module module_test
implicit none
type:: complex_os       ! 定义复数类型
        real :: re      ! real part
        real :: im      ! image part
end type complex_os
contains
type(complex_os) function sum_c(a,b)
	! 计算两个复数的和
        type(complex_os),intent(in)::a,b
        sum_c%re=a%re+b%re
        sum_c%im=a%im+b%im
end function sum_c
end module module_test
real function modu(a)
! 计算类型complex_os的模
use module_test
implicit none
type(complex_os),intent(in)::a
modu = sqrt(a%re*a%re+a%im*a%im)
end function modu
program main
! 测试函数, 计算两个复数的和, 单个复数的模
use module_test 
implicit none
real, external :: modu
type(complex_os) :: z1, z2 
real:: m1,m2
z1%re = 1.0
z1%im =1.0
z2%re = 4.0
z2%im = 3.0
m1 = modu(z1)
m2 = modu(z2)

write(*,*) 'z1=',z1,'z2=', z2
write(*,*) 'z1+z2=', sum_c(z1,z2)
write(*,*) '|z1|=',m1,'|z2|=', m2
end program main
# 为测试程序写的makefile
# 		---- Numanal 2015/08/20

CC = gfortran
CFLAGS = -g -Wall 

OBJS = module_test.o modu.o main.o
EXECNAME = main

$(EXECNAME) : $(OBJS)
	$(CC) -o $(EXECNAME) $(OBJS) 

module_test.o : module_test.f90
	$(CC) $(CFLAGS)-c module_test.f90
modu.o : modu.f90
	$(CC) $(CFLAGS) -I. -c modu.f90
main.o : main.f90
	$(CC) $(CFLAGS) -I. -c main.f90

.PHONY : clean exe
exe :
	./$(EXECNAME)

clean : 
	rm -f $(OBJS);
	rm -f $(EXECNAME) 
	rm -f *.mod
	rm -f *~

在看过源文件之后, 这里有几点说明是必要的:① 有人可能要问了, 为什么不把函数modu放在模块module_test中, 而偏偏放在一个单独的文件中, 这不是更麻烦了? 原因是, 这样做完全是为了理解编译过程的需要. 函数作为一个独立的编译单元, 如果想在其它单元中使用, 就必须先声明后使用, 测试函数里的声明语句real, external :: modu就是这个目的, 否则就出现编译错误, 这样是完全类似于C语言的;
② 模块里的函数sum_c在测试函数main中没有类似声明modu的语句声明, 因为测试函数通过use语句关联模块module_test中的所有类型定义, 数据及函数, 就好像告诉了测试函数main: 我在这呢, 你可以放心的使用. 其实, use关联为测试函数创建了模块中所有函数的显示接口, 可以直接调用而不会发生错误.

3. 编译过程

命令gfortran module_test.f90 modu.f90 main.f90同时编译文件而不出现任何问题, 生成可执行文件(exe):a.out, 为了更好的理解编译过程中文件的信赖关系及作用, 我们把过程分拆开来:

$ gfortran -Wall -g -c module_test.f90
# 首先编译模块, 因为模块里有其它程序单元所需要的类型定义及函数;
# 生成两个文件: module_test.o 用于链接;
# module_test.mod 作用类似于C中的头文件, 用来告知其它单元模块中内容, 但不参与链接;

$ gfortran -Wall -g -c modu.f90
# 生成用于链接的文件modu.o;
# 因为modu.f90需要模块module_test中的内容, 如果没有先编译该模块产生模块的说明文件module_test.mod, 这一步会出错. 比如可以先把.mod文件剪切到其它文件夹下, 再执行此步骤看一下是否出错;

$ gfortran -Wall -g -c main.f90
# 生成用于链接的文件main.o;
# 同样的这一步需要文件.mod;

$ gfortran -o main module_test.o modu.o main.o
# 链接.o文件, 生成可执行文件main;

$ ./main
# 执行.

说明: 以上就是这个小工程的简单编译过程, 读者可以自己剪切或删除某些文件, 测试这些文件的信赖关系, 想必会有更深的体会. 关于造成库文件.a的内容可以网上查找, 初学的时候基本不会使用库文件, 但库文件对代码的可重用性非常重要.

4. 相关内容

[1] 百度知道上一个关于.mod文件的问题http://zhidao.baidu.com/link?url=PIHXgDAvG_doOA27eJqvZBxcuZwGUPIi4G18KLv_Fb9G75Pncks8bOjzQaXVQmH4pv4146jBQSowXeJijoNY3K;
[2] gfortran编译与程序执行http://forum.ubuntu.org.cn/viewtopic.php?p=887205
[3] Fortran 95/2003 for Scientists and Engineers, 一本Fortran教材, 很不错. 它的中译本叫做:fortran 95/2003程序设计, 书里有一些介绍函数接口的内容;
[4] http://fcode.cn/ 一个很不错的fortran论坛网站.
[5] Code::blocks + GFortran开发环境设置方法http://fcode.cn/guide-39-1.html
[6] 因为gfortran与gcc编译过程非常想像, 所以如果想看一些比较专业的术语可以参考这篇文章:GCC编译过程分解http://blog.chinaunix.net/uid-20196318-id-28797.html