Skip to content

静态库 (Static Libraries)


静态库是编译后的代码,可以在构建可执行文件时使用。

当编译器生成可执行文件时,基本源文件首先转换为目标文件,然后将目标文件链接在一起生成可执行文件。编译源代码时,不一定非要生成可执行文件。可以将所有目标文件(由源文件生成)组合到一个称为静态库的单一文件中。

该库之所以称为静态库,是因为当它所包含的目标文件随后被链接到可执行文件中时,库中所有需要的代码副本都会被添加到可执行文件中。

一旦创建了静态库,我们就可以像直接将源代码与程序一起编译一样使用其中的代码。

与静态库交换/共享变量

静态库允许通过在库代码和模块代码中同时使用 CommonExtern 关键字来直接共享变量。

否则,将参数(按值或按引用)传递给库过程,或从库函数返回变量(按值或按引用),可以间接地与共享库交换数据(按值)或共享数据(按引用)。

静态库简单示例

以下是使用三个文件创建静态库的简单示例:

  • mylib.bas - 库的源代码
  • mylib.bi - 库的头文件
  • mytest.bas - 测试程序

我们的库将是一个提供单一函数的模块:

start GeSHi

vb
'' mylib.bas
'' 编译命令:fbc -lib mylib.bas

'' 将两个数字相加并返回结果
Public Function Add2( ByVal x As Integer, ByVal y As Integer ) As Integer
  Return( x + y )
End Function

end GeSHi

使用以下命令编译库:

fbc -lib mylib.bas

-lib 选项告诉编译器获取源代码 mylib.bas 并将其转换为目标文件 mylib.o,然后将目标文件存储到库文件(也称为存档文件)libmylib.a 中。一个库可能包含许多模块(源文件),每个模块有许多函数,但在这个简单示例中各只有一个。

要在其他源代码中使用库,我们需要某种方式告诉编译器库中确切包含什么内容。一个好方法是将库的声明(也称为接口或 API)放入头文件:

start GeSHi

vb
'' mylib.bi
#inclib "mylib"
Declare Function Add2( ByVal x As Integer, ByVal y As Integer ) As Integer

end GeSHi

头文件无需编译。我们希望以源代码形式保存它,以便与其他源文件一起包含。#inclib 语句将告诉编译器最终生成可执行文件时需要链接的静态库名称。

有了库(.a 文件)和头文件(.bi 文件),我们可以在测试程序中试用它们:

start GeSHi

vb
'' mytest.bas
'' 编译命令:fbc mytest.bas
#include once "mylib.bi"
Print Add2(1,2)

end GeSHi

#include 语句告诉编译器将 mylib.bi 中的源代码包含进来,就像我们直接将其输入到原始源文件中一样。按照我们编写包含文件的方式,它告诉编译器关于该库所需的一切。

我们使用以下命令编译:

fbc mytest.bas

然后运行 mytest 可执行文件,应该得到以下结果:

 3

OOP 静态库高级示例

以下是使用三个文件创建 OOP 静态库的高级示例:

  • varZstring.bi - 库的头文件
  • varZstring.bas - 库的源代码
  • varZstringTest.bas - 测试程序

编译库的方法与上述相同,然后编译并执行测试程序。

库的头文件:

start GeSHi

vb
'' 头文件:'varZstring.bi'

Type varZstring Extends ZString
    Public:
        Declare Constructor (ByRef z As Const ZString)
        Declare Operator Cast () ByRef As ZString
        Declare Operator Let (ByRef z As Const ZString)
        Declare Property allocated () As Integer
        Declare Destructor ()
    Private:
        Dim As ZString Ptr _p
        Dim As UInteger _allocated
End Type

Declare Operator Len (ByRef v As varZstring) As Integer  '' 用户代码必须声明此项才能调用
                                                         ''    重载的 Len 运算符而非
                                                         ''    内置 Len 运算符

end GeSHi

注意:还要注意声明每个重载过程,因为缺少声明不一定会导致编译错误(如果用户代码中的用法在语法上与预置过程兼容),但结果显然不会如预期。

库的源文件:

start GeSHi

vb
'' 库模块:'varZstring.bas'

#include "varZstring.bi"

Constructor varZstring (ByRef z As Const ZString)
    If This._p <> 0 Then
        Deallocate(This._p)
    End If
    This._allocated = Len(z) + 1
    This._p = CAllocate(This._allocated, SizeOf(ZString))
    *This._p = z
End Constructor

Operator varZstring.Cast () ByRef As ZString
    Return *This._p
End Operator

Operator varZstring.Let (ByRef z As Const ZString)
    If This._allocated < Len(z) + 1 Then
    Deallocate(This._p)
    This._allocated = Len(z) + 1
    This._p = CAllocate(This._allocated, SizeOf(ZString))
  End If
  *This._p = z
End Operator

Property varZstring.allocated () As Integer
    Return This._allocated
End Property

Destructor varZstring ()
    Deallocate(This._p)
    This._p = 0
End Destructor

Operator Len (ByRef v As varZstring) As Integer
    Return Len(Type<String>(v))  '' found nothing better than this
End Operator                     ''     (or: 'Return Len(Str(v))')

end GeSHi

测试程序文件:

start GeSHi

vb
'' 测试程序:'varZstringTest.bas'

#include "varZstring.bi"  '' 必须包含重载 Len 运算符的声明,
                          ''    否则将调用内置 Len 运算符并应用于
                          ''    对象变量(返回其成员数据的长度)
                       
#inclib "varZstring"      '' 也可以将此行放入 'varZstring.bi' 文件中
                          ''   (而不是放在此文件中),但这样做有些别扭

Print "VARIABLE",,, "|     LEN|  SIZEOF|"
Print "------------------------------------------|--------|--------|"

Dim As varZstring v = "FreeBASIC"  '' adjusts memory allocation to minimum
Print "varZstring v:", "'" & v & "'",, "|"; Using "########|"; Len(v); v.allocated

Dim As ZString * 256 z
z = v
Print "Zstring    z:", "'" & z & "'",, "|"; Using "########|"; Len(z); SizeOf(z)

v = z & " & SourceForge"  '' only increases memory allocation if necessazy
Print "varZstring v:", "'" & v & "'", "|"; Using "########|"; Len(v); v.allocated

v = z & " & Wiki" '' only increases memory allocation if necessazy
Print "varZstring v:", "'" & v & "'", "|"; Using "########|"; Len(v); v.allocated

v.Constructor(v)  '' readjusts memory allocation to minimum
Print "varZstring v:", "'" & v & "'", "|"; Using "########|"; Len(v); v.allocated

Sleep

end GeSHi

输出:

VARIABLE                                  |     LEN|  SIZEOF|
------------------------------------------|--------|--------|
varZstring v: 'FreeBASIC'                 |       9|      10|
Zstring    z: 'FreeBASIC'                 |       9|     256|
varZstring v: 'FreeBASIC & SourceForge'   |      23|      24|
varZstring v: 'FreeBASIC & Wiki'          |      16|      24|
varZstring v: 'FreeBASIC & Wiki'          |      16|      17|

创建库时可以使用多个源模块。基本程序可以通过包含每个所需的头文件来使用多个库。有些库非常大,可能使用多个头文件。在大型项目中,将很少更改的某些代码模块制作为库可以显著缩短编译时间。

库可以选择性地包含用 -g 命令行选项指定的调试信息。

库可以选择性地包含模块构造函数、主代码和模块析构函数。模块构造函数和主代码在库加载时执行,模块析构函数在库卸载时执行。

目标文件(因此也包括库)是特定于平台的,在某些情况下还特定于编译器和 FreeBASIC 运行时库的特定版本。

参见

返回 目录

基于 FreeBASIC 官方文档翻译 如有侵权请联系我们删除
FreeBASIC 是开源项目,与微软公司无隶属关系