文章目录
温馨提示
如果只是想尽快、立刻运行书上的例子,那么直接用方法一或者方法四就好了。如果要“讲究”一点,推荐方法三。
准备: 下载相关文件
本书所有源代码
其中有两个文件是必须下载的:
1. 方法一:直接在shell命令中指明csapp.h 和 csapp.c文件(成功)
这种方法简单明了,我们就把这两个文件当成平常自己写的就可以了。
例如:下图中,红框中的是我跟着书上敲的代码, 另外两个就是刚刚下载的。
然后就可以像普通文件一样编译、连接了(注意要加-lpthread):
prog就是我们的可执行文件了。
优点
简单明了。
缺点
- 每次都要复制那两个文件,麻烦。
- 每次都会重新编译csapp.c文件, 费时。
2. 方法二:将csapp.c文件制作成静态库(失败)
这种方法应该是不可行的,可能是因为.c中有线程相关代码。
静态库就是一组可重定位文件(.o文件)的集合,通常是将多个源文件编译打包成一个可重定位文件。但是我们这里将一个.c文件打包也是可行的。
-
首先我的目录结构是这样的。
-
编译csapp.c 文件。
-
利用ar工具生成静态库.a文件。
-
将静态库放入 /usr/local/lib 目录下。
-
为了效果,我们把csapp.o 和 csapp.c 都删掉
-
编译连接,生成可执行文件。
失败了。。 应该是线程的库里面有线程的原因,似乎必须使用动态库。
3. 方法三: 将csapp.c 编译成动态库 (成功)
- 首先,我的目录如下:
- 生成动态库
- 将动态库.so文件移入 /usr/local/lib目录下
如果不行的话,就移到 /lib 目录下试试。
- 编译、动态链接。
其中 -lcsapp 选项告诉gcc的链接器:去默认寻找目录下,寻找名为 libcsapp.so 的动态库进行连接。由于第3步中我们已经将该动态库放入了/usr/local/lib中,而该目录是链接器的默认目录之一,所以链接器能够找到。
优点
具备动态库的所有优点: 不需要重新编译.c的代码,不需要发生额外的拷贝,在整个系统中所有使用该库的进程中,都共用一份物理映像。节约了磁盘和内存空间。
注意(必看)
在以上的方法中,我们都是将csapp.h放在当前目录下的,目的是为了看得更清楚,如果要将其放在预处理器默认寻找路径下,当然也是可以的,实际应用中也的确是这么做的。方法如下:
以后,在任何地方,我们都可以直接include “csapp.h”, 预处理器会去系统默认目录下寻找该头文件,而我们已经将头文件放了进去。
4. 方法四: 直接将csapp.c 包含进csapp.h中(成功,但不推荐)
这种方法是目前网上已有的方法,基本一搜全是这个方法。
他的原理就跟前面 [注意] 一节中提到的一样,不过他改动了csapp.h文件: 把csapp.c文件也包含了进去。
步骤
- 将csapp.c文件和 csapp.h文件 都复制到一个默认目录下,例如 /usr/local/include
- 改动csapp.h, 在中间添加一行 #include “csapp.c” 即可。
原理
以后每次预处理器在看到csapp.h文件的时候,就把csapp.c文件的内容替换到.h中的相应位置,然后整个.h文件再替换到我们main程序中的相应位置。
分析
这种方法虽然简单,但是每次都会编译csapp.c里面的代码,本质上和方法一没什么区别。但我个人觉得这个属于奇淫巧技,在一些特殊场合会用到,不太推荐,尤其是对于初学者来说。
我个人认为.h文件和.c文件应该有比较明确的分工: .h 负责符号声明, .c 负责符号定义。如果需要在.h文件中定义的话,那么就直接将定义代码写在.h中。例如,在本方法中,我们可以直接将csapp.c的内容复制到csapp.h文件中,这种风格我想会更加清晰明了。(虽然效果上没什么差别)。
总结
通过以上探索过程,复习了预处理、编译、链接(静态、动态)的概念,并且实战了静态库和动态库的制作、配置和使用。如有理解错误,欢迎探讨~