linux ndk 生成动态库,NDK(三):静态库和动态库

66b52468c121889b900d4956032f1009.png

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的知识点,也可以结合之前的知识点看

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值