C语言(Head First C)-9_1:静态库与动态库:静态库
来源:互联网 发布:dating付费软件靠谱吗 编辑:程序博客网 时间:2024/04/25 16:22
该系列文章系个人读书笔记及总结性内容,任何组织和个人不得转载进行商业活动!
9_1:静态库与动态库:静态库
我们已经见识过标准库的威力了,现在是时候在代码中发挥这种威力了;
本章内容:
创建自己的库,并在多个程序中复用相同代码;
通过动态库在运行时共享代码;
基于此,我们可以写出易于扩展并可以有效管理的代码;
场景:
还记得之前我们使用过一个加密的函数encrypt();并把它放到了单独的源文件中(头文件声明),这样可以做到在多个程序中使用它;
现在又写了一个checksum()的函数,可以用于校验字符串是否被篡改;
加密和防篡改是安全领域很重要的问题;
(Code9_1)
9_1-checksum.h
int checksum(char * message);9_1-checksum.c
/* * 校验字符串是否被串改 */#include "9_1-checksum.h"int checksum(char * message){ int c = 0; while (*message) { c += c ^ (int)(*message); message++; } return c;}
9_1-encrypt.h
void encrypt(char * message);9_1-encrypt.c
/* * 异或加密 */#include <stdio.h>#include "9_1-encrypt.h"void encrypt(char * message){ printf("%s",message); char c; while (*message) { *message = *message ^ 31;//可以对char进行运算,因为他是数值类型 message++; }}9_1-demo.c
/* * 加密和防篡改使用 */#include <stdio.h>#include "9_1-checksum.h"#include "9_1-encrypt.h"int main() { char s[] = "Flower Boy!"; printf("摘要值:%i\n",checksum(s)); encrypt(s); encrypt(s); printf("解密后:%s\n",s); printf("摘要值:%i\n",checksum(s)); return 0;}
我们先使用命令生成目标文件:
gcc -c 9_1-checksum.c -o 9_1-checksum.o
gcc -c 9_1-encrypt.c -o 9_1-encrypt.o
之后链接成可执行文件:
gcc 9_1-demo.c *.o -o 9_1
之后执行:
./9_1
log:
摘要值:55081
Flower Boy!Ysphzm?]pf>解密后:Flower Boy!
摘要值:55081
以上算是对之前内容的一个回顾;
尖括号代表标准头文件:
我们注意到上面例子中导入头文件的方式有些许不同;
如果在#include语句中使用<>尖括号,编译器就会在 标准头文件目录 汇总查找头文件,而不是当前目录;
为了使用本地头文件编译程序,需要使用""双引号;
标准头文件目录在哪里:
通常类UNIX操作系统(如Mac或Linux),编译器会在以下目录查找头文件:
/usr/local/include //通常用来存第三方库的头文件;
/usr/include //一般用来存操作系统的头文件;(这个路径在我的Mac上就没有)
来看一下我的电脑(Macpro):
bogon:C_Head First huaqiang$ cd /
bogon:/ huaqiang$ ls
Applications etc
Library home
Network installer.failurerequests
System net
User Information private
Users sbin
Volumes tmp
bin usr
cores var
dev
bogon:/ huaqiang$ cd usr/
bogon:usr huaqiang$ ls
bin libexec sbin standalone
lib local share
bogon:usr huaqiang$ cd local
bogon:local huaqiang$ ls
CODEOFCONDUCT.md bin remotedesktop
Cellar etc sbin
LICENSE.txt include share
Library lib var
README.md opt
bogon:local huaqiang$ cd include/
bogon:include huaqiang$ ls
google lzma.h pcre_stringpiece.h
libltdl node pcrecpp.h
ltdl.h pcre.h pcrecpparg.h
lzma pcre_scanner.h pcreposix.h
如何共享代码:
如果需要在多个程序中使用相同代码,但这些程序四散在计算中各个角落,不同的文件夹中,我们之前示例的方案就不是太好了,因为需要复制好多的代码道不同程序的文件夹中;
我们需要共享两类代码:.h头文件和.o目标文件;
来看看方式-先说头文件;
共享.h头文件:
在多个C项目中共享头文件的方法很多;
1)把头文件保存在标准目录中:
把头文件保存到/usr/local/include标准目录中,就可以在源代码中用尖括号包含它们;(因为会搜索这个目录)
出于安全考虑,有的操作系统会禁止往标准目录中写文件;
2)在include语句中使用完整路径名:
如果头文件放在了其他地方,可以把目录名加到include语句中;
如临时建一个test目录,把9_1-checksum.h和9_1-encrypt.h放到里边,在需要导入头文件的地方使用以下的方式:
#include "/Users/huaqiang/HQDSwiftDemo/C_Head_First/test/9_1-checksum.h"
#include "/Users/huaqiang/HQDSwiftDemo/C_Head_First/test/9_1-encrypt.h"
3)你可以告诉编译器去哪里找头文件:
最后一种方式是告诉编译器去哪里找头文件,可以使用gcc -I选项:
在当前目录新建一个9_1-test文件夹,将9_1-checksum.h 9_1-encrypt.h 移到这个目录下,然后使用gcc -I选项进行编译:
gcc -I/Users/huaqiang/HQDSwiftDemo/C_Head_First/0907-1/9_1-test 9_1-demo.c 9_1-checksum.c 9_1-encrypt.c -o 9_1 && ./9_1
我们的代码仍然是可以正常运行的;
这种方式是:让编译器同时在/test以及标准目录中进行查找:
-I选项告诉编译器还可以去哪里找头文件;编译器会先检查-I选项中的目录,然后像往常一样查找所有标准目录;
再看一下.o目标文件;
用完整路径名共享.o目标文件:
可以把.o目标文件放到一个类似共享目录的地方;当编译器编译程序时,只要在目标文件前加上完整路径就行了;
(Code9_2)
9_2-checksum.h
int checksum(char * message);
9_2-checksum.c
/* * 校验字符串是否被串改 */#include <9_2-checksum.h>int checksum(char * message){ int c = 0; while (*message) { c += c ^ (int)(*message); message++; } return c;}
9_2-encrypt.h
void encrypt(char * message);9_2-encrypt.c
/* * 异或加密 */#include <stdio.h>#include <9_2-encrypt.h>void encrypt(char * message){ printf("%s",message); char c; while (*message) { *message = *message ^ 31;//可以对char进行运算,因为他是数值类型 message++; }}
9_2-demo.c
/* * 加密和防篡改使用 */#include <stdio.h>#include <9_2-checksum.h>#include <9_2-encrypt.h>int main() { char s[] = "Flower Boy!"; printf("摘要值:%i\n",checksum(s)); encrypt(s); encrypt(s); printf("解密后:%s\n",s); printf("摘要值:%i\n",checksum(s)); return 0;}
首先,使用共享头文件的第三种方法,把9_2-checksum.h 9_2-encrypt.h 移到新建目录9_2-my_header_files下;
然后编译生成目标文件:
gcc -c 9_2-checksum.c
gcc -c 9_2-encrypt.c
在我们的示例中9_2-xxxx.h的两个文件放在了目录9_2-my_header_files下,所以可以这样生成目标文件:
gcc -c 9_2-encrypt.c 9_2-checksum.c -I/Users/huaqiang/HQDSwiftDemo/C_Head_First/0907-1/9_2-my_header_files
然后把9_2-checksum.o 9_2-encrypt.o放到文件夹9_2-my_object_files下;
重新编译链接程序:
gcc -I/Users/huaqiang/HQDSwiftDemo/C_Head_First/0907-1/9_2-my_header_files 9_2-demo.c /Users/huaqiang/HQDSwiftDemo/C_Head_First/0907-1/9_2-my_object_files/9_2-checksum.o /Users/huaqiang/HQDSwiftDemo/C_Head_First/0907-1/9_2-my_object_files/9_2-encrypt.o -o 9_2 && ./9_2
log:
摘要值:55081
Flower Boy!Ysphzm?]pf>解密后:Flower Boy!
摘要值:55081
我们看到程序正常运行了;
我们重新看一下这条命令:
gcc -I/.../9_2-my_header_files 9_2-demo.c //-I选项指定头文件目录
/.../9_2-my_object_files/9_2-checksum.o //目标文件前加上路径
/.../9_2-my_object_files/9_2-encrypt.o
-o 9_2 && ./9_2
小结:
使用目标文件的完整路径名,就能在多个C项目中共享它们;
/.../9_2-my_object_files就好比一个中央仓库,专门用来保存目标文件;
不足之处:
我们看到上边命令长的可以,如果要共享的目标文件很多,这简直就是灾难;
有没有什么方法可以告诉编译器我想共享一大堆目标文件呢;
创建目标文件存档:
通过创建目标文件存档,就可以一次告诉编译器一批目标文件;
把一批目标文件打包在一起就成了存档文件;创建安全代码的存档文件,就可以很方便的在多个项目之间共享代码;
存档中包含多个.o文件:
和我们平时用的压缩文件类似,其实就是这么简单;
打开终端,进入库目录,比如我的Mac就是/usr/lib、/usr/local/lib,库代码就存放在这个目录下;(当然,如果有需要,我们可以创建一个自己的库目录,就叫my_lib如何)
可以看到其中有很多.a存档;我们可以用nm命令查看存档中的内容:nm xxxx.a;
nm命令列出存档中保存文件的名字(诸多目标文件);
示例:
bogon:lib huaqiang$ nm libprotoc.a
libprotoc.a(code_generator.o):
0000000000000ae0 s GCC_except_table11
0000000000000b20 s GCC_except_table12
0000000000000bb0 s GCC_except_table15
0000000000000a3c s GCC_except_table3
0000000000000a88 s GCC_except_table8
0000000000000ab4 s GCC_except_table9
U __Unwind_Resume
U __ZN6google8protobuf16SplitStringUsingERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEPKcPNS1_6vectorIS7_NS5_IS7_EEEE
0000000000000020 T __ZN6google8protobuf8compiler13CodeGeneratorD0Ev
...
以上只是存档libprotoc.a的一部分:
我们已经能看到他所包含的一个目标文件:code_generator.o;
看这个示例可能不太清楚,稍后我们会生成自己的存档,然后再用nm查看,以便我们理解;
在使用.a文件编译程序之前,我们先看看如何在存档中保存目标文件;
用ar命令创建存档:
存档命令(ar)会在存档文件中保存一批目标文件:我们在9_2-my_object_files目录下将之前生成的两个目标文件进行存档;
ar -rcs lib9_2-hqsecurity.a 9_2-encrypt.o 9_2-checksum.o
分析:
ar:存档;
rcs:r表示如果.a文件存在就更新他;
c表示创建存档时不显示反馈信息;
s告诉ar要在.a文件开头建立索引;
要创建的.a文件名须是libxxx.a的形式;
最后跟着的是保存在存档中的诸多文件;
到目前为止为了目录越来越多,我把当前的目录结构放这下边,以便理解:
(P9_1)
我们使用nm命令查看一下:
nm lib9_2-hqsecurity.a
lib9_2-hqsecurity.a(9_2-encrypt.o):
0000000000000000 T _encrypt
U _printf
lib9_2-hqsecurity.a(9_2-checksum.o):
0000000000000000 T _checksum
可以清晰的看到存档中包含的目标文件,T _encrypt T _checksum表示各自的目标文件中包含encrypt 和 checksum函数,T是文本的意思;
所有.a文件名都是libxxx.a的形式,这是命名存档的标准方式,存档是静态库(static library),所以以lib开头;
注意:
必须把存档名命名为libxxx.a,否则编译器找不到它们;
在库目录下保存.a文件:
可以把存档保存在库目录中,我们已经知道,库目录可以是系统也可以是自定义的,具体由你决定;
1)把.a文件保存在标准目录中,如/usr/local/lib:
可以在代码正确运行之后,将存档安装到标准目录中;我们之前看到的/usr/local/lib的目录,就是专门用来放本地自定义库的;
很多系统,只有管理员才能这样做;
2)把.a文件放到其他目录:
如果还在开发阶段,或者不想在系统目录中安装代码,可以创建自己的库目录,例如/my_lib;
最后编译其他程序:
创建库存档是为了能在其他程序中使用它;
1)如果把程序安装到标准目录,可以用-l开关编译代码:
gcc 9_2-demo.c -l9_2-hqsecurity -o 9_2 && ./9_2
分析:
首先列出必要的源文件;
如果要使用多个存档,可以设置多个-l;
9_2-hqsecurity会叫编译器去找一个叫lib9_2-hqsecurity.a的存档;
现在知道为什么要把存档命名为libxxx.a了吧;-l选项后的名字必须与存档名的一部分匹配;
2)如果存档在其他地方,比如我们建的一个目录:/9_2-my_lib;可以用-L选项告诉编译器去哪个目录查找存档:
bogon:0907-1 huaqiang$ gcc 9_2-demo.c -L/Users/huaqiang/HQDSwiftDemo/C_Head_First/0907-1/9_2-my_lib -l9_2-hqsecurity -I/Users/huaqiang/HQDSwiftDemo/C_Head_First/0907-1/9_2-my_header_files -o 9_2 && ./9_2
摘要值:55081
Flower Boy!Ysphzm?]pf>解密后:Flower Boy!
摘要值:55081
程序编译运行成功了!注意别忘了-I指定头文件的搜索目录;
源文件结构图:
(P9_2)
需要注意的是:
如果存档和源文件都在当前目录下,编译命令应该这样写:
gcc 9_2-demo.c -I . -L . -l9_2-hqsecurity -o 9_2 && ./9_2
对应的目录结构如下:
(P9_3)
这里的-I其实是需要写的,因为导入头文件的方式使用的是尖括号,需要指定头文件目录;
nm命令补充说明:
nm命令会告诉你每个.o目标文件的名字,然后列车=出所有目标文件中的名字,如果某个名字前出现了T,就说明它是目标文件中某个函数的名字;
要点:
-使用尖括号<>,编译器就会从标准目录中读取头文件;
-常见的标准头文件目录有/usr/include、/usr/local/include等;
-一个库存档中有多个目标文件;
-可以使用ar -rcs libarchive.a file0.o file1.o ... 创建存档;
-库存档名应以lib开头,以.a结尾;
-如果想链接一个叫libfred.a的存档,就使用-lfred选项;
-在gcc命令中,-l标志应该在源代码文件后出现;
-大多数Unix操作系统,标准库目录有/usr/lib和/usr/local/lib,具体的可以查看编译器文档;
-ar命令的存档格式在不同系统中有很大不同,并不是说存档格式差异很大,而是目标文件的格式可谓天差地别;
-可以使用ar -t <文件名>列出存档中的目标文件:
bogon:9_2-my_lib huaqiang$ ar -t 9_2libhqsecurity.a
__.SYMDEF SORTED
9_2-checksum.o
9_2-encrypt.o
-存档中目标文件以独立文件的形式保存;ar命令会检查文件类型,并不是任何类型的文件都可以放到存档中;
-可以使用ar -x libxxx.a xxx.o命令把目标文件从存档中提取出来;
ar -x 9_2libhqsecurity.a 9_2-encrypt.o
-之所以生成的存档叫 静态链接,是因为一旦链接以后就不能修改;
- C语言(Head First C)-9_1:静态库与动态库:静态库
- C语言(Head First C)-9_2:静态库与动态库:动态库
- 静态库与动态库--Head First C读书笔记
- c语言:静态库与动态库
- 【C/C++】动态库与静态库
- 【C/C++】动态库与静态库
- [C/C++] 动态库与静态库
- 重温C语言 - 静态库与动态库
- c语言共享代码之静态库与动态库
- C语言之静态库与动态库学习笔记
- C语言如何编译动态库与静态库
- C语言静态库动态库
- C语言--动态库和静态库
- C语言编译动态或者 静态库
- C/C++静态库与动态库
- 【c++】C++静态库与动态库
- C静态库与动态库
- C语言(Head First C)-6_1:结构、联合与位字段:创建自己的结构
- 父子进程之间的关系
- 前端开发细节-2
- react 真机不出现开发者菜单 不能reload
- js数组冒泡排序
- TCP/IP协议 笔试题和面试题
- C语言(Head First C)-9_1:静态库与动态库:静态库
- Nginx安装教程
- cocos2dx 《忍着飞镖射幽灵》小结
- 解决无法看到eth0的简单情况
- 簇索引与非簇索引在查询中的应用与分析
- 颜色估值器
- Spring Security Advanced Usage Sample
- 单纯形法 simplex method 求解线性规划问题的通用方法.单纯形是美国数学家G.B.丹齐克于1947年首先提出来的.它的理论根据是:线性规划问题的可行域是 n维向量空间Rn中的多
- 搭建Git服务器的最简单方式