NDK14_C++基础:gcc/g++/clang编译

NDK开发汇总

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

一 gcc/g++/clang

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++程序

  2. g++会自动链接c++标准库stl,gcc不会

  3. gcc不会定义__cplusplus宏,而g++会

apt install build-essential #安装gcc、g++与make

二 编译器过程

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

1、预处理

​ gcc -E main.c -o main.i

-E的作用是让gcc在预处理结束后停止编译。

​ 预处理阶段主要处理include和define等。它把#include包含进来的.h 文件插入到#include所在的位置,把源程序中使用到的用#define定义的宏用实际的字符串代替

2、编译阶段

​ gcc -S main.i -o main.s

-S的作用是编译后结束,编译生成了汇编文件。

​ 在这个阶段中,gcc首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,gcc把代码翻译成汇编语言。

3、汇编阶段

​ gcc -c main.s -o main.o

​ 汇编阶段把 .s文件翻译成二进制机器指令文件.o,这个阶段接收.c, .i, .s的文件都没有问题。

4、链接阶段

​ gcc -o main main.s

​ 链接阶段,链接的是函数库。在main.c中并没有定义”printf”的函数实现,且在预编译中包含进的”stdio.h”中也只有该函数的声明。系统把这些函数实现都被做到名为libc.so的动态库。

/etc/ld.so.conf
在没有特别指定时,gcc会到系统编译器只会使用/lib和/usr/lib这两个目录下的库文件。如果存在一个so不在这两个目录,在编译时候就会出现找不到的情况。

/etc/ld.so.conf文件中可以指定而外的编译链接库路径。

输入:cat /etc/ld.so.conf

include /etc/ld.so.conf.d/*.conf #引入其他的conf文件
/usr/local/lib	#增加库搜索目录

#编辑完成后 使用 ldconfig 更新

三、静态编译与动态编译

函数库一般分为静态库和动态库两种

  • 静态库 是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。Linux中后缀名为”.a”。
  • 动态库 与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库。Linux中后缀名为”.so”,如前面所述的libc.so就是动态库。gcc在编译时默认使用动态库。

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

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

Java中在不经过封装的情况下只能直接使用动态库。

  • 链接静态库

a.a => test
source.c(test1) => source.a
source.a里面就拥有test+test1
编译时就将所有符号加入到 输出的库

  • 链接动态库

a.so => test
source.c(test1) => source.a
source.a里面就拥有test1
运行时候动态加载 a.so

 生成静态库
 -fPIC 产生与位置无关代码 
#可能会被不同的进程加载到不同的位置上,如果共享对象中的指令使用了绝对地址。
#那么在共享对象被加载时就必须根据相关模块的加载位置对这个地址做调整,也就是修改这些地址,
#让它在对应进程中能正确访问,那么就不能实现多进程共享一份物理内存(无法动态共享)
gcc -fPIC -c  Test.c -o Test.o
ar r libTest.a Test.o 

#生成动态库
gcc -fPIC -shared Test.c -o libTest.so
#或者
gcc -fPIC -c Test.c  #生成.o
gcc -shared Test.o -o libTest.so

#使用库
#默认优先使用动态库
gcc main.c -L. -lTest -o main
#强制使用静态库
#-Wl 表示传递给 ld 链接器的参数
#最后的 -Bdynamic 表示 默认仍然使用动态库 
gcc main.c -L. -Wl,-Bstatic  -lTest -Wl,-Bdynamic -o main
#使用动态库链接的程序,linux运行需要将动态库路径加入/etc/ld.so.conf
#mac(dylib)和windows(dll)可以将动态库和程序(main)放在一起
#mac 中gcc会被默认链接到xcode的llvm,不支持上面的指定链接动态库

#查看可执行文件符号
nm main

#打包 .a 到so
#--whole-archive: 将未使用的静态库符号(函数实现)也链接进动态库 
#--no-whole-archive : 默认,未使用不链接进入动态库
gcc -shared -o libMain.so -Wl,--whole-archive libMain.a -Wl,--no-whole-archive

头文件与库文件指定

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

猜你喜欢

转载自blog.csdn.net/baopengjian/article/details/106589487