FORTRAN静态库动态库的生成

FORTRAN静态库动态库的生成
FORTRAN静态库动态库的生成

FORTRAN静态库、动态库的生成、维护与调用

闫昊明2006-9-10

一、FORTRAN静态库的生成与维护

FORTRAN 静态库是经过编译的代码块,它与主程序相对独立,可以被主程序调用,是FORTRAN工程类型之一. 静态库包含一系列子程序,但不包括主程序. 静态库一般具有LIB扩展名并包含目标代码,且静态库存放在它们特定的目录中. FORTRAN静态库在组织大型程序和在不同程序之间共享子程序等方面具有较大的优点,其重要性不言而喻. 当将静态库与主程序联系起来时,在主程序中调用静态库中的任何子程序将编译到相应的可执行程序. 应用静态库的时候,只有所需要的子程序才在编译过程中插入到可执行文件(.EXE),这意味着这种可执行文件将比包含所有的子程序所生成的可执行文件小. 而且,不必担心哪些子程序是需要的,哪些是不需要的,编译器将替你做出选择. 同时,当更改静态库中的子程序时,相应的应用程序可以不做任何改变,而只需要对其进行重新的编译链接,即可获得新的结果,这无疑也是方便的.

目前,常用的FORTRAN静态库有很多种,WINDOWS操作系统下的Compaq Visual FORTRAN version 6.5(简称CVF65)自带的数学统计库IMSL就是一个非常全面的静态库,可以用来解决线性代数和统计学上的很多经典问题. 此外,在NCAR互联网站有很多有用的FORTRAN子程序(网址:https://www.360docs.net/doc/107465448.html,/softlib/mathlib.html),其中包括地球物理科学问题、离散和快速Fourier变换、可分离的椭圆微分方程、插值、Legendre多项式、普通数学问题、本征值问题求解、线性方程求解、非线性方程求解、常微分方程求解、特殊函数、统计学等常用子程序集等. 这些FORTRAN子程序可以解决很多基础性的问题,因此有很高的利用价值.

在WINDOWS操作系统下,可以用两个命令分别生成静态库. 一个是用‘nmake’命令,它一般用来编译原来应用在UNIX环境下的FORTRAN子程序集,在编译过程中要读取makefile文件中的编译命令,类似于在UNIX下安装软件. 另一个是用‘lib’命令,它可以在WINDOWS环境下编译任何需要集成为静态库的子程序集.

编译静态库在DOS命令行环境下比较方便,以后的命令行都指在此环境下运行. 在编译静态库前,首先要安装CVF65,其次要完成要编译的FORTRAN子程序(*.f90). 对于FORTRAN子程序,最好用FORTRAN90的标准来完成,应该放弃FORTRAN77标准。FORTRAN90是FORTRAN语言从结构化走向面向对象化的重要一步,使FORTRAN语言更加接近C++。在FORTRAN90标准中,对数组的操作既增强了功能又简化了使用,此外自由格式、MODULE、动态数组、指针等的应用大大丰富了FORTRAN语言,使得编程更加轻松。目前,FORTRAN95和FORTRAN2000标准也在应用,它们与FORTRAN90标准比较类似,主要的改进在并行运算方面,因此目前在单机上应用的主要还是FORTRAN90. 在DOS命令行环境下,进入到FORTRAN子程序所在的子目录,然后按下面两个步骤生成FORTRAN静态库.

(1)键入“df *.f90 /c”,回车,可以看到CVF65编译器对所有的FORTRAN子程序(*.f90)进行编译,生成*.obj文件(注意,编译时,/c中的“c”必须小写).

(2)键入“lib *.obj /out:libname.lib”,回车,可以看到链接生成libname.lib静态库.

需要注意的是,每次加入新的子程序或对静态库中的子程序修改以后,都要按上述两个步骤重新进行编译链接. 生成静态库以后,可用“dumpbin /linkermember libname.lib”来查看静态库中可用的子程序名称. 也可执行“lib /list libname.lib”来查看静态库中的*.obj文件.

当然,也可以在CVF65集成环境下,生成静态库. 步骤如下:

(a)进入到CVF65集成环境下,依次打开菜单File | New | FORTRAN Static Library. 为新的静态库命名,如:libname.lib.

(b)依次打开菜单Project | Add to Project | Files,选择要编译的*.f90子程序到当前工作空间.

(c)依次打开菜单Build | Compile和Build | Build进行编译链接,生成libname.lib静态库(在当前目录中的debug子目录下).

当要编译的静态库需要其它静态库支持时,在步骤(b)中将支持库(*.lib)也加入到当前工作空间,即可顺利编译新的静态库.

从上面的介绍可以看出,无论采用哪种方法,其基本步骤是一致的,即首先生成目标文件*.obj,然后再将这些文件链接成一个静态库文件*.lib. 对于简单的静态库可以按第一种方法在DOS环境下生成,对于需要其它静态库支持的子程序集则可以首先加入库的路径,再在编译时链接这些静态库,最后生成静态库文件.

静态库生成以后还要经常进行更新和维护,以便更有效的利用这些资源. 下面给出维护静态库时常用的命令.

(Ⅰ)将一个编译好的obj文件(如:ok.obj)加入到现有静态库(如:libname.lib),命令为“lib ok.obj libname.lib /out:libname.lib”.

(Ⅱ)将两个或多个静态库合并成一个(all.lib),命令为“lib 1.lib 2.lib 3.lib /out:all.lib”,或“lib *.lib /out:all.lib”.

(Ⅲ)列出静态库中的成员(MEMBER),成员对大小写敏感,命令为“lib libname.lib /list:[outputfilename]”,可给出静态库中的成员,即*.obj.

(Ⅳ)从静态库中解出(extract)特定成员,命令为“lib libname.lib /extract:member”.

(Ⅴ)从库中删除成员,命令为“lib libname.lib /remove:member”.

二、FORTRAN静态库的调用

FORTRAN静态库的调用主要有两种方式,第一种方式是在DOS环境下用命令行调用,其基本命令为“df *.f *.lib”,值得注意的是,在此种情况下,要设置合适的搜索路径(修改CVF65目录下的子目录bin中的文件dfvars.bat),也可以直接将自己的静态库拷贝到CVF65默认库目录下(CVF65根目录下的子目录lib中). 第二种方式是在CVF65环境下调用. 在此方式下有两种方法:方法一:首先对主程序进行编译,然后将静态库文件插入到当前工作空间(参考第一节步骤(b)),再进行链接,即可获得可执行文件. 方法二:对主程序编译后,依次打开菜单Project->settings…在对话框中选择Link选项卡,在Object/library models项下加入静态库文件的名字,再进行链接,同样可以获得可执行文件.

上面所述的两种方法都比较麻烦。下面介绍在FORTRAN90语言环境中应用MODULE来解决这一问题。首先建立一个F90的module程序,名字为userlibmod.f90,对所有静态库中的子程序在此module程序中加入接口语句。其基本结构如下(为便于叙述,在每行都加了行号,源程序中应无此行号):

1.MODULE USERLIBMOD

2.!DEC$OBJCOMMENT LIB:'LIBNAME.LIB'

3.INTERFACE SUMAB

4.SUBROUTINE INT_SUMAB(A,B,C)

5.INTEGER A,B,C

6.…

7.END SUBROUTINE INT_SUMAB

8.SUBROUTINE REAL_SUMAB(A,B,C)

9.REAL A,B,C

10.…

11.END SUBROUTINE REAL_SUMAB

12.END INTERFACE SUMAB

13.INTERFACE

14.SUBROUTINE CALENDAR(IY1,IM1,ID1,IY2,IM2,ID2,IUNITS,NDAYS)

15.INTEGER,INTENT(IN):: IY1,IM1,ID1,IY2,IM2,ID2,IUNITS

16.INTEGER,INTENT(OUT):: NDAYS

17.…

18.END SUBROUTINE CALENDAR

19.END INTERFACE

20.END MODULE

从这个module的结构可以看出,第1行要给出module的名字,最好与文件名相同。第2行告诉编译器要链接的静态库的名字(这里虽然看似注释语句,其实不是,此行可以在编译的过程中与编译器进行通讯,不能省略,不然可能造成编译错误)。3到12行是一个有名接口语句,这里边有两个子程序(对整数和实数分别求和)。我们可以用两个子程序名来分别调用对应的两个数求和,也可以用接口名来直接调用这两个子程序,当用接口名来调用时,如果输入的参数是整数,相当于调用int_sumab(a,b,c),如果输入的参数是实数,相当于调用real_sumab(a,b,c)。所以在F90语言中,可以简化调用函数的个数。13到19行定义了另外一个无名接口,接口中也可以加入多个类似14行到18行的子程序或函数,这些子程序或函数必须已经编译到静态库“LIBNAME.LIB”中。20行是module 的结束语句。

将编辑好的module程序存盘,在命令行下执行“df userlibmod.f90 /c”,即可生成MOD文件“userlibmod.mod”。将此MOD文件放入合适的地方,如“d:/myforlib/include/”,再将生成的静态库文件放入相应的路径,如“d:/myforlib/lib/”,则一个自己的静态库就建成了。

为了应用此静态库,首先要对CVF65设置合适的搜索路径。打开CVF65集成环境,依次打开菜单“tools | options | directories”,在show directories for下拉菜单中依次点击“include files”和“library files”,然后再下面的“directories”中在空白处依次将“d:/myforlib/include/”和“d:/myforlib/lib/”加入,点击“ok”按钮,即完成了对搜索路径的设置。现在,就可以方便的调用静态库中的子程序或函数了,只需要在你自己的主程序中第一行加入如下语句“use userlibmod”即可(如果第一行是“program yourprogramname”,则将其加入到此行的下一行)。

三、静态库的其它说明

如果你应用的FORTRAN编译器是INTEL VISUAL FORTRAN 9.0 (IVF)以上的版本,可以按照和上面的过程类似的方法来生成静态库,唯一的区别是编译命令不同,IVF用ifort命令进行编译,而lib命令是一致的;此外,对于IVF路径的设置是ifortvars.bat文件,其它的内容都是类似的。需要注意的是,由于采用了不同的编译器,CVF和IVF编译的静态库一般不能共享,需要重新编译。

作者已经发布了自己编译的FORTRAN静态库软件WHIGG F90 LIB(WFL),可以从https://www.360docs.net/doc/107465448.html,访问。

有关静态库的问题可以发送email:wfllib@https://www.360docs.net/doc/107465448.html,

四、动态库与静态库的联系与区别

上面我们详细介绍了静态库的生成维护与使用,我们在此做一个小的总结。静态库在使用的时候,首先把需要使用的函数编译成机器代码,保存在*.lib文件中,然后在主程序中调用这些静态库中的函数时,编译器会到指定的*.lib文件中找出所需要的函数代码(或称之为机器码),并把这些代码复制一份,从而一起放到可执行文件exe中,这样就可以使exe文件脱离lib文件,在操作系统中单独运行。也就是说,这样编译得到的exe文件具有独立性。当然,这是跟动态库相比而得出的结论。

再来说说动态库的使用,大家经常在计算机系统里面看到dll为后缀的文件,觉得很神奇,它就是系统中必不可少的动态链接库文件。其实,动态库跟静态库类似,也是计算机中使用库函数的另一种技术,它同样是把多个功能不同的函数使用编译器编译成机器码,然后存储在*.dll文件中。与静态库不同的是,如果主程序调用的是动态库函数,那么编译器在编译的时候将不会把动态库中的机器码复制到可执行文件exe中去。编译器只会在exe文件中说明要调用的函数放在哪个动态库中,其位置在那里。当exe文件执行到这些在动态库中的函数的时候,操作系统就会把dll文件中的函数拿出来交给exe文件去执行使用。同windows系统类似,在unix/linux系统中也可以编译动态链接库,只不过他们的文件名是以*.a结尾而已,其编译器也略有不同而已。

DLL文件有三个好处。第一,可以让Fortran语言很好的与其它语言,如c++,VC,VB,Delphi…等进行混合编程,这样可以利用各自的优点,如fortran强于计算,而c++,VB等做界面比较方便,从而给出界面更友好的计算软件,达到人机更好的对话的效果。第二,可以有效的减少exe文件的大小。我们可以把很多程序共同使用的函数放在特定的DLL文件中,让执行文件变小,以减少对硬盘资源的消耗。如果使用静态库,每个exe文件所使用的函数都会复制在对应的exe文件中,执行文件将因此变大,这样的选择并不是最佳的。第三,DLL文件容易更新,因为DLL文件和exe文件是分离的,不在同一个文件中,因此只要更新必要的DLL文件,而不需要重新编译exe可执行文件,这样就可以得到最新版本的链接库。如果使用静态库,则必须重新编译exe可执行文件,才能链接新的更新库。

五.用FORTRAN语言编译动态链接库

使用Fortran语言来编译静态库,用CVF的命令行方式最直观,命令格式为:df *.f90 /dll:dllfilename 。由于是编译动态库,Fortran源程序的内容要添加一部分内容,下面我们给一个具体的例子来说明。

1.Subroutine add(a,b,c)

2.!DEC$ ATTRIBUTES DLLEXPORT :: ADD

3.!DEC$ ATTRIBUTES ALIAS:’_ADD’:: ADD

4.Implicit none

5.real, intent(in) :: a, b

6.real, intent(out) :: c

7.c=a+b

8.contains

9.subroutine prt()

10.print *, a, b, c

11.end subroutine prt

12.end subroutine add

我们首先把上面的代码储存为add.f90文件,在这个文件中,我们定义了一个subroutine名字为add,其目的是把a和b两个参数求和,然后返回c=a+b的值。注意到,这个subroutine与一般的

subroutine的不同点在第2和第3行,其中,第2行是必须的,而第3行可以省略。第2行的作用是定义输出的dll函数名称,这样dll文件就可以告诉操作系统,在此dll里面,哪些函数是可以被调用的。注意,第2行这个命令也是隐含在注释中的,但编译器遇到!DEC$为开始的注释行会知道这是传递给FORTRAN编译器的功能选项,知道这是在dll中输出一个函数名。在dll中,只有这种有明确输出接口的函数才能对外公开使用。没有使用这种接口(或称之为命令,!DEC$, 对于不同的编译器,其dll接口的定义可能是不同的,比如以前也应用过!MS$等,这个需要查找编译器的帮助文件,以便正确的理解何接口才是编译器认识的)的函数只对dll内部函数有效,对外是不可见的。譬如,上例中的9到11行定义了一个内部函数prt,用来打印a,b,c三个值到屏幕上,这个函数对dll以外的函数是不可见的,也不能调用。同样,prt函数也不能用!DEC$ ATTRIBUTES DLLEXPORT :: PRT 来声明,这会导致编译错误。现在在CVF的command命令行方式下,用df add.f90 /dll:TEST.DLL 就可以生成一个TEST.DLL动态链接库。在生成动态链接库的同时,还将生成以下文件TEST.LIB, TEST.EXP,其中TEST.LIB文件是在编译调用TEST.DLL动态链接库的主程序时必须提供的文件,TEST.LIB文件是TEST.DLL的辅助文件,它里面没有函数add的机器码,但是说明了add函数在哪个dll里。TEST.EXP文件是由LIB工具从DEF文件生成的输出文件,其中包含了函数和数据项目的输出信息,LINK工具将使用EXP文件来创建动态链接库。只有在编译DLL时才会生成,记录了DLL文件中的一些信息。TEST.EXP文件对用户来讲,没有更多的实际作用。

六.动态链接库的调用

下面我们编辑一个主程序,用来调用TEST.DLL这个动态链接库。程序名称为test.f90,内容如下:

1.module testdll

2.!DEC$OBJCOMMENT LIB:'TEST.LIB'

3.interface

4.subroutine add(a,b,c)

5.!DEC$ ATTRIBUTES DLLIMPORT :: ADD

6.!DEC$ ATTRIBUTES ALIAS:’_ADD’:: ADD

7.real,intent(in) :: a,b

8.real,intent(out):: c

9.end subroutine add

10.end interface

11.end module testdll

12.program test

https://www.360docs.net/doc/107465448.html,e testdll

14.real a,b,c

15.a=1.0; b=2.5

16.call add(a,b,c)

17.end

在命令行下或在IDE集成环境下对test.f90进行编译,命令行下的命令为df test.f90 。注意,此时TEST.LIB文件要与TEST.F90在同一目录下,或是TEST.LIB文件在lib的搜索路径中。这样就可以生成TEST.EXE可执行文件。执行TEST.EXE可执行文件可能会出现找不到TEST.DLL错误这样一个对话框,这是因为TEST.DLL不在系统的搜索路径或不和TEST.EXE在同一目录,将TEST.DLL 放到TEST.EXE的目录下或放到C:\WINDOWS\SYSTEM32目录下,就可以顺利运行TEST.EXE。注意,1到11行我们定义了一个module,这样使得主程序在调用dll时,有明显的接口,在接口中(3

到10行)我们给出了add函数的接口,其中5和6行是告诉主程序要引入add这一dll中的函数。第2行定义了需要链接的对应于TEST.DLL的静态库文件TEST.LIB,这也是在编译可执行文件时所不能或缺的。

以上,我们比较详细的讲了如何生成动态链接库,如何调用动态链接库。如果是大型的动态链接库,将会设计到非常多的FUNCTION和SUBROUTINE,此外在某一个FUNCTION或SUBROUTINE中还会调用其它的FUNCTION和SUBROUTINE,这时的FUNCTION和SUBROUTINE就要稍微复杂一些。再举个例子,例如SUBROUTINE的内容如下:

1.SUBROUTINE ADD1(A,B,C)

2.!DEC$ ATTRIBUTES DLLEXPORT :: ADD1

3.!DEC$ ATTRIBUTES ALIAS:’_ADD1’:: ADD1

4.!DEC$ ATTRIBUTES DLLIMPORT :: DIV

5.!DEC$ ATTRIBUTES ALIAS:’_DIV’:: DIV

6.REAL,INTENT(IN) :: A,B

7.REAL,INTENT(OUT):: C

8.INTERFACE

9.FUNCTION PLUS(AA,BB)

10.!DEC$ ATTRIBUTES DLLIMPORT :: PLUS

11.!DEC$ ATTRIBUTES ALIAS:’_PLUS’:: PLUS

12.REAL,INTENT(IN) :: AA,BB

13.REAL PLUS

14.END FUNCTION PLUS

15.END INTERFACE

16.REAL AA,BB, D, E

17.AA=1.0; BB=6.0

18.D=PLUS(AA,BB)

19.CALL DIV(D, E)

20.A=E

21.C=A+B

22.END SUBROUTINE ADD1

在这个例子里面,在ADD1这个SUBROUTINE里面调用了一个名为DIV的SUBROUTINE,还调用了一个名为PLUS的函数。注意,2到5行和8到14行的写法。对于SUBROUTINE来讲,一般直接用4到5行这样引用就可以了;对于FUNCTION来讲,最好是写在一个接口里面,如8到14行,当然也可以写成4到5行的形式。如果FUNCTION和SUBROUTINE里面有optional变量的话,则最后用INTERFACE写成一个完整的接口里面,这样就不会出现问题。当然,你也可以将所有的接口都定义在一个module中,此时要用DLLIMPORT属性,然后用use调用也可以。需要注意的是,对于CVF自带的内部函数,不需要加任何东西,直接调用就可以了。对于IMSL中的库函数,同样只要用use numerical_libraries来引用就可以了,也不用加!DEC$ ATTRIBUTES 等这些属性。至于其它语言如何调用FORTRAN编译的动态链接库,请参考相关的文献,在此不再详述。

七.动态链接库的发布

编译好动态链接库以后,就可以把他与可执行文件一起发布了,但很多时候,在自己的计算机上运行的很好的程序到别人的计算机上就运行不了了,出现缺某某dll的错误对话框。这是因为,你所编译的dll是与系统内部的一些dll相关的,要运行你自己的dll,还需要其它的dll来支持,如果没有这些dll支持,就会出现上述错误。解决的办法有一个,把这些关联的dll也一起发给用户。那

么,怎么才能知道还需要那些关联的dll呢。其实CVF提供了一个工具,叫做dependency walker,在开始菜单中的CVF的程序菜单中可以找到,用这个小工具打开你的dll文件,就会看到与之相关联的dll了。当然,并不是所有的dll都需要与可执行文件一起复制发布,譬如说kernel32.dll就是系统自带的,不需要一同复制。你可以在dependency walker中右键需要查看的dll文件,在属性的版本等信息中可以发现此dll文件是否是系统文件,这样就可以很好的区分哪些是必须要复制的文件,哪些是不需要的,当然也可以看到这些dll文件所在的位置。当然这个工具其实也就是C++语言中的depends.exe这个小工具。

八.静态库和动态库的条件编译

上面讲到了分别对静态库和动态库进行编译,但我们面对的事实是,我们只想管理一套源程序,而不是分别为静态库和动态库各自建立一套类似的源程序,只有一些“!DEC$ ATTRIBUTES …”的区别,这样会增加管理的难度和出错的几率。解决这一问题的一个方法就是利用条件编译,这是利用了General Compiler Directives的功能,对源程序进行条件编译。条件编译的源程序可按下面的方法来写:

!DEC$ IF (expr) -or-

!DEC$ IF DEFINED (name)

block

[!DEC$ ELSEIF (expr)

Block] ...

[!DEC$ ELSE

Block]

!DEC$ ENDIF

Expr是一个逻辑变量,具有.TRUE. 或是.FALSE.值;name是定义的符号名称;block是在各种不同条件成立下的执行块体。

这里,我们可以采用事先定义符合名称来实现静态库和动态库的源程序合二为一。举个例子:

1.SUBROUTINE AAA(A1,A2)

2.!DEC$ IF DEFINED (DLLFLAG)

3.!DEC$ ATTRIBUTES DLLEXPORT :: AAA

4.!DEC$ ATTRIBUTES ALIAS:'AM2EAM' :: AAA

5.!DEC$ ELSE

6.!HERE IS THE CODE FOR STATIC LIB

7.!DEC$ ENDIF

8.…

9.END SUBROUTINE AAA

在这个例子中,我们首先定义了一个子程序AAA,然后用条件编译的方式来区分动态库和静态库,如果在编译时定义了符合DLL_FLAG,那么将执行3、4行语句,如果没有定义DLL_FLAG,那么将执行6行的内容;然后,无论是否定义了DLL_FLAG,都将执行8行的内容。如果将上述子程序(名称为AAA.F90)编译成静态库,可以用如下命令:

df aaa.f90 /c

lib aaa.obj /out:aaa.lib

如果要编译成动态库,可以采用如下的命令行命令:

df aaa.f90 /dll:aaa.dll /define:DLL_FLAG

这样我们就解决了同时用一个源程序来编译成不同形式的目标代码,大大的减少了代码的重复性管

理,提高了效率。

九不同语言调用动态库应注意的问题

FORTRAN语言生成的DLL可以被其它语言所调用,但在正确调用之前,首先要保证在不同语言中的参数传递的正确性。这里主要介绍C或C++语言调用FORTRAN的DLL应该注意的问题。

字符变量

在C语言中,一个字符占一个字节,且字符串的最后一个字符是NULL或者是0。Fortran语言中,字符串没有象C语言这样的最后一个NULL或0字符。字符串一般是传址方式,而不是传值方式进行引用的,因此可能引起错误。传址方式意味着计算机把字符串的首位字符的地址传递到内存中,而不是传递整个字符串的值。当Fortran过程运行完成以后,它仅仅把字符的首位地址传递给用C语言写的主程序,这样C语言的主程序只知道字符串的首地址,却不知道哪里是字符串的结束,因为没有NULL或是0字符在这样一个字符串的结尾。

尽管传递字符有许多好处(例如,传递错误信息),避免这个问题的最好办法是不传递字符串参数。其次,可以把字符串的长度作为一个参数来传递,这样也可以避免上面的问题。此外,还可以在FORTRAN的DLL中,固定字符串的长度,使他们同为一个确定的长度,例如255个字符,这样可以方便C语言的调用。

整数和浮点数变量

我们知道,不同的FORTRAN编译器和不同的操作系统中,整数和浮点数的定义是不尽相同的。例如,在windows系统中,一个整数可以定义为1byte(8bits),2byte(16bits),4byte(32bits),8byte (64bits),一个浮点数可以是4byte(32bits)或是8byte(64bits),还可能是16byte(128bits)或是10byte等。为了防治不同语言中整数和浮点数的不同,最好应用4byte的整数和8byte的浮点数。

数组参数

在主程序和DLL之间传递数组,也可能出现问题。数组与字符参数类似,其缺省的传递方式是传址方式而不是传值方式。而且,在很多情况下,最好把数组的维数和大小用整数传递。

调用约定(Calling Conventions)

当生成或是调用DLL的时候,必需正确指出DLL的调用约定。如果不能正确调用,那么,最好的情况是DLL不能正确工作,最糟糕的情况可能引起计算机崩溃。也许有人会问,为什么调用或生成DLL没有一个统一的标准呢?调用约定是用于链接(link)静态目标码(static objects)的,因此不同的编译器会定义不同的调用规则用来区分和调用静态目标码,从而链接成可执行文件。即使不同的编译器具有不同的调用约定,但对其自己来说不存在任何问题,每个编译器都有自己的一套规则,不需要进行统一。但如果生成DLL,然后用不同的编译器进行调用的话,调用约定确实成了一个问题,而且是一个非常关键的问题。在Windows系统,各个不同编译器之间的调用约定只有微小的差别,但即使再小的差别也可能导致不能正确调用DLL,因此必需重视调用约定的统一。

传址还是传值?

不论是传址还是传值,都可以应用在DLL中,但需要注意的是,在生成和调用DLL过程中,使用统一的调用原则,DLL是传址的就传址调用,是传值的就传值调用。传址或是传值可以在ATTRIBUTE 中定义,分别是REFERENCE, 和V ALUE两个关键字。例如:

!DEC$ ATTRIBUTE V ALUE :: A1

!DEC$ ATTRIBUTE REFERENCE :: A2

调用过程名称(Symbolic names)

调用过程名称是用来区分DLL输出表中的过程的唯一标志。一般情况下,调用过程名称与FORTRAN的子程序或函数同名,但有些情况下,在特定的调用约定下,有的调用过程名称前面有一个下划线(_),且调用过程名称的大小写也可能有区别。因此在调用DLL时,必需注意这些问题。当然,我们可以定义A TTRIBUTE中的特定关键字来强制输出调用过程名称,这样可以减少调用过程中的麻烦。在DLL生成后,可以用Dumpbin /exports *.dll 来查看有哪些调用过程名称是可用的。在调用DLL过程中,调用过程名称必需与DLL输出表中的名称一致。在编译FORTRAN的DLL时,也可以使用命令参数来强制调用过程名称,下面是一个例子:

ifort /names:[lowercase,as_is] /iface:cref /dll /assume:underscore /align:sequence

参考文献:

1.Compaq Visual Fortran (V er.6.6C)online help。

2.彭国伦,FORTRAN95程序设计,中国电力出版社,北京,2002。

3.Lemmon D. R. , J. L. Schafer, Developing statistical software in Fortran 95, Springer, 2005.

https://www.360docs.net/doc/107465448.html,/club/post-242763.html

windowXP下IVF、CVF子程序调用格式说明及混合语言编程

最近在编译ScaLapck需要的BLACS时遇到了Fortran语言与C语言之间的相互调用问题,经过几天的研究,终于有点收获了。

一、IVF和CVF中程序调用格式

1、IVF对子程序名和参数的调用规则

Intel Visual Fortran(IVF)中调用子程序时,编译器默认(即使用参数/iface:default)是按参数传址方式(子程序中对参数进行修改,返回到主程序后参数也是被修改了)、程序名大写进行调用的。若参数中包含有字符参数时,是按隐含字符长度的方式进行传递的,即在字符参数后紧接着有一个隐含的整型数用来指用字符长度的。

在程序编译时可以通过/iface参数来改变子程序的调用规则,以下以一个例子来进行说明。

Subroutine MySub1(int1,real1,char1,int2,real2,char2)

Integer*4 int1,int2

Real*8 real1,real2

Character char1*(*)3,char2*(*)

End Subroutine MySub1

A、当默认编译参数或用/iface:default时,调用参数按地址进行传递,在编译时会将上面这个子程序名编译为MYSUB1(大写),参数列表为(int1,real1,char1[,int3],int2,real2,char2[,int4]),若主调用程序也是用Fortran编写的话,参数列表中的方括号内的参数是不需要显式的指定的,因它主程序和子程序都符合IVF默认的调用规则;但若主调用程序为其它语言编写的(比如VB),侧在调用时需要给出参数列表中方括号内的整形数以说明其前面的字符长度。

B、当编译参数用/iface:cref时,调用参数按地址进行传递,编译后的子程序名为mysub1(小写),参数列表同A;

C、当编译参数用/iface:stdref时,调用参数按地址进行传递,编译后的子程序名为mysub1@n(子程序名小写,n为参数列表中所有参数所占字节数),参数列表同A;

D、当编译参数用/iface:C时,调用参数按值进行传递,编译后的子程序名为mysub1(小写),参数列表同A;

E、当编译参数用/iface:STDCALL时,调用参数按值进行传递,编译后的子程序名为mysub1@n(子程序名小写,n 为参数列表中所有参数所占字节数),参数列表同A;

F、当编译参数用/iface:CVF时,调用参数按地址进行传递,编译后的子程序名为MYSUB1@n(子程序名大写,n为参数列表中所有参数所占字节数),参数列表同A,与情况下CVF中默认的子程序调用相同;

另外,在以上几种情况下,若指定了编译参数/iface:nomixed_str_len_arg,则参数列表为(int1,real1,char1,int2,real2,char2[,int3,int4]),即字符长度参数位移参数列表最后,若主调用程序也是用Fortran编写的话,参数列表中的方括号内的参数也不需要显式的指定的;若指定了编译参数/names:uppercase,则编译后的子程序名全为大写,若指定编译参数/names:lowercase,则编译后的子程序名全为小写,若指定编译参数/names:as_is,则编译后的子程序与原程序名一样。

2、CVF对子程序名和参数的调用规则

Compaq Visual Fortran(CVF)中调用子程序时,编译器默认(即使用参数/iface:default)也是按参数传址方式、程序名大写进行调用的。若参数中包含有字符参数时,也是按隐含字符长度的方式进行传递的。

在程序编译时也可以通过/iface参数来改变子程序的调用规则,仍以上面的例子来进行说明。

A、当默认编译参数或用/iface:default时,调用参数按地址进行传递,在编译时会将上面这个子程序名编译为MYSUB1(大写),参数列表为(int1,real1,char1[,int3],int2,real2,char2[,int4]),若主调用程序也是用Fortran编写的话,参数列表中的方括号内的参数是不需要显式的指定的,因它主程序和子程序都符合IVF默认的调用规则;但若主调用程序为其它语言编写的(比如VB),侧在调用时需要给出参数列表中方括号内的整形数以说明其前面的字符长度。

B、当编译参数用/iface:cref时,调用参数按地址进行传递,编译后的子程序名为mysub1(小写),参数列表同A;

C、当编译参数用/iface:stdref时,调用参数按地址进行传递,编译后的子程序名为mysub1@n(子程序名小写,n为参数列表中所有参数所占字节数),参数列表同A;此选项在CVF的集成开发环境下面没法设置,但在帮助文件里有此选项。

在CVF里没有关于按值传递的设置。

二、混合编程时对编译参数的设置

了解了IVF和CVF中对子程序名和参数传递方式的规则后,在进行混合编程时需要对编译参数进行正确的设置才能保证在最终的连接时不会再出现找不到_XXXX接口或者_XXXX@n的错误提示。FORTRAN调用C++子程序

若一子程序名MySub2为C++子程序,主程序为FORTRAN编写,由于C++默认的编译选项里用的是/Gd(MS C++),即对函数名采用小写的命名规则,且函数名后不加@n,此时,C++编译后的子程序名为mysub2。若主程序里的调用语句为CALL MySub2,若用IVF默认选项编译后这个子程序名成为MYSUB2,因此,在连接时会提示找不到接口的提示。若用CVF的默认编译主程序后这个子程序名为MYSUB2@n,,在连接时也会提示找不到接口的提示与C++的程序进行连接都可能提示找不到接口。

解决方法由以下几种:

a、在IVF里用/names:lowercase编译后连接;

b、在ICVF里用/iface:cref编译后连接;

c、在CVF里用/iface:cref编译后连接;

还有一种方法就是在编译C语言的子程序时用编译选项/Gz(即STDCALL方式调用)进行编译,然后在IVF里用/iface:STDCALL或者/iface:stdref进行编译后连接;在CVF里直接用默认的iface选项和/names:lowercase选项进行编译后连接。

以上所说的编译选项都是对程序中所有的子程序进行设置的,若仅对很少几个子程序有特殊要求的话,可能通过FORTRAN的ATTRIBUTES属性进行个别设置或者设置子程序的别名。

本人不怎么懂C语言,只是这几天在WINDOWS下编译BLAC和SCALAPACK库时里面有很多C语言写的接口子程序才去看了一下。不正之处还望大家指正。

静态链接库lib和动态链接库dll区别

1.什么是静态连接库,什么是动态链接库 静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib 中的指令都全部被直接包含在最终生成的EXE 文件中了。但是若使用DLL,该DLL 不必被包含在最终EXE 文件中,EXE 文件执行时可以“动态”地引用和卸载这个与EXE 独立的DLL 文件。静态链接库和动态链接库的另外一个区别在于静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。静态链接库与静态链接库调用规则总体比较如下。 对于静态链接库(比较简单): 首先,静态链接库的使用需要库的开发者提供生成库的.h头文件和.lib文件。 生成库的.h头文件中的声明格式如下: extern "C" 函数返回类型函数名(参数表); 在调用程序的.cpp源代码文件中如下: #include "..\lib.h" #pragma comment(lib,"..\\debug\\libTest.lib") //指定与静态库一起链接 第二,因为静态链接库是将全部指令都包含入调用程序生成的EXE文件中。因此如果用的是静态链接库,那么也就不存在“导出某个函数提供给用户使用”的情况,要想用就得全要!要不就都别要!:) 对于动态链接库: 动态链接库的使用,根据不同的调用方法,需要提供不同的资源: 1. 静态加载------程序静态编译的时候就静态导入dll,这样的话就需要提供给库 使用者(C客户)如下文件:*.lib文件和.dll文件和*.h。其有2个坏处: 1 程序一开始运行就需要载入整个dll,无法载入程序就不能开始运行; 2 由于载入的是整个dll,需要耗费资源较多 其调用方法如下: #include "..\lib.h" #pragma comment(lib,"..\\debug\\libTest.lib") 但是这种方式的话可以调用Class method. 2.动态加载-----那么只需要提供dll文件。 因此调用程序若想调用DLL中的某个函数就要以某种形式或方式指明它到底想调用哪一个函数。但是无法调用Class method了。 如果要调用Dll中的function,需要经历3个步骤: Handle h=LoadLibrary(dllName) --> GetProcAddress(h,functionName) 返回函数指针,通过函指针调用其function-->FreeLibrary(h) 例如:Another.dll有一个int Add(int x,int y)函数。则完整的调用过程如下:

GCC编译动态和静态链接库

我们通常把一些公用函数制作成函数库,供其它程序使用。函数库分为静态库和动态库两种。静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库。动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在。本文主要通过举例来说明在Linux中如何创建静态库和动态库,以及使用它们。 在创建函数库前,我们先来准备举例用的源程序,并将函数库的源程序编译成.o文件。 第1步:编辑得到举例的程序--hello.h、hello.c和main.c; hello.c(见程序2)是函数库的源程序,其中包含公用函数hello,该函数将在屏幕上输出"Hello XXX!"。hello.h(见程序1)为该函数库的头文件。main.c(见程序3)为测试库文件的主程序,在主程序中调用了公用函数hello。 1.#ifndef HELLO_H 2.#define HELLO_H 3. 4.void hello(const char *name); 5. 6.#endif //HELLO_H 复制代码 程序1: hello.h 1.#include 2. 3.void hello(const char *name) 4.{ 5.printf("Hello %s!\n", name); 6.} 复制代码 程序2: hello.c 1.#include "hello.h" 2. 3.int main() 4.{ 5.hello("everyone"); 6.return 0; 7.} 复制代码 程序3: main.c

第2步:将hello.c编译成.o文件; 无论静态库,还是动态库,都是由.o文件创建的。因此,我们必须将源程序hello.c通过g cc先编译成.o文件。 在系统提示符下键入以下命令得到hello.o文件。 # gcc -c hello.c # 我们运行ls命令看看是否生存了hello.o文件。 # ls hello.c hello.h hello.o main.c # 在ls命令结果中,我们看到了hello.o文件,本步操作完成。 下面我们先来看看如何创建静态库,以及使用它。 第3步:由.o文件创建静态库; 静态库文件名的命名规范是以lib为前缀,紧接着跟静态库名,扩展名为.a。例如:我们将创建的静态库名为myhello,则静态库文件名就是libmyhello.a。在创建和使用静态库时,需要注意这点。创建静态库用ar命令。 在系统提示符下键入以下命令将创建静态库文件libmyhello.a。 # ar crv libmyhello.a hello.o # 我们同样运行ls命令查看结果: # ls hello.c hello.h hello.o libmyhello.a main.c # ls命令结果中有libmyhello.a。 第4步:在程序中使用静态库; 静态库制作完了,如何使用它内部的函数呢?只需要在使用到这些公用函数的源程序中包含这些公用函数的原型声明,然后在用gcc命令生成目标文件时指明静态库名,gcc将会从静态库中将公用函数连接到目标文件中。注意,gcc会在静态库名前加上前缀lib,然后追加

lib和dll文件的区别和联系

(1)lib是编译时需要的,dll是运行时需要的。 如果要完成源代码的编译,有lib就够了。 如果也使动态连接的程序运行起来,有dll就够了。 在开发和调试阶段,当然最好都有。 (2)一般的动态库程序有lib文件和dll文件。lib文件是必须在编译期就连接到应用程序中的,而dll文件是运行期才会被调用的。如果有dll文件,那么对应的lib文件一般是一些索引信息,具体的实现在dll文件中。如果只有lib文件,那么这个lib文件是静态编译出来的,索引和实现都在其中。静态编译的lib文件有好处:给用户安装时就不需要再挂动态库了。但也有缺点,就是导致应用程序比较大,而且失去了动态库的灵活性,在版本升级时,同时要发布新的应用程序才行。 (3)在动态库的情况下,有两个文件,一个是引入库(.LIB)文件,一个是DLL文件,引入库文件包含被DLL导出的函数的名称和位置,DLL包含实际的函数和数据,应用程序使用LIB文件链接到所需要使用的DLL文件,库中的函数和数据并不复制到可执行文件中,因此在应用程序的可执行文件中,存放的不是被调用的函数代码,而是DLL中所要调用的函数的内存地址,这样当一个或多个应用程序运行是再把程序代码和被调用的函数代码链接起来,从而节省了内存资源。从上面的说明可以看出,DLL和.LIB文件必须随应用程序一起发行,否则应用程序将会产生错误。 一、开发和使用dll需注意三种文件 1、 dll头文件 它是指dll中说明输出的类或符号原型或数据结构的.h文件。当其它应用程序调用dll时,需要将该文件包含入应用程序的源文件中。 2、 dll的引入库文件 它是dll在编译、链接成功后生成的文件。主要作用是当其它应用程序调用dll时,需要将该文件引入应用程序。否则,dll无法引入。 3、 dll文件(.dll) 它是应用程序调用dll运行时,真正的可执行文件。dll应用在编译、链接成功后,.dll文件即存在。开发成功后的应用程序在发布时,只需要有.exe文件和.dll文件,不必有.lib文件和dll头文件。 动态链接库 (DLL) 是作为共享函数库的可执行文件。动态链接提供了一种方法,使进程可以调用不属于其可执行代码的函数。函数的可执行代码位于一个 DLL 中,该 DLL 包含一个或多个已被编译、链接并与使用它们的进程分开存储的函数。DLL 还有助于共享数据和资源。多个应用程序可同时访问内存中单个 DLL 副本的内容。 动态链接与静态链接的不同之处在于:动态链接允许可执行模块(.dll 文件或 .exe 文件)仅包含在运行时定位 DLL 函数的可执行代码所需的信息。在静态链接中,链接器从静态链接库获取所有被引用的函数,并将库同代码一起放到可执行文件中。 使用动态链接代替静态链接有若干优点。DLL 节省内存,减少交换操作,节省磁盘空间,更易于升级,提供售后支持,提供扩展 MFC 库类的机制,支持多语言程序,并使国际版本的创建轻松完成。 lib与dll文件最大区别在调用方面 dll可以静态陷入 lib与DLL 从这一章起,我讲述的内容将特定于windows平台。其实这篇文章也可看作是我在windows下的开发经验总结,因为以后我决定转unix了。 前面有一章说编译与链接的,说得很简略,其实应该放到这一章一块儿来说的。许多单讲

FORTRAN静态库动态库的生成

FORTRAN静态库、动态库的生成、维护与调用 闫昊明2006-9-10 一、FORTRAN静态库的生成与维护 FORTRAN 静态库是经过编译的代码块,它与主程序相对独立,可以被主程序调用,是FORTRAN工程类型之一. 静态库包含一系列子程序,但不包括主程序. 静态库一般具有LIB扩展名并包含目标代码,且静态库存放在它们特定的目录中. FORTRAN静态库在组织大型程序和在不同程序之间共享子程序等方面具有较大的优点,其重要性不言而喻. 当将静态库与主程序联系起来时,在主程序中调用静态库中的任何子程序将编译到相应的可执行程序. 应用静态库的时候,只有所需要的子程序才在编译过程中插入到可执行文件(.EXE),这意味着这种可执行文件将比包含所有的子程序所生成的可执行文件小. 而且,不必担心哪些子程序是需要的,哪些是不需要的,编译器将替你做出选择. 同时,当更改静态库中的子程序时,相应的应用程序可以不做任何改变,而只需要对其进行重新的编译链接,即可获得新的结果,这无疑也是方便的. 目前,常用的FORTRAN静态库有很多种,WINDOWS操作系统下的Compaq Visual FORTRAN version 6.5(简称CVF65)自带的数学统计库IMSL就是一个非常全面的静态库,可以用来解决线性代数和统计学上的很多经典问题. 此外,在NCAR互联网站有很多有用的FORTRAN子程序(网址:https://www.360docs.net/doc/107465448.html,/softlib/mathlib.html),其中包括地球物理科学问题、离散和快速Fourier变换、可分离的椭圆微分方程、插值、Legendre多项式、普通数学问题、本征值问题求解、线性方程求解、非线性方程求解、常微分方程求解、特殊函数、统计学等常用子程序集等. 这些FORTRAN子程序可以解决很多基础性的问题,因此有很高的利用价值. 在WINDOWS操作系统下,可以用两个命令分别生成静态库. 一个是用‘nmake’命令,它一般用来编译原来应用在UNIX环境下的FORTRAN子程序集,在编译过程中要读取makefile文件中的编译命令,类似于在UNIX下安装软件. 另一个是用‘lib’命令,它可以在WINDOWS环境下编译任何需要集成为静态库的子程序集. 编译静态库在DOS命令行环境下比较方便,以后的命令行都指在此环境下运行. 在编译静态库前,首先要安装CVF65,其次要完成要编译的FORTRAN子程序(*.f90). 对于FORTRAN子程序,最好用FORTRAN90的标准来完成,应该放弃FORTRAN77标准。FORTRAN90是FORTRAN语言从结构化走向面向对象化的重要一步,使FORTRAN语言更加接近C++。在FORTRAN90标准中,对数组的操作既增强了功能又简化了使用,此外自由格式、MODULE、动态数组、指针等的应用大大丰富了FORTRAN语言,使得编程更加轻松。目前,FORTRAN95和FORTRAN2000标准也在应用,它们与FORTRAN90标准比较类似,主要的改进在并行运算方面,因此目前在单机上应用的主要还是FORTRAN90. 在DOS命令行环境下,进入到FORTRAN子程序所在的子目录,然后按下面两个步骤生成FORTRAN静态库. (1)键入“df *.f90 /c”,回车,可以看到CVF65编译器对所有的FORTRAN子程序(*.f90)进行编译,生成*.obj文件(注意,编译时,/c中的“c”必须小写). (2)键入“lib *.obj /out:libname.lib”,回车,可以看到链接生成libname.lib静态库. 需要注意的是,每次加入新的子程序或对静态库中的子程序修改以后,都要按上述两个步骤重新进行编译链接. 生成静态库以后,可用“dumpbin /linkermember libname.lib”来查看静态库中可用的子程序名称. 也可执行“lib /list libname.lib”来查看静态库中的*.obj文件. 当然,也可以在CVF65集成环境下,生成静态库. 步骤如下:

编译生成动态库时,被关联的静态库会被编译到动态库里面

动态库调用静态库. 生成动态库: 需要的目标文件得用-fPIC选项生成. 而静态库所需的目标文件可以不用-fPIC选项. 一个应用程序调用动态库, 而这个动态库其中的函数调用某静态库时,如何生成应用程序呢? 例: /////// static.h void static_print(); ///////static.cpp #include #include "static.h" void static_print() { std::cout<<"This is static_print function"< #include "shared.h" #include "static.h" void shared_print() { std::cout<<"This is shared_print function"; static_print(); } ////////test.cpp #include "share.h" int main() { shared_print(); return 0; } 方法一: 静态库的.o文件也用-fPIC生成. 生成动态库时把静态库加入. 生成应用程序时只加载动态库 g++ -c -fPIC static.cpp // 生成static.o ar -r libstatic.a static.o // 生成静态库libstatic.a g++ -c -fPIC shared.cpp // 生成shared.o g++ -shared shared.o -lstatic -o libshared.so // 生成动态库libshared.so 注: -shared是g++的选项,与shared.o无关. -lstatic选项把libstatic.a的函数加入动态库中. g++ test.cpp -lshared -o test.exe // link libshared.so 到test.exe中. 方法二: 静态库的.o文件不用-fPIC生成. 生成动态库时不加静态库. 生成应用程序时加载动态库和静态库. g++ -c static.cpp // 生成static.o ar -r libstatic.a static.o // 生成静态库libstatic.a g++ -c -fPIC shared.cpp // 生成shared.o g++ -shared shared.o -o libshared.so // 生成动态库libshared.so 注: -shared是g++的选项,与shared.o无关. 这时如果加-lstatic. error:relocation R_X86_64_32 against `a local symbol' can not be used when making a shared object; recompile with -fPIC

C语言程序静态库和动态库的创建及其应用

C语言程序静态库和动态库的创建及其应用 在用c写程序时,很多时候需要存储一些简单的数据,如果为此而用mysql数据库就有些大才小用了,可以把这些数据以结构的形写入文件,然后再需要时读取文件,取出数据。 如下是定义函数的源文件和头文件: 源文件struct.c: #include "struct.h" //第一个参数是要写入的文件名,第二个参数是缓冲区,第三个参数是缓冲区大小,第四个参数是打开文件流的形态,返回TRUE表示写入成功,返回FALSE表示写入失败int writeStruct(const char *fileName,char *buffer,int bufferLen,char *mode){ int ret; FILE *fileID = NULL; fileID = fopen(fileName,mode); if (fileID == NULL){ perror("fopen"); goto writeEnd; } rewind(fileID); ret = fwrite(buffer,bufferLen,1,fileID); if (ret <= 0){ perror("fwrite"); goto writeEnd; } if (fileID != NULL){ fclose(fileID); fileID = NULL; } return TRUE;

writeEnd: if (fileID != NULL){ fclose(fileID); fileID = NULL; } return FALSE; } //第一个参数是要读取的文件名,第二个参数是缓冲区,第三个参数是缓冲区大小,第四个参数是打开文件流的形态,返回TRUE表示读取成功,返回FALSE表示读取失败int readStruct(const char *fileName,char *buffer,int bufferLen,char *mode){ int ret; FILE *fileID = NULL; fileID = fopen(fileName,mode); if (fileID == NULL){ perror("fopen"); goto readEnd; } rewind(fileID); memset(buffer,0,sizeof(buffer)); ret = fread(buffer,bufferLen,1,fileID); if (ret >= 0){ strcat(buffer,"\0"); }else{ perror("fread") ; goto readEnd; } if (fileID != NULL){ fclose(fileID); fileID = NULL; }

使用Automake生成Makefile及动态库和静态库的创建

使用Automake生成Makefile及动态库和静态库的创建使用Automake 创建和使用静态库 1. 目录结构如下: [c-sharp]view plaincopy 1.example 2.|——src 目录(存放源代码文件) 3. |——hello.c 4.|——lib 目录(存放用来生成库的文件) 5. |——test.c 用来生成静态库libhello.a 6.|——include 目录(存放程序中使用的头文件) 7. |——hello.h 2. 编写的各个目录下的源文件 [c-sharp]view plaincopy 1.hello.h 文件 2.extern void print(char *); 3.test.c 文件 4.#include 5.void print(char *msg) 6.{ 7.print(“%s/n”, msg); 8.} 9.hello.c 文件 10.#include “hello.h” 11.int main() 12.{ 13.print(“Hello static library!”);//这里用到的是静态库中的函数 14.return 0; 15.} 3. 编写lib/Makefile.am 文件

[c-sharp]view plaincopy 1.noinst_LIBRARIES=libhello.a 2.libhello_a_SOURCES=test.c 3.AUTOMAKE_OPTIONS=foreign 第一行noinst 表示生成的是静态库,不需要make install ,直接制定它的位置和名字就 可以使用。 第二行表示用来生成静态库的源文件。如果要把静态库生成到其他地方,可以在=后面 加上路径(建议用绝对路径,并将所要用到的静态库生成在同一个文件夹下,如lib)。 第三行AUTOMAKE_OPTIONS 是Automake 的选项。Automake 主要是帮助开发 GNU 软 件的人员来维护软件,所以在执行Automake 时,会检查目录下是否存在标准GNU 软件中 应具备的文件,例如 'NEWS'、'AUTHOR'、 'ChangeLog' 等文件。设置为foreign 时,Automake 会改用一般软件的标准来检查。如果不加这句的话,需要在autoconf之前,先执行touch NEWS README AUTHORS ChangeLog 来生成'NEWS'、'AUTHOR'、 'ChangeLog' 等文件4. 编写src/Makefile.am 文件 [c-sharp]view plaincopy 1.AUTOMAKE_OPTIONS=foreign 2.INCLUDES= -I../include 3.bin_PROGRAMS=hello 4.hello_SOURCES=hello.c 5.hello_LDADD=../lib/libhello.a 第二行指定头文件的位置,-I 是idirafter 的缩写。../include 指定头文件的位置,..是上 一级目录,也就是这里的example 目录。 第三行指定生成可执行文件名hello,在这里可执行文件生成在src 下,建议将可执行文 件生成到一个特定的文件夹下,让它和源代码分开,如/root/test 目录下。写法为: [c-sharp]view plaincopy 1.bin_PROGRAMS=/root/test/hello,后面的第四、五行也相对应地变为: 2._root_test_hello_SOURCES=hello.c 3._root_test_hello_LDADD=../lib/libhello.a

VC++动态链接库创建和调用全过程详解

1.概论 先来阐述一下DLL(Dynamic Linkable Library)的概念,你可以简单的把DLL看成一种仓库,它提供给你一些可以直接拿来用的变量、函数或类。在仓库的发展史上经历了“无库-静态链接库-动态链接库”的时代。 静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib中的指令都被直接包含在最终生成的EXE文件中了。但是若使用DLL,该DLL不必被包含在最终EXE文件中,EXE文件执行时可以“动态”地引用和卸载这个与EXE独立的DLL文件。静态链接库和动态链接库的另外一个区别在于静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。 对动态链接库,我们还需建立如下概念: (1)DLL 的编制与具体的编程语言及编译器无关 只要遵循约定的DLL接口规范和调用方式,用各种语言编写的DLL都可以相互调用。譬如Windows提供的系统DLL(其中包括了Windows的API),在任何开发环境中都能被调用,不在乎其是Visual Basic、Visual C++还是Delphi。 (2)动态链接库随处可见 我们在Windows目录下的system32文件夹中会看到kernel32.dll、user32.dll和gdi32.dll,windows的大多数API都包含在这些DLL中。kernel32.dll中的函数主要处理内存管理和进程调度;user32.dll中的函数主要控制用户界面;gdi32.dll中的函数则负责图形方面的操作。 一般的程序员都用过类似MessageBox的函数,其实它就包含在user32.dll这个动态链接库中。由此可见DLL对我们来说其实并不陌生。 (3)VC动态链接库的分类 Visual C++支持三种DLL,它们分别是Non-MFC DLL(非MFC动态库)、MFC Regular DLL(MFC规则DLL)、MFC Extension DLL(MFC扩展DLL)。 非MFC动态库不采用MFC类库结构,其导出函数为标准的C接口,能被非MFC或MFC编写的应用程序所调用;MFC规则DLL 包含一个继承自CWinApp的类,但其无消息循环;MFC扩展DLL采用MFC的动态链接版本创建,它只能被用MFC类库所编写的应用程序所调用。 由于本文篇幅较长,内容较多,势必需要先对阅读本文的有关事项进行说明,下面以问答形式给出。 问:本文主要讲解什么内容? 答:本文详细介绍了DLL编程的方方面面,努力学完本文应可以对DLL有较全面的掌握,并能编写大多数DLL程序。 问:如何看本文? 答:本文每一个主题的讲解都附带了源代码例程,可以随文下载(每个工程都经WINRAR压缩)。所有这些例程都由笔者编写并在VC++6.0中调试通过。

[Linux]链接,静态库和动态库

[Linux]链接,静态库和动态库 Filename:[Linux]链接,静态库和动态库 Version:V1.0 Date:12/01/2009 Author:S.C.Leon ============================================================== ======= 在Linux中创建静态库和动态库 一、基本概念 1.1什么是库 在windows平台和linux平台下都大量存在着库。 本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。由于windows和linux的平台不同(主要是编译器、汇编器和连接器的不同),因此二者库的二进制是不兼容的。 本文仅限于介绍linux下的库。 1.2库的种类 linux下的库有两种:静态库和共享库(动态库)。 二者的不同点在于代码被载入的时刻不同。 静态库的代码在编译过程中已经被载入可执行程序,因此体积较大。 共享库的代码是在可执行程序运行时才载入内存的,在编译过程中仅简单的引用,因此代码体积较小。 1.3库存在的意义 库是别人写好的现有的,成熟的,可以复用的代码,你可以使用但要记得遵守许可协议。 现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。 共享库的好处是,不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例。

1.4库文件是如何产生的在linux下 静态库的后缀是.a,它的产生分两步 Step 1.由源文件编译生成一堆.o,每个.o里都包含这个编译单元的符号表 Step 2.ar命令将很多.o转换成.a,成文静态库 动态库的后缀是.so,它由gcc加特定参数编译产生。 具体方法参见后文实例。 1.5库文件是如何命名的,有没有什么规范 在linux下,库文件一般放在/usr/lib和/lib下, 静态库的名字一般为libxxxx.a,其中xxxx是该lib的名称 动态库的名字一般为libxxxx.so.major.minor,xxxx是该lib的名称,major是主版本号,minor是副版本号 1.6如何知道一个可执行程序依赖哪些库 ldd命令可以查看一个可执行程序依赖的共享库, 例如# ldd /bin/lnlibc.so.6 => /lib/libc.so.6 (0×40021000)/lib/ld-linux.so.2 => /lib/ld- linux.so.2 (0×40000000) 可以看到ln命令依赖于libc库和ld-linux库 1.7可执行程序在执行的时候如何定位共享库文件 当系统加载可执行代码时候,能够知道其所依赖的库的名字,但是还需要知道绝对路径 此时就需要系统动态载入器(dynamic linker/loader) 对于elf格式的可执行程序,是由ld-linux.so*来完成的,它先后搜索elf文件的DT_RPATH段—环境变量LD_LIBRARY_PATH—/etc/ld.so.cache文件列表— /lib/,/usr/lib目录找到库文件后将其载入内存 如:export LD_LIBRARY_PATH=’pwd’ 将当前文件目录添加为共享目录 1.8在新安装一个库之后如何让系统能够找到他 如果安装在/lib或者/usr/lib下,那么ld默认能够找到,无需其他操作。

linux下静态库与动态库的区别

静态库与动态库 什么是库 库是写好的,现有的,成熟的,可以复用的代码。现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。 本质上来说,库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。库有两种:静态库(.a、.lib)和动态库(.so、.dll)。 所谓静态、动态是指链接。回顾一下,将一个程序编译成可执行程序的步骤: 静态库 之所以称为【静态库】,是因为在链接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中。因此对应的链接方式称为静态链接。 试想一下,静态库与汇编生成的目标文件一起链接为可执行文件,那么静态库必定跟.o文件格式相似。其实一个静态库可以简单看成是一组目标文件(.o/.obj 文件)的集合,即很多目标文件经过压缩打包后形成的一个文件。静态库特点总结如下: ?静态库对函数库的链接是放在编译时期完成的。 ?程序在运行时与函数库再无瓜葛,移植方便。

?浪费空间和资源,因为所有相关的目标文件与牵涉到的函数库被链接合成一个可执行文件。 Linux下创建与使用静态库 Linux静态库命名规则 Linux静态库命名规范,必须是"lib[your_library_name].a":lib为前缀,中间是静 态库名,扩展名为.a。 创建静态库(.a) 通过上面的流程可以知道,Linux创建静态库过程如下: ?首先,将代码文件编译成目标文件.o(StaticMath.o) g++ -c StaticMath.cpp 注意带参数-c,否则直接编译为可执行文件 ?然后,通过ar工具将目标文件打包成.a静态库文件 ar -crv libstaticmath.a StaticMath.o 生成静态库libstaticmath.a 图3.Linux下使用静态库 大一点的项目会编写makefile文件(CMake等等工程管理工具)来生成静态库,输入多个命令太麻烦了。 使用静态库 Linux下使用静态库,只需要在编译的时候,指定静态库的搜索路径(-L选 项)、指定静态库名(不需要lib前缀和.a后缀,-l选项)。 g++ TestStaticLibrary.cpp -L../StaticLibrary -lstaticmath

动态链接库及静态链接库(windows下的.dll.lib和linux下的.so.a)

ln -S libhello.so.1.0 libhello.so.1 ln -S libhello.so.1 libhello.so 使用库 当要使用静态的程序库时,连接器会找出程序所需的函数,然后将它们拷贝到执行文件, 由于这种拷贝是完整的,所以一旦连接成功,静态程序库也就不再需要了。然而,对动态库而言,就不是这样。动态库会在执行程序内留下一个标记,指明当程序执行时,首先必须载入这个库。由于动态库节省空间,linUX下进行连接的缺省操作是首先连接动态库,也就 是说,如果同时存在静态和动态库,不特别指定的话,将与动态库相连接。 现在假设有一个叫hello的程序开发包,它提供一个静态库libhello.a 一个动态库libhello.so,一个头文件hello.h,头文件中提供Sayhello()这个函数 /* hello.h */ void Sayhello(); 另外还有一些说明文档。这一个典型的程序开发包结构 1. 与动态库连接] linux默认的就是与动态库连接,下面这段程序testlib.c使用hello库中的Sayhello()函数 /*testlib.c*/ #i nclude #i nclude int mai n() { Sayhello(); return 0; } 使用如下命令进行编译 $gcc -C testlib.c -o testlib.o 用如下命令连接: $gcc testlib.o -lhello -o testlib 在连接时要注意,假设libhello.o和libhello.a都在缺省的库搜索路径下/usr/lib下,如果在

3.动态库的路径问题 为了让执行程序顺利找到动态库,有三种方法:

Windows静态库和动态库演练

Windows的静态库和动态库: 演练:创建和使用静态库 在本演练中,您将创建一个静态库(LIB),其中包含可供其他应用程序使用的有用例程。使用静态库是重用代码的一种绝佳方式。您不必在自己创建的每个程序中重新实现这些例程,而只需对这些例程编写一次,然后从需要该功能的应用程序引用它们即可。 本演练涵盖以下内容: 创建新的静态库项目 向静态库添加类 创建引用静态库的应用程序 在控制台应用程序中使用静态库的功能 运行应用程序 先决条件 本主题假定您具备C++语言的基础知识。 创建新的静态库项目 从“文件”菜单中,选择“新建”,然后选择“项目…”。 从“项目类型”窗格中,选择“Visual C++”下的“Win32”。 从“模板”窗格中,选择“Win32控制台应用程序”。 为项目选择一个名称,如“MathFuncsLib”,并将其输入“名称”字段。为解决方案选择一个名称,如“StaticLibrary”,并将其输入“解决方案名称”字段。 按“确定”启动Win32应用程序向导。在“Win32应用程序向导”对话框的“概述”页中,按“下一步”。

从“Win32应用程序向导”的“应用程序设置”页中,选择“应用程序类型”下的“静态库”。 从“Win32应用程序向导”的“应用程序设置”页中,取消选择“附加选项”下的“预编译头”。 按“完成”创建项目。 向静态库添加类 若要为新类创建头文件,请从“项目”菜单中选择“添加新项…”。将显示“添加新项”对话框。从“类别”窗格中,选择“Visual C++”下的“代码”。从“模板”窗格中选择“头文件(.h)”。为头文件选择一个名称,如“MathFuncsLib.h”,并按“添加”。将显示一个空白文件。 添加一个名为“MyMathFuncs”的简单类,以执行常见的算术运算,如加、减、乘和除。代码应与以下内容类似: //MathFuncsLib.hnamespace MathFuncs{class MyMathFuncs{public: //Returns a+b static double Add(double a,double b);//Returns a-b static double Subtract(double a,double b);//Returns a*b static double Multiply(double a,double b);//Returns a/b//Throws DivideByZeroException if b is0static double Divide(double a,double b);};} 若要为新类创建源文件,请从“项目”菜单中选择“添加新项…”。将显示“添加新项”对话框。从“类别”窗格中,选择“Visual C++”下的“代码”。从“模板”窗格中,选择“C++文件(.cpp)”。为源文件选择一个名称,如“MathFuncsLib.cpp”,并按“添加”。将显示一个空白文件。 在源文件中实现“MyMathFuncs”的功能。代码应与以下内容类似: //MathFuncsLib.cpp//compile with:/c/EHsc#include"MathFuncsLib.h"#include using namespace std;namespace MathFuncs{double MyMathFuncs::Add(double a,double b){return a+b;}double MyMathFuncs::Subtract(double a,double b){return a-b;}double MyMathFuncs::Multiply(double a,double b){return a*b;}double MyMathFuncs::Divide(double a,double b){if(b==0) {throw new invalid_argument("b cannot be zero!");}return a /b;}}

MinGW_Eclipse开发静态库和动态库

MinGW_EclipseCDT开发C++动态库、静态库本文主要介绍C++使用MinGW进行跨平台开发时如何创建与使用静态库、动态库。 文章分为以下几个部分: 第一部分介绍了Linux和MinGW使用Gcc编译器创建和使用静态库与动态库的基本方法。 第二部分介绍了MinGW与MSVC库间的转换及其调用。

第一部分GCC系列编译器下的静态库与动态库 一什么是库 所谓库就是已经写好的,成熟的,可以复用的代码—这些代码往往不开源,但在实践中非常有用。现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,C语言中有stdio和stdlib等;C++中有STL和Boost都是程序员不可缺少的库。 本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。 库有两种:静态库(文件后缀分别为.a、.lib)和动态库(文件后缀分别为.so、.dll)。 所谓静态、动态是指链接。回顾一下,将一个程序编译成可执行程序的步骤: 图:编译过程 二静态库 之所以称之为静态库,因为编译器在链接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中。因此对应的链接方式称为静态链接。 试想一下,静态库与汇编生成的目标文件一起链接为可执行文件,那么静态库必定跟.o文件格式相似。其实一个静态库可以简单看成是一组目标文件(.o/.obj文件)的集合,即很多目标文件经过压缩打包后形成的一个文件。静态库特点总结: ●静态库对函数库的链接是放在编译时期完成的。 ●程序在运行时与函数库再无瓜葛,移植方便。 ●浪费空间和资源,因为所有相关的目标文件与牵涉到的函数库被链接合成一个可执行文件。 编译器在编译时将目标文件压缩到一起,并且对其进行编号和索引,以便于查找和检索。一般创建静态库的步骤如图所示:

linux下静态链接库和动态链接库编译和使用[1]

Linux 下编译链接动静态库 2010年03月29日星期一 17:11 Linux 版本是 Red Hat 9 ,内核版本是 2.4.18 输入 which gcc 查看 gcc 的位置在 /usr/bin/gcc gcc -v 查看 gcc 编译前的配置信息 --prefix=/usr 说明了安装目录 没有 --with-headers 说明默认的 include 就在安装目录下 所以 gcc 默认的 include 目录是 /usr/include ,要包含另一个目录, 可以用 -I dir 选项包含该目录,想要更方便的可以 在 /etc/profile 中添加一个环境变量 C_INCLUDE_PATH C_INCLUDE_PATH="your include path" export C_INCLUDE_PATH gcc 默认的 lib 目录很多,一般是 /lib 和 /usr/lib 可以输入 gcc -print-search-dirs 查看 同样可以在编译时通过 -L dir 来添加,也可以在 /etc/profile 中添加 LD_LIBRARY_PATH="your ldlib path" export LD_LIBRARY_PATH 还有就是可以/etc/ld.so.conf中添加目录,这对于安装别的库很方便 当然修改了库文件后需要运行一下ldconfig 自己制作交叉编译工具太复杂了,直接下一个arm-linux-gcc-3.4.1.tar.bz2 tar jxvf arm-linux-gcc-3.4.1.tar.bz2 -C / 解压缩到根目录下 其实由于压缩包带的目录是 usr/local/arm/3.4.1 所以实际还是在 /usr/local/arm/3.4.1 目录下 在bin中可以看到各个工具 arm-linux-gcc ... 输入 ./arm-linux-gcc -v 可以看到配置信息 有 --with-headers=/usr/local/arm/3.4.1/arm-linux/include 说明了默认的include目录 输入 ./arm-linux-gcc -print-search-dirs 查看搜索的 lib 目录,主要的库文件还是在 /usr/local/arm/3.4.1/arm-linux/lib目录下. arm-linux-gcc 3.4.1 可以用来编译2.6的内核 而编译bootloader还是用原来的2.95.2版的 arm-linux-gcc 程序的预处理、编译、链接都可以由gcc完成,gcc会自动调用cpp来做预处理,ld来进行链接。其中对库的链接是很重要的一部分,有静态库和动态库两种,静态库以 .a 为后缀,ld会把静态库中的代码拷到待链接的程序中,形成完整的可执行的程序。而链接动态库生成可执行程序又分为静态调用和动态调用,静态调用是在程序中包含头文件直接调用库函数,也叫显式调用,程序被加载的同时也加载了库,在加载时完成真正的地址链接。而动态调用则不需要包含头文件,在程序中使用库加载函数dlopen来加载库,使用dlsym来获取所需函数的地址,所以是在需要时才加载动态库,也是隐式调用。这样编译时和库就没有关系,不需要链接了。

gcc 生成动态库和静态库

gcc生成静态库和动态库 我们通常把一些公用函数制作成函数库,供其它程序使用。函数库分为静态库和动态库两种。 静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库。动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在。 在创建函数库前,我们先来准备举例用的源程序,并将函数库的源程序编译成.o文件。 第1步:编辑得到举例的程序--hello.h、hello.c和main.c; hello.c(见程序2)是函数库的源程序,其中包含公用函数hello,该函数将在屏幕上输出" Hello XXX!"。hello.h(见程序1)为该函数库的头文件。main.c(见程序3)为测试库文件的主程序,在主程序中调用了公用函数hello。 程序1: hello.h #ifndef HELLO_H #define HELLO_H void hello(const char *name); #endif //HELLO_H 程序2: hello.c #include void hello(const char *name) { printf("Hello %s!\n", name); }

程序3: main.c #include "hello.h" int main() { hello("everyone"); return 0; } 第2步:将hello.c编译成.o文件; 无论静态库,还是动态库,都是由.o文件创建的。因此,我们必须将源程序hello.c通过gcc先编译成.o文件。 在系统提示符下键入以下命令得到hello.o文件。 # gcc -c hello.c 我们运行ls命令看看是否生存了hello.o文件。 # ls hello.c hello.h hello.o main.c 在ls命令结果中,我们看到了hello.o文件,本步操作完成。 下面我们先来看看如何创建静态库,以及使用它。 第3步:由.o文件创建静态库; 静态库文件名的命名规范是以lib为前缀,紧接着跟静态库名,扩展名为.a。例如:我们将创建的静态库名为myhello,则静态库文件名就是libmyhello.a。在创建和使用静态库时,需要注意这点。创建静态库用ar命令。 在系统提示符下键入以下命令将创建静态库文件libmyhello.a。 # ar crv libmyhello.a hello.o 我们同样运行ls命令查看结果: # ls

相关文档
最新文档