在软件开发过程中,Linux的ar
命令常被用来构建函数库。本教程将引导你了解如何创建、修改和在程序中使用静态库,并附有实际代码示例。
ar
命令历史悠久,自1971年就已经存在。其名称ar
来源于该工具最初的用途——创建归档文件。归档文件可以被视为一个容器,用于存放其他文件,有时是大量文件。文件可以在归档文件中添加、删除或从中提取。如今,人们不再依赖ar
来完成这些功能,这项工作已被tar
等其他实用工具所取代。
然而,ar
命令在某些特定场景下仍然非常有用。它主要用于创建静态库,这些静态库在软件开发中扮演着重要角色。此外,ar
还用于创建软件包文件,例如Debian Linux及其衍生版本(如Ubuntu)中使用的.deb
文件。
本文将详细介绍创建和修改静态库的步骤,并演示如何在程序中使用它。为了更好地说明,我们将创建一个静态库,其主要功能是对文本字符串进行加密和解密。
请注意,这里提供的加密方法仅用于演示目的,请勿将其用于任何有价值的敏感数据。这是一种非常简单的替换密码,它将A变为B,B变为C,以此类推。
cipher_encode()
和 cipher_decode()
函数
我们将所有的操作都放在一个名为library
的目录中,并在其中创建一个名为test
的子目录。
在library
目录下,我们有两个文件。第一个文件名为cipher_encode.c
,其中包含了cipher_encode()
函数:
void cipher_encode(char *text) { for (int i=0; text[i] != 0x0; i++) { text[i]++; } } // end of cipher_encode
第二个文件名为cipher_decode.c
,其中包含了对应的cipher_decode()
函数:
void cipher_decode(char *text) { for (int i=0; text[i] != 0x0; i++) { text[i]--; } } // end of cipher_decode
这些包含程序指令的文件被称为源代码文件。我们将创建一个名为libcipher.a
的库文件,它将包含这两个源代码文件的编译版本。此外,我们还会创建一个名为libcipher.h
的头文件,其中包含了我们新库中两个函数的定义。
有了库文件和头文件,开发者就可以在自己的程序中使用这两个函数。他们无需重新编写这些功能,只需简单地利用我们提供的库即可。
编译 cipher_encode.c
和 cipher_decode.c
文件
要编译源代码文件,我们将使用gcc
,这是标准的GNU编译器。-c
选项(编译但不链接)告诉gcc
编译文件后停止。它会从每个源代码文件生成一个中间文件,称为目标文件。通常,gcc
链接器会获取所有目标文件并将它们链接在一起以生成可执行程序。但我们使用-c
选项跳过了这一步,我们只需要目标文件。
首先,让我们检查一下是否拥有了我们所需要的文件。
ls -l
确认该目录下存在两个源代码文件。接下来,我们将使用gcc
将它们编译成目标文件。
gcc -c cipher_encode.c
gcc -c cipher_decode.c
如果一切顺利,gcc
应该不会输出任何信息。
这将生成两个与源代码文件同名但扩展名为.o
的目标文件。这些就是我们需要添加到库文件中的文件。
ls -l
创建 libcipher.a
库
要创建库文件(本质上是一个归档文件),我们将使用ar
命令。
我们使用-c
选项(创建)来创建库文件,-r
选项(添加或替换)将文件添加到库中,以及-s
选项(索引)来创建库文件的内部索引。
我们将库文件命名为libcipher.a
。 在命令行中指定该名称,以及要添加到库中的目标文件的名称。
ar -crs libcipher.a cipher_encode.o cipher_decode.o
如果列出目录中的文件,我们将会看到现在多了一个libcipher.a
文件。
ls -l
我们可以使用ar
命令的-t
选项(列出)来查看库文件中的模块。
ar -t libcipher.a
创建 libcipher.h
头文件
libcipher.h
文件将被包含在任何使用libcipher.a
库的程序中。libcipher.h
文件必须包含库中函数的定义。
要创建头文件,我们需要使用文本编辑器(例如gedit
)输入函数定义。 将文件命名为“libcipher.h”并将其保存在与libcipher.a
文件相同的目录中。
void cipher_encode(char *text); void cipher_decode(char *text);
使用 libcipher
库
验证新库的可靠方法是编写一个小型测试程序来使用它。 首先,创建一个名为test
的目录。
mkdir test
将库文件和头文件复制到新目录中。
cp libcipher.* ./test
切换到新目录。
cd test
检查这两个文件是否已经复制到这里。
ls -l
我们需要创建一个可以使用该库的小程序,并证明它能正常工作。 在编辑器中输入以下代码,并将内容保存到测试目录中名为“test.c”的文件中。
#include <stdio.h> #include <stdlib.h> #include "libcipher.h" int main(int argc, char *argv[]) { char text[]="How-To Geek loves Linux"; puts(text); cipher_encode(text); puts(text); cipher_decode(text); puts(text); exit (0); } // end of main
程序的逻辑很简单:
- 它包含了
libcipher.h
文件,以便可以访问库中函数的定义。 - 创建了一个名为
text
的字符串,并初始化为“How-To Geek loves Linux”。 - 将该字符串打印到屏幕上。
- 调用
cipher_encode()
函数对字符串进行编码,并将编码后的字符串打印到屏幕上。 - 调用
cipher_decode()
函数对字符串进行解码,并将解码后的字符串打印到屏幕上。
要生成测试程序,我们需要编译test.c
程序并将其链接到库。-o
选项(输出)指示gcc
生成的可执行程序的名称。
gcc test.c libcipher.a -o test
如果gcc
没有输出任何信息,并直接返回到命令提示符,那么一切正常。现在来测试我们的程序,关键时刻到了:
./test
我们可以看到预期输出。 测试程序打印出纯文本,加密后的文本,以及解密后的文本。它正在使用我们新库中的函数。我们的库运行良好!
成功了! 但为什么停在这里呢?
向库中添加另一个模块
让我们向库中添加另一个函数。 我们将添加一个函数,开发者可以使用它来显示他们正在使用的库的版本信息。 我们需要创建新函数、编译它,并将新的目标文件添加到现有的库文件中。
在编辑器中键入以下行。 将编辑器的内容保存到库目录中名为cipher_version.c
的文件中。
#include <stdio.h> void cipher_version(void) { puts("How-To Geek :: VERY INSECURE Cipher Library"); puts("Version 0.0.1 Alphan"); } // end of cipher_version
我们需要将新函数的定义添加到libcipher.h
头文件中。 在该文件的底部添加一行,使其看起来像这样:
void cipher_encode(char *text); void cipher_decode(char *text); void cipher_version(void);
保存修改后的libcipher.h
文件。
我们需要编译cipher_version.c
文件,以便获得cipher_version.o
目标文件。
gcc -c cipher_version.c
这将创建一个cipher_version.o
文件。 我们可以使用以下命令将新目标文件添加到libcipher.a
库中。 -v
选项(详细)使通常保持沉默的ar
命令输出执行的操作信息。
ar -rsv libcipher.a cipher_version.o
新的目标文件被添加到库文件中。ar
打印出确认。“a”表示“添加”。
我们可以使用-t
选项(列出)来查看库文件中有哪些模块。
ar -t libcipher.a
现在我们的库文件中有三个模块了。 让我们使用新功能。
使用 cipher_version()
函数
首先,从测试目录中删除旧的库和头文件,复制新文件,然后切换回测试目录。
删除旧版本的文件。
rm ./test/libcipher.*
将新版本复制到测试目录中。
cp libcipher.* ./test
进入测试目录。
cd test
现在我们可以修改test.c
程序,使其使用新的库函数。
我们需要在调用cipher_version()
函数的test.c
程序中添加一行,我们将其放在第一个puts(text)
之前。
#include <stdio.h> #include <stdlib.h> #include "libcipher.h" int main(int argc, char *argv[]) { char text[]="How-To Geek loves Linux"; // new line added here cipher_version(); puts(text); cipher_encode(text); puts(text); cipher_decode(text); puts(text); exit (0); } // end of main
将此保存为test.c
。现在编译并测试新功能是否可用。
gcc test.c libcipher.a -o test
现在运行新版本的测试程序:
新功能正在正常运行。 我们可以在测试输出的开头看到库的版本信息。
但可能存在问题。
替换库中的模块
这不是库的第一个版本,而是第二个。 我们的版本号不正确。 在第一个版本中没有cipher_version()
函数,所以这个版本应该是“0.0.2”。我们需要用更正后的函数替换库中的cipher_version()
函数。
幸运的是,ar
可以轻松实现这一点。
首先,让我们编辑库目录中的cipher_version.c
文件。 将“Version 0.0.1 Alpha”文本更改为“Version 0.0.2 Alpha”。 修改后应该如下所示:
#include <stdio.h> void cipher_version(void) { puts("How-To Geek :: VERY INSECURE Cipher Library"); puts("Version 0.0.2 Alphan"); } // end of cipher_version
保存此文件。 我们需要再次编译它以创建新的cipher_version.o
目标文件。
gcc -c cipher_version.c
现在,我们将用新编译的版本替换库中现有的cipher_version.o
模块。
我们之前使用-r
选项(添加或替换)来向库中添加新模块。 当我们将其与库中已存在的模块一起使用时,ar
将用新版本替换旧版本。 -s
选项(索引)将更新库索引,-v
选项(详细)将使ar
输出执行的操作信息。
ar -rsv libcipher.a cipher_version.o
这次,ar
报告它已替换了cipher_version.o
模块。“r”表示“替换”。
使用更新后的 cipher_version()
函数
我们应该使用修改后的库并检查它是否有效。
将库文件复制到测试目录。
cp libcipher.* ./test
进入测试目录。
cd ./test
我们需要使用新的库再次编译我们的测试程序。
gcc test.c libcipher.a -o test
现在可以测试我们的程序了。
./test
测试程序的输出符合我们的预期。版本字符串中显示了正确的版本号,并且加密和解密例程正在正常运行。
从库中删除模块
尽管看起来有些可惜,但我们还是从库文件中删除cipher_version.o
文件。
为此,我们将使用-d
选项(删除)。我们还将使用-v
选项(详细),以便ar
输出执行的操作信息。 我们还将包含-s
选项(索引)来更新库文件中的索引。
ar -dsv libcipher.a cipher_version.o
ar
报告它已经删除了该模块。“d”表示“删除”。
如果我们要求ar
列出库文件中的模块,我们将看到又回到了两个模块。
ar -t libcipher.a
如果从库中删除模块,请记住从库的头文件中删除它们的定义。
分享你的代码
库以一种实用且私密的方式使代码可共享。 任何拥有库文件和头文件的人都可以使用您的库,但您的实际源代码仍然是私有的。