函数库、gcc / g++

函数库

  • 在大多数时候,由于源文件太多,编译生成的中间目标文件太多,而在链接时需要明显地指出中间目标文件名,这对于编译很不方便,所以,我们要给中间目标文件打个包,在 Windows 下这种包叫 “库文件”(Library File),也就是 .lib 文件,在 UNIX下,是 Archive File,也就是 .a / .so 文件

动态与静态函数库

静态函数库

  • 扩展名:通常为 libxxx.a
  • 编译操作:这类函数库在编译的时候会直接整合到执行程序当中,所以利用静态函数库编译成的文件会比较大
  • 独立执行的状态: 这类函数库最大的优点,就是编译成功的可可执行文件可以独立执行,而不需要再向外部要求读取函数库的内容
  • 升级难易度: 虽然可执行文件可以独立执行,但因为函数库是直接整合到可执行文件中, 因此若函数库升级时,整个可执行文件必须要重新编译才能将新版的函数库整合到程序当中

动态函数库

  • 扩展名:通常为 libxxx.so
  • 编译操作:动态函数库在编译的时候并没有被整合到可执行文件当中,而是当可执行文件要使用到函数库的机制时, 程序才会通过指针去读取函数库来使用。因此文件会比较小
  • 独立执行的状态: 这类型的函数库所编译出来的程序不能被独立执行, 因为当我们使用到函数库的机制时,程序才会去读取函数库,所以函数库文件“必须要存在” 才行,而且,函数库的“所在目录也不能改变”。因此 动态函数库不能随意移动或删除,会影响很多依赖的程序软件
  • 升级难易度: 虽然这类型的可执行文件无法独立运行,然而由于是具有指向的功能, 所以,当函数库升级后,可执行文件根本不需要进行重新编译的行为,因为可执行文件会直接指向新的函数库文件 (前提是函数库新旧版本的文件名相同)

Linux 中的函数库

  • 目前的 Linux 发行版比较倾向于使用动态函数库,主要是因为函数库的升级比较容易
  • 绝大多数函数库都放置在 /lib64, /lib 目录下。 此外,Linux 系统里面很多的函数库其实内核就提供了,内核的函数库放在 /lib/modules

ldconfig/etc/ld.so.conf

  • 使用 ldconfig/etc/ld.so.conf 可以先将动态函数库加载到缓存中,提高动态函数库的读取性能

  1. /etc/ld.so.conf 里写入 “想要读入缓存的动态函数库所在的目录
  2. ldconfig 这个可执行文件将 /etc/ld.so.conf 的数据读入缓存,同时也将数据记录一份在 /etc/ld.so.cache

[root@study ~]# ldconfig [-f conf] [ -C cache]
[root@study ~]# ldconfig [-p]
选项与参数:
-f conf :那个 conf 指的是某个文件名称,也就是说,使用 conf 作为 libarary 函数库的取得路径,而不以 /etc/ld.so.conf 为默认值
-C cache:那个 cache 指的是某个文件名称,也就是说,使用 cache 作为高速缓存暂存的函数库数据,而不以 /etc/ld.so.cache 为默认值
-p :列出目前有的所有函数库数据内容 (在 /etc/ld.so.cache 内的数据)

  • 范例一:假设我的 Mariadb 数据库函数库在 /usr/lib64/mysql 当中,如何读进 cache ?
# /etc/ld.so.conf 文件中的内容如下:
# include /etc/ld.so.conf.d/*.conf

$ vim /etc/ld.so.conf.d/vbird.conf
/usr/lib64/mysql 		<== 这一行是新增的
$ ldconfig 
$ ldconfig -p
874 libs found in cache `/etc/ld.so.cache'
	libzstd.so.1 (libc6,x86-64) => /lib/x86_64-linux-gnu/libzstd.so.1
	libz.so.1 (libc6,x86-64) => /lib/x86_64-linux-gnu/libz.so.1
	...

程序的动态函数库解析:ldd

  • 利用 ldd 可以判断某个可执行二进制文件含有什么动态函数库
[root@study ~]# ldd [-vdr] [filename]
选项与参数:
-v :列出所有内容信息;
-d :重新将数据有遗失的 link 点秀出来!
-r :将 ELF 有关的错误内容显示出来

$ ldd /usr/bin/passwd
	linux-vdso.so.1 (0x00007ffcbf667000)
	libpam.so.0 => /lib/x86_64-linux-gnu/libpam.so.0 (0x00007feadc316000)	# PAM 模块
	libpam_misc.so.0 => /lib/x86_64-linux-gnu/libpam_misc.so.0 (0x00007feadc311000)
	libaudit.so.1 => /lib/x86_64-linux-gnu/libaudit.so.1 (0x00007feadc2e5000)
	libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007feadc2ba000)	# SELinux 模块
	...
$ ldd -v /lib64/ld-linux-x86-64.so.2 
	statically linked

gcc/g++

gcc/g++安装

sudo apt-get update
sudo apt-get install build-essential gdb	# gdb 为调试器
gcc -v		# 查看 gcc 版本

gcc/g++ 编译流程

  • 预处理 [预处理器 cpp]:展开所有头文件,替换宏,解析条件编译并添加到文件中;生成文件 .i
  • 编译 [编译器 egcs]:将预处理后的文件编译为汇编语言, 生成文件 .s
  • 汇编 [汇编器 as]:将汇编文件编译为二进制目标文件,生成文件 .o
  • 链接 [链接器 ld]:将汇编出来的多个二进制目标文件以及函数库链接在一起, 生成可执行程序

例子

编译多个文件

# 这样写比较简洁,但任一源文件被修改,都需要重新编译所有文件
gcc hello1.c hello2.c -o hello	

# 等价写法,先生成各个源文件的目标文件,最后将它们链接起来
# 这样任一源文件被修改时,就不需要重新编译其他没有修改过的源文件了
gcc -c hello1.c
gcc -c hello2.c
gcc -o hello hello1.o hello2.o

常用参数

-g 产生调试信息

  • 如果想用调试器执行一个可执行文件, 在用 gcc 编译时最好加上 -g 选项用于产生调试信息
  • 加上 -g 选项以后,gcc 在编译时会关闭所有的优化机制,以便程序执行过程中严格按照原来的 C 代码顺序执行

gcc -g helloworld.c

-c 只进行预处理,编译,和汇编

-c
  • 只进行预处理,编译,和汇编,也就是只生成 obj 文件

gcc -c hello.c 		# 生成 hello.o

-S 只进行预处理和编译

-S
  • 只激活预处理编译,就是指把文件编译成为汇编代码

gcc -S hello.c 	# 生成 hello.s 汇编代码

-E 只进行预处理、-C

-E
  • 只进行预处理,不生成文件, 你需要把它重定向到一个输出文件里

gcc -E hello.c > pianoapan.txt 
gcc -E hello.c | more 

-C
  • 在预处理的时候, 不删除注释信息, 一般和 -E 一起使用

-o 指定目标名称

-o
  • 指定目标名称, 默认是 a.out (assembler output)

gcc -o hello hello.c
gcc -o hello.asm -S hello.c

-ansi-fno-asm:激活 ansi c 的专有特性

  • -ansi:关闭 gnu c 中与 ansi c 不兼容的特性, 激活 ansi c 的专有特性(包括禁止一些 asm inline typeof 关键字, 以及 UNIX,vax 等预处理宏)
  • -fno-asm 实现 ansi 选项的功能的一部分,它禁止将 asm, inlinetypeof 用作关键字

-funsigned-char / -fno-signed-char-fsigned-char / -fno-unsigned-char 设置 char 类型

  • 这四个参数是对 char 类型进行设置, 决定将 char 类型设置成 unsigned char(前两个参数)或者 signed char (后两个参数)

-include file 引用代码

  • 功能就相当于在代码中使用 #include<filename>

gcc hello.c -include /root/pianopan.h 

-imacros file / -Dmacro / -Umacro / -undef 添加/取消宏定义

  • -imacros file:将 file 文件的宏, 扩展到 gcc/g++ 的输入文件, 宏定义本身并不出现在输入文件中

  • -Dmacro:相当于 #define macro
  • -Dmacro=defn:相当于 #define macro=defn

  • -Umacro:相当于 #undef macro

  • -undef:取消对任何非标准宏的定义

-Idir 添加头文件查找路径

  • 寻找代码中引用的头文件时,gcc/g++ 会先在当前目录查找, 如果没有找到, 就到默认的头文件目录 (/usr/include) 找
  • 如果使用 -I 指定了目录, gcc/g++ 会先在你所指定的目录查找, 然后再按常规的顺序去找

gcc -I/usr/openwin/include hello.c

-M / -MD-MM / -MMD 生成文件关联的信息

  • -M:生成文件关联的信息。包含目标文件所依赖的所有源代码
  • -MD:和 -M 相同,但是输出将导入到 .d 文件里
gcc -M hello.c

  • -MM:生成文件关联的信息。但忽略由 #include<file> 造成的依赖关系
  • -MMD:和 -MM 相同,但是输出将导入到 .d 文件里

-llibrary 指定函数库

  • 指定编译时使用的函数库 (新版的 gcc 会主动将需要的函数库放进来编译)

gcc -lcurses hello.c 	# 使用 ncurses 库编译程序
gcc -lm hello.c			# 使用 libm.so 库 (l 代表 libs,省略扩展名)

-Ldir 指定函数库的搜索路径

  • 指定编译的时候,搜索函数库的路径。如果不指定,编译器将只在标准库的目录 (/lib/lib64) 中搜索

-O-O0-O1-O2-O3 优化选项

  • -O0-O1-O2-O3 为编译器的 4 个优化级别,-O0 表示没有优化, -O1 为默认值-O3 优化级别最高
  • -O 为产生最佳化的参数

-static 禁用动态函数库

  • 禁用动态函数库,所以编译出来的文件一般很大,但不需要动态链接就可以运行

-share 尽量使用动态库

  • 尽量使用动态库,所以生成文件比较小

-w 不生成任何警告信息

-Wall 生成所有警告信息

-x 设定文件所使用的语言, 无视后缀名

-x language filename
  • 设定文件所使用的语言, 使后缀名无效, 对以后的多个有效。也就是说,如果你很个性,决定你的 C 代码文件的后缀名是 .pig,那你就要用这个参数, 这个参数对他后面的文件名都起作用,除非到了下一个参数的使用。
  • 可以使用的参数:‘c’, ‘objective-c’, ‘c-header’, ‘c++’, ‘cpp-output’, ‘assembler’, 与 'assembler-with-cpp’

-x none filename	 
  • 关掉上一个选项,也就是让 gcc 根据文件名后缀,自动识别文件类型

gcc -x c hello.pig -x none hello2.c 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值