gcc/g++ 链接库的编译与链接

GCC编译步骤

gcc -E t1.c -o t1.i 预处理

gcc -S t1.i -o t1.s 转成汇编语言

gcc -c t1.s -o t1.o 转成机器码

gcc t1.o -o t1.exe 链接

直接使用gcc t1.c将自动编译链接生成t1.out

或gcc t1.c -o t1.exe规定生成可执行文件的文件名

举例:

#include <stdio.h>

int main()
{
printf("hello world!");
return 0;
}

1.1预处理

gcc/g++ 链接库的编译与链接

预处理后生成1.i文件,打开如下:相当于把stdio.h文件包含进1.c文件了

//.....
# 1 "/usr/include/sys/cdefs.h" 1 3 4
# 43 "/usr/include/sys/cdefs.h" 3 4
# 1 "/usr/include/machine/_default_types.h" 1 3 4
# 41 "/usr/include/machine/_default_types.h" 3 4 # 41 "/usr/include/machine/_default_types.h" 3 4
typedef signed char __int8_t; //......
# 741 "/usr/include/stdio.h" 3 4
static __inline int
_getchar_unlocked(void)
{
struct _reent *_ptr; _ptr = (__getreent());
return (__sgetc_r(_ptr, ((_ptr)->_stdin)));
} static __inline int
_putchar_unlocked(int _c)
{
struct _reent *_ptr; _ptr = (__getreent());
return (__sputc_r(_ptr, _c, ((_ptr)->_stdout)));
}
# 797 "/usr/include/stdio.h" 3 4 # 2 "1.c" 2 # 3 "1.c"
int main()
{
printf("hello world!");
return 0;
}

1.2转汇编

gcc/g++ 链接库的编译与链接

转汇编 后生成1.s文件,打开如下:

	.file	"1.c"
.text
.def __main; .scl 2; .type 32; .endef
.section .rdata,"dr"
.LC0:
.ascii "hello world!\0"
.text
.globl main
.def main; .scl 2; .type 32; .endef
.seh_proc main
main:
pushq %rbp
.seh_pushreg %rbp
movq %rsp, %rbp
.seh_setframe %rbp, 0
subq $32, %rsp
.seh_stackalloc 32
.seh_endprologue
call __main
leaq .LC0(%rip), %rcx
call printf
movl $0, %eax
addq $32, %rsp
popq %rbp
ret
.seh_endproc
.ident "GCC: (GNU) 7.3.0"
.def printf; .scl 2; .type 32; .endef

1.3转为机器码

gcc/g++ 链接库的编译与链接

转机器码(二进制)后生成1.o文件,打开如下:

gcc/g++ 链接库的编译与链接

1.4连接成可执行文件(我在win7上演示的)

gcc/g++ 链接库的编译与链接

生成exe可执行文件

详情点击

gcc/g++的编译参数,这里只介绍 -L 、-l、-include、-I、-shared、-fPIC

-L :表示要链接的库所在的目录。-L.  表示要链接的库在当前目录, -L/usr/lib 表示要连接的库在/usr/lib下。目录在/usr/lib时,系统会自动搜索这个目录,可以不用指明。

-l (L的小写):表示需要链接库的名称,注意不是库文件名称,比如库文件为 libtest.so,那么库名称为test

-include :包含头文件,这个很少用,因为一般情况下在源码中,都有指定头文件。

-I (i 的大写):指定头文件的所在的目录,可以使用相对路径。

-shared :指定生成动态链接库

-fPIC:  表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时事通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码共享的目的。

生成链接库

第1步,生成目标文件:g++ -c xxx.cpp

gcc/g++ 链接库的编译与链接

第2步,创建静态链接库:  ar  cqs  libxxxx.a  xx1.o xx2.o xx3.o (参数选项请看【5】)

gcc/g++ 链接库的编译与链接

第3步,程序中使用静态链接库

gcc/g++ 链接库的编译与链接

第4步,创建动态链接库 g++ -fPIC -shared -o libxxx.so xx1.cpp xx2.cpp xx3.cpp

gcc/g++ 链接库的编译与链接

第5步,动态链接库使用

gcc/g++ 链接库的编译与链接

 库的链接,上面简单演示了一遍库的生成过程,但是还有很多细节没有讲清楚。以下问题需要注意:

1. 链接过程中可能出现多种链接方式,需要使用一些参数来指定,下面只是一个演示,在测试时,自己填写具体的名称

[plain] view plain copy

  1. g++ testmain.o -o testmain -WI,-Bstatic -lstaticlib -WI,-Bdynamic -ldynamiclib

2. 链接过程中同一个库(名称相同)的静态和动态两种链接库,在链接过程中,系统优先选择动态链接库

gcc/g++ 链接库的编译与链接

3. 动态链接库路径,系统默认在/usr/lib 和/usr/local/lib两个库目录搜索,自己定义的库需要格外指定路径(设定变量LD_LIABRARY_PATH)或者将其拷贝到这两个目录下,在上面的例子的测试过程,已经有说明。当然也可以将当前路径添加到/etc/ld.so.conf文件中或者/etc/ld.so.conf.d目录下的一个文件中。

4. 查看动态链接库。有时候可能需要查看一个库中到底有哪些函数,nm命令可以打印出库中的涉及到的所有符号。库既可以是静态的也可以是动态的。nm列出的符号有很多,常见的有三种:

一种是在库中被调用,但并没有在库中定义(表明需要其他库支持),用U表示;

一种是在库中定义的函数,用T表示,这是最常见的;

另一种所谓的“弱态”符号,它们虽然在库中定义,但可能被其他库中的同名符号覆盖,用W表示。

gcc/g++ 链接库的编译与链接

使用ldd命令可以查看程序的库依赖:

gcc/g++ 链接库的编译与链接

上一篇:Node.js实战项目学习系列(2) 开发环境和调试工具


下一篇:ECharts数据图表系统? 5分钟上手!