8种机械键盘轴体对比
本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选?
计算机的发展,离不开前人的一点点积累,让我们可以直接使用别人的轮子进行快速开发。库存在的意义,就是避免重复造轮子,对于开发好的重复可用的代码,就直接封装为库。
库一般分为两大类,一类是动态库,一类是静态库。
[TOC]
静态库、动态库的介绍以及对比
静态库
静态库,一般命名后缀为.a。
会在编译阶段完全被整合进代码段中,所以生成的可执行性文件也比较大。
优点是:编译后的执行程序不再需要函数库的支持,因为所有要使用的函数己经被编译进去了。
这也是他的缺点:生成可执行性文件体积大;
静态库如果发生了改变那么你的程序必须要重新编译。
动态库
动态库,一般命名后缀为.so。
动态库在编译的时候并没有被编译进目标代码中,你的程序执行到相关函数时才调用该函数库里的相应函数,因此动态函数库所产生的可执行文件比较小。
由于函数库没有被整合进你的程序,而是程序运行时动态的申请并调用,所以程序的运行环境中必须提供相应的库。动态函数库的改变并不影响你的程序,所以动态函数库的升级比较方便。
gcc的编译流程
在介绍怎样编译出静态库和动态库之前,先介绍一下,gcc的编译过程,怎样把一个c、c++文件编译为可执行文件。
编译为可执行的文件,一般需要四个流程:预处理(preprocessing)
编译(compilation)
汇编(assembly)
链接(linking)
gcc编译参数1
2
3
4
5
6
7
8
9
10常用选项
-E:只进行预处理,不编译
-S:只编译,不汇编,生成汇编文件
-c:只编译、汇编,不链接,把汇编文件翻译为二进制机器指令文件
-g:包含调试信息
-I:指定include包含文件的搜索目录
-o:输出成指定文件名
高级选项
-v:详细输出编译过程中所采用的每一个选项
-C:预处理时保留注释信息
注意:-o为输出指定文件名,这个命名不影响文件属性,如下面的预处理阶段,如果改为➜ gcc -E main.c -o main.o,也可能正常输出。但是,文件属性仍然只是进行预处理后的文件,而非可执行文件,不同后缀只是为了辨识不同阶段而已。
预处理(preprocessing)
先创建一个main.c文件1
2
3
4
5#include
int (){
printf("hello worldn");
return 0;
}1➜ gcc -E main.c -o main.i
预处理阶段主要处理include和define等。它把#include包含进来的.h 文件插入到#include所在的位置,把源程序中使用到的用#define定义的宏用实际的字符串代替
下面列出了生成的部分内容,可以看到stdio.h的内容被插入进来1
2
3
4
5
6
7
8
9
10
11
12
13......省略......
extern int __vsprintf_chk (char * restrict, int, size_t,
const char * restrict, va_list);
extern int __vsnprintf_chk (char * restrict, size_t, int, size_t,
const char * restrict, va_list);
# 412 "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk/usr/include/stdio.h" 2 3 4
# 2 "main.c" 2
int (){
printf("hello worldn");
return 0;
}
编译(compilation)1➜ gcc -S main.i -o main.s
在这个阶段中,gcc首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,gcc把代码翻译成汇编语言。
同样列出部分内容,可以看到被转化为汇编语言1
2
3
4
5
6
7_main: ## @main
.cfi_startproc
## BB#0:
pushq%rbp
Lcfi0:
.cfi_def_cfa_offset 16
......省略......
汇编(assembly)1➜ gcc -c main.s -o main.o
汇编阶段把.s汇编语言文件翻译成二进制机器指令文件.o
链接(linking)1➜ gcc main.s -o main
链接阶段,链接的是函数库。
在main.c中并没有定义printf的函数实现,且在预编译中包含进的stdio.h中也只有该函数的声明。系统把这些函数实现都放到名为libc.so的动态库,所以在这个阶段,gcc默认去系统默认路径/usr/local/lib搜索动态库并进行链接,就可以生成可执行文件。1
2
3执行可执行文件,输出结果
➜ ./main
hello world
四个阶段一并执行
上面的为分阶段介绍的过程,一般我们只是执行gcc命令,会包含以上四个阶段1
2
3
4➜ gcc main.c -o main2
➜ ./main2
hello world
可以看到与上面的效果一致
查看引用库
上面链接阶段,说到默认会去搜索本地的动态库,通过otool可以查看引用的库,linux为ldd命令1
2
3
4可以看到引用库id为libSystem.B.dylib,地址为/usr/lib
➜ otool -L ./main
./main:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.50.4)
去到/usr/lib目录下,可以看到libSystem.B.dylib文件,再执行otool可以看到详细引用库id1
2
3
4➜ otool -L ./libSystem.B.dylib
./libSystem.B.dylib:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.0.0)
......省略......
.a .so .o 文件的区别
通过上面的介绍,应该大概知道,.a、.so、.o文件的区别了,如果还不太理解,可以参考浅析Linux中的.a、.so、和.o文件这篇文章的介绍,通过window的文件去类比介绍
怎样编译出静态库和动态库
前面介绍不同后缀文件的区别作为铺垫,接下来,就来到这篇文章的主题
创建静态库1ar cr 生成的静态库名称.a 引用的可执行文件.o [引用的可执行文件.o]
c:创建一个库。不管库是否存在,都将创建。
r:在库中插入模块(替换)
创建动态库1gcc -shared -fPCI 可执行文件.o [可执行文件.o] -o 生成的动态库名称.so
实例介绍
接下来,举例详细介绍怎样创建静态库和动态库,并且对比两者的不同点
创建使用到的程序
hello.h文件1
2
3
4
5
6#ifndef HELLO_H
#define HELLO_H
void hello(const char *name);
#endif
hello.c文件1
2
3
4
5
6#include
void hello(const char *name)
{
printf("Hello %s!n", name);
}
main.c文件1
2
3
4
5
6
7#include "hello.h"
int ()
{
hello("world");
return 0;
}
把hello.c编译成.o文件
无论静态库,还是动态库,都是由.o文件创建的。因此,我们必须将源程序hello.c通过gcc先编译成.o文件1
2
3
4➜ gcc -c hello.c -o hello.o
#查看当前路径下的文件,多了一个hello.o
➜ ls
hello.c hello.h hello.o main.c
创建、使用静态库
创建静态库
通过.o可执行文件,创建静态库libhello.o1
2
3➜ ar cr libhello.a hello.o
➜ ls
hello.c hello.h hello.o libhello.a main.c
使用静态库
使用静态库,编译main.c源文件,生成hello可执行文件1
2
3
4
5
6
7
8
9➜ gcc -o hello main.c -L. -lhello
➜ ls
hello hello.c hello.h libhello.a main.c
➜ ./hello
Hello world!
#查找库文件
-LXX 指定库文件查找路径,同一个路径下,为.
-lXX 指定需要链接的库名,不需要加前缀lib和文件后缀.so
删除libhello.a文件,验证是否hello.c文件中的实现被直接链接到hello中1
2
3
4➜ rm libhello.a
#没有libhello.a,发现还是可以正常输出,印证了上面静态库会在编译阶段完全被整合进代码段中
➜ ./hello
Hello world!
创建、使用动态库
创建动态库1
2
3➜ gcc -shared -fPIC -o libhello.so hello.o
➜ ls
hello.c hello.h hello.o libhello.so main.c
使用动态库1
2
3
4
5➜ gcc -o hello main.c -L. -lhello
➜ ls
hello hello.c hello.h hello.o libhello.so main.c
➜ ./hello
Hello world!
把生成的libhello.so移动到其他路径,再执行1
2
3
4
5
6
7#提示加载不到libhello.so库,找不到hello的实现
#印证了上面,动态库是在程序运行时动态申请并调用其中的内容
➜ ./hello
dyld: Library not loaded: libhello.so
Referenced from: /Users/guidongyuan/code/NDKStudy/./hello
Reason: image not found
[1] 18773 abort ./hello
mac os上,如果有通过-L -l去指定路径,那么会优先在指定的路径下找,如果找不到,会去默认路径/usr/local/lib找,可以尝试把libhello.so文件移动到默认路径中,再次执行1
2
3
4
5
6
7
8
9➜ mv libhello.so /usr/local/lib
#成功输出,可验证,会去默认路径寻找
➜ ./hello
Hello world!
#同样,通过otool -L查看可执行文件引用库的id,可以发现引用了libhello.so
➜ otool -L hello
hello:
libhello.so (compatibility version 0.0.0, current version 0.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.50.4)
参考资料可以参考其gcc编译的属性值
编译过程也可以结合这篇看一下编译的过程,生成不同格式的文件,该文章还介绍了静态库和动态库的区别
写得非常好,一步步介绍生成.o文件,再分别介绍.a和.so文件,并且最后还验证使用两种库的区别
主要介绍so的知识点,也可以结合之前的知识点看