DNK基础之静态库、动态库、编译流程

DNK基础之静态库、动态库、编译流程

2020-06-24 22:24:10发布 浏览数:561
概述:DNK基础之静态库、动态库、编译流程

DNK基础之静态库、动态库、编译流程

区分DNK和JNI的概念

DNK:Android Native Development Kit 一套允许使用原生代码语言C/C++,实现部分应用的工具集。包含Android平台的交叉编译器,包含Android平台可用的静、动态库。 C/C++与Java通过JNI交互。

JNI:是一套本地编程接口。它允许运行在JAVA虚拟机中的JAVA代码和用其他编程语言,诸如C语言、C++、汇编,写的应用和库之间的交互操作。

编译器 gcc/g++/clang

了解c/c++编译器的基本使用,能够在后续移植第三方框架进行交叉编译时,清楚的了解应该传递什么参数。

clang

clang 是一个C、C++、Object-C的轻量级编译器。基于LLVM (LLVM是以C++编写而成的构架编译器的框架系统,可以说是一个用于开发编译器相关的库)

gcc

GNU C编译器。原本只能处理C语言,很快扩展,变得可处理C++。(GNU计划,又称革奴计划。目标是创建一套完全自由的操作系统)

g++

GNU c++编译器
gcc、g++、clang都是编译器。
gcc和g++都能够编译c/c++,但是编译时候行为不同。
clang也是一个编译器,对比gcc,它具有编译速度更快、编译产出更小等优点,但是某些软件在使用clang编译时候因为源码中内容的问题会出现错误。
clang++与clang就相当于gcc与g++。

对于gcc与g++:

  1. 后缀为.c的源文件,gcc把它当作是C程序,而g++当作是C++程序;后缀为.cpp的,两者都会认为是c++程序 g++会自动链接c++标准库stl,gcc不会 gcc不会定义__cplusplus宏,而g++会

动态库、静态库原理,编译流程


库的概念

在 windows平台和 alinux平台下都大量存在着库。 Android中也存在库。库,顾名思义,指的是一个容器文件,里面装的是函数,由于 Windows和 linux的平台不同(主要是编译器、汇编器和连接器的不同),因此二者库的二进制是不兼容的。


库的存在意义

库是别人写好的现有的,成熟的,可以复用的代码。现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。

编译流程

一个C/C++文件要经过预处理(preprocessing)、编译(compilation)、汇编(assembly)、和连接(linking)才能变成可执行文件。

test.c文件内容

#include <stdio. h>
int main( ){
    printf("hello world!\n");
    return 0;
}
12345

1.预处理——生成test.i文件(预处理后的文件)

#-E:让gcc在预处理结束后停止编译
#“test.i”文件为预处理后输出的文件
#-o:指定输出文件
$ gcc -E test.c -o test.i
1234

2.编译——得到test.s文件(编译后生成的汇编代码)

# -S:让gcc在编译结束后停止编译过程
#“test.s”文件为编译后生成的汇编代码
$ gcc -S test.i -o test.s
123

3.汇编——汇编就是把编译阶段生成的“.s”文件转成二进制目标代码,也就是机器代码(01序列)。

也可以直接执行命令: gcc -c test.c 能直接生成test.c同名的test.o文件

#-c:让gcc在汇编结束后停止编译过程
#“test.o” 文件为汇编后生成的机器码目标文件
$ gcc -c test.s -o test.o
123

4.链接——生成text文件(这里它是可执行文件)

也可以直接执行命令: gcc -o test test.c 能直接生成test可执行文件

$ gcc test.o -o test          #-o本质上是一个重命名选项。不使用-o选项时,默认生成的是a.out文件。这里生成的是可执行文件test。
$ ./test                      #执行结果:hello world!
12

静态库

静态库:静态库实际就是一些目标文件(一般以.a结尾)的集合,静态库一般以.a结尾,只用于生成可执行文件阶段。

在链接步骤中,链接器将从库文件取得所需代码,复制到生成的可执行文件中。这种库称为静态库。其特点是可执行文件中包含了库代码的一份完整拷贝,在编译过程中被载入程序中。缺点就是多次使用就会有多份冗余拷贝,并且对程序的更新、部署和发布会带来麻烦,如果静态库有更新,那么所有使用它的程序都需要重新编译、发布。

静态库例如:.a / .lib (可以理解为类似Java中的 .aar)

生成静态库:

#首先生成目标文件tool.o 
$ gcc -c tool.c -o tool.o          
#使用ar命令将目标文件打包成静态库   【rcs含义:r : 更新或增加新文件到静态库中 c: 创建一个库,不管是否存在都创建 s:表示创建文档索引】
$ ar rcs libtool.a tool.o           
 #使用ar t libtest.a查看静态库内容
$ ar t libtool.a             

#-------方式二 交叉编译
#交叉编译静态库,同样原理,利用NDK目录下的ar工具
export AR="/root/ndk/android-ndk-r17c/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-ar"
export CC="/root/ndk/android-ndk-r17c/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-gcc"
export CFLAGS="--sysroot=/root/ndk/android-ndk-r17b/platforms/android-21/arch-arm -isysroot /root/ndk/android-ndk-r17b/sysroot -isystem /root/ndk/android-ndk-r17b/sysroot/usr/include/arm-linux-androideabi -pie"
$ $CC $CFLAGS -fPIC test.c -o test.o
$ $AR rcs libTest.a test.o
1234567891011121314

使用静态库

#main.c链接为执行文件main,
#参数一:-L. 编译程序按照指定的路径去寻找库文件,这里点是代表当前目录下     
#参数二:-ltool , -l 后面带的是指定要链接的库(这里是因为main中引入了 tool.h)
$ gcc -o main main.c -L. ltool        
1234

动态库

动态库:动态库在链接阶段没有被复制到程序中,而是在程序运行时由系统动态加载到内存中供程序调用。系统只需载入一次动态库,不同的程序可以得到内存中相同动态库的副本,因此节省了很多内存。相比于静态库而言内存开销较小。

动态库例如:.so / .dll (可以理解为类似Java中的 .jar)

生成动态库:

#------方式一
#首先生成目标文件test.o
$ gcc -c test.c                  
#使用-fPIC和-shared参数生成动态库
$ gcc -shared -fPIC -o libtool.so tool.o           

#------方式二
#直接将.c文件生成.so
$ gcc -shared -fPIC test.c -o xxx.so

#------方式三:配合NDK进行交叉编译,生成能在AS上Android使用的动态库 xxx.so
#这种方式需先配置到环境变量
export CC="/root/ndk/android-ndk-r17c/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-gcc"
export CFLAGS="--sysroot=/root/ndk/android-ndk-r17b/platforms/android-21/arch-arm -isysroot /root/ndk/android-ndk-r17b/sysroot -isystem /root/ndk/android-ndk-r17b/sysroot/usr/include/arm-linux-androideabi"
$ $CC $CFLAGS -shared -fPIC test.c -o libTest.so

12345678910111213141516

使用动态库

$ gcc -o main main.c -L. ltool            #存在同名的静态库和动态库时候,系统会优先加载动态库
1

LD_LIBRSRY_PATH=. 是配置环境变量,指定查找共享库

动态库与静态库的区别

静态库文件比较大动态库比较小

静态库需要在编译时被链接在目标代码中,动态库在运行时才会被加载到目标代码

静态库类似于 Android中的 Module,一旦打包APK需要重新进行编译

动态库类似于jar包打包是不需要重新进行编译

静态库节省时间:不需要再进行动态链接,需要调用的代码直接就在代码内部

动态库节省空间:如果一个动态库被两个程序调用,那么这个动态库只需要在内存中

编译脚本头文件与库文件指定

--sysroot=XX
    使用xx作为这一次编译的头文件与库文件的查找目录,查找下面的 usr/include usr/lib目录
-isysroot XX
    头文件查找目录,覆盖--sysroot ,查找 XX/usr/include
-isystem XX
    指定头文件查找路径(直接查找根目录)
-IXX
    头文件查找目录
优先级:
    -I -> -isystem -> sysroot
    
-LXX
    指定库文件查找目录
-lxx.so
    指定需要链接的库名
    
#查找 目录1/usr/lib的库文件     目录2 /usr/include 的头文件   还去查找 目录3 下的头文件  还去查找 目录4 下的头文件
gcc --sysroot=目录1 -isysroot 目录2 -isystem 目录3 -I目录4  main.c

gcc -L目录1 -l库名

javac -classpath xxx

例子: 链接ndk的日志库
gcc -LC:\Users\Administrator\AppData\Local\Android\Sdk\ndk-bundle\platforms\android-21\arch-arm\usr\lib
-llog  -lGLESv2

gcc --sysroot=C:\Users\Administrator\AppData\Local\Android\Sdk\ndk-bundle\platforms\android-21\arch-arm
-llog -lGLESv2    

#pie 位置无关的可执行程序
export CC="/root/android-ndk-r17b/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-gcc"
export CFLAGS="--sysroot=/root/android-ndk-r17b/platforms/android-21/arch-arm -isysroot /root/android-ndk-r17b/sysroot -isystem /root/android-ndk-r17b/sysroot/usr/include/arm-linux-androideabi -pie"  
$CC $CFLAGS -o main main.c
12345678910111213141516171819202122232425262728293031323334


请先
登录
后评论
0 条评论
暂时没有评论
最新文章
更多