gcc使用
如何编写 gcc 或者 g++ 指令来编译 C、C++ 程序;
需经历 4 个过程,分别是预处理、编译、汇编和链接;
预处理, 展开头文件/宏替换/去掉注释/条件编译 (test.i main .i)
编译, 检查语法,生成汇编 ( test.s main .s)
汇编, 汇编代码转换机器码 (test.o main.o)
链接, 链接到一起生成可执行程序 a.out
C语言编译过程详解:
https://blog.csdn.net/weixin_41143631/article/details/81221777
====================================================
无选项编译链接
gcc test.c
作用:将test.c预处理,汇编,编译,链接形成可执行文件;默认输出为a.out
选项 -o
gcc test.c -o test
将test.c预处理、汇编、编译并链接形成可执行文件test。
-o选项用来指定输出文件的文件名。
选项 -E
gcc -E test.c -o test.i
将test.c预处理输出test.i文件。中间文件*.i,实际工作中通常不用专门生成这种文件;
选项 -S
gcc -S test.i
将预处理输出文件test.i汇编成test.s文件。
选项 -c
将汇编输出文件test.s编译输出test.o文件
无选项链接
gcc test.o -o test
将编译输出文件test.o链接成最终可执行文件test。
选项-O
gcc -O1 test.c -o test
使用编译优化级别1编译程序。级别为1~3,级别越大优化效果越好,但编译时间长。
多源文件的编译方法:
1、多个文件一起编译:
#gcc testfun.c test.c -o test
将testfun.c和test.c分别编译后链接成test可执行文件
2、分别编译各个源文件,之后对编译后输出的目标文件链接:
#gcc -c testfun.c //将testfun.c编译成testfun.o
#gcc -c test.c //将test.c编译成test.o
#gcc testfun.o test.o -o test //将testfun.o和test.o链接成test
第一种方法编译时需要所有文件重新编译,
第二种方法可以只重新编译修改的文件,未修改的文件不用重新编译。
====================================================
函数库实际上就是一些头文件(.h)和库文件(.so或者.a)的集合。
Linux下的大多数函数都默认将头文件放到 /usr/include/目录下,而库文件则放到/usr/lib/目录下。
如果依赖库不在上述默认路径下;
GCC在编译时必须有自己的办法来 查找所需要的头文件和库文件;
GCC采用搜索目录的办法来查找所需要的文件,
-I选项可以向GCC的头文件搜索路径中添加新的目录。
gcc foo.c -I /home/xiaowp/include -o foo
有一种-i,小写的方式,用于指定要引用的头文件;
不过一般不采用这种方法。因为直接在源文件中引用该头文件即可。
使用大写L,用于指定要链接库的路径。要链接的库去掉.so,.lib,.a等后缀
然后加上-l前缀,如下所示:
gcc foo.c -L /home/xiaowp/lib -lfoo -o foo
GCC在链接时优先使用动态链接库,只有当动态链接库不存在时才考虑使用静态链接库,
如果需要的话 可以在编译时加上-static选项,强制使用静态链接库。
gcc foo.c -L /home/xiaowp/lib -static -lfoo -o foo
假设现在要用到外部库:
inclulde文件夹的路径是/usr/dev/mysql/include
lib文件夹是/usr/dev/mysql/lib
首先编译test.c为目标文件
gcc –c –I /usr/dev/mysql/include test.c –o test.o
最后我们把所有目标文件链接成可执行文件
gcc –L /usr/dev/mysql/lib –lmysqlclient test.o –o test
如果需要的话可以在编译时加上-static选项,强制使用静态链接库。
gcc –L /usr/dev/mysql/lib –static –lmysqlclient test.o –o test
======================================================
GCC 编译器提供有大量的指令选项,可满足我们在大部分场景下的编译需求。
gcc -g test.c -o test.exe //-g 可调试记号; -o 表示指定输出文件,后面跟上输出文件的名字
//将c文件编译成一个可执行的二进制文件;
-c 选项只是令 GCC 编译器将指定文件加工至汇编阶段,但不执行链接操作。
======================================================
链接:
静态链接是由链接器在链接时将库的内容加入到可执行程序中的做法。
链接器是一个独立程序,将一个或多个库或目标文件(先前由编译器或汇编器生成)链接到一块生成可执行程序。
静态链接是指把要调用的函数或者过程链接到可执行文件中,成为可执行文件的一部分。
动态链接所调用的函数代码并没有被拷贝到应用程序的可执行文件中去,
而是仅仅在其中加入了所调用函数的描述信息(往往是一些重定位信息)。
仅当应用程序被装入内存开始运行时,在Windows的管理下,才在应用程序与相应的DLL之间建立链接关系。
当要执行所调用DLL中的函数时,根据链接产生的重定位信息,Windows才转去执行DLL中相应的函数代码。
在 Linux 发行版中,静态链接库和动态链接库通常存放在 /usr/bin 或者 /bin 目录下。
系统库文件在 /usr/bin 目录下的存储状态。
在 Linux 发行版系统中,动态链接库的后缀名通常用 .so 表示
==================================================
链接库示例:
gcc main.c -o main.out -lm
数学库的文件名是 libm.a。前缀lib和后缀.a是标准的,m是基本名称,
GCC 会在-l选项后紧跟着的基本名称的基础上自动添加这些前缀、后缀,本例中,基本名称为 m。
链接其它目录中的库:
GCC 会自动在标准库目录中搜索文件,例如 /usr/lib,如果想链接其它目录中的库,就得特别指明。
把链接库作为一般的目标文件,为 GCC 指定该链接库的完整路径与文件名。
如果链接库名为 libm.a,并且位于 /usr/lib 目录,
那么下面的命令会让 GCC 编译 main.c,然后将 libm.a 链接到 main.o
gcc main.c -o main.out /usr/lib/libm.a
使用-L选项,为 GCC 增加另一个搜索链接库的目录
gcc main.c -o main.out -L/usr/lib -lm
可以使用多个-L选项,或者在一个-L选项内使用冒号分割的路径列表。
设置LIBRARY_PATH,以便gcc能够找到编译时需要的动态链接库。
设置LD_LIBRARY_PATH,以便程序加载运行时能够自动找到需要的动态链接库。
LD_LIBRARY_PATH环境变量用于在程序加载运行期间查找动态链接库时指定除了系统默认路径之外的其他路径。
=================================================
在一个 C(或者 C++)项目中,往往在存储多个源文件,如果仍按照之前“先单独编译各个源文件,
再将它们链接起来”的方法编译该项目,需要编写大量的编译指令,事倍功半。
=================================================
链接顺序:
静态库链接时搜索路径顺序:
1. ld会去找GCC命令中的参数-L
2. 再找gcc的环境变量LIBRARY_PATH
3. 再找内定目录 /lib /usr/lib /usr/local/lib 这是当初compile gcc时写在程序内的
动态链接时、执行时搜索路径顺序:
1. 编译目标代码时指定的动态库搜索路径
2. 环境变量LD_LIBRARY_PATH指定的动态库搜索路径
3. 配置文件/etc/ld.so.conf中指定的动态库搜索路径
4. 默认的动态库搜索路径/lib
5. 默认的动态库搜索路径/usr/lib
有关环境变量:
LIBRARY_PATH环境变量:指定程序静态链接库文件搜索路径
LD_LIBRARY_PATH环境变量:指定程序动态链接库文件搜索路径
=============【makefile中和编译相关的一些宏】====================
CFLAGS 表示用于 C 编译器的选项 //实际上涵盖了编译和汇编两个步骤
LDFLAGS 是告诉链接器从哪里寻找库文件,
LIBS 是告诉链接器要链接哪些库文件。
不过使用时链接阶段这两个参数都会加上,所以你即使将这两个的值互换,也没有问题。
CFLAGS: 指定头文件(.h文件)的路径,如:CFLAGS=-I/usr/include -I/path/include。
同样地,安装一个包时会在安装路径下建立一个include目录,
当安装过程中出现问题时,试着把以前安装的包的include目录加入到该变量中来。
LDFLAGS:gcc 等编译器会用到的一些优化参数,也可以在里面指定库文件的位置。
用法:LDFLAGS=-L/usr/lib -L/path/to/your/lib。
LIBS:告诉链接器要链接哪些库文件,如LIBS = -lpthread -liconv
=================【gcc编译选项】===========================
GCC编译选项CFLAGS参数
-c 用于把源码文件编译成 .o 对象文件,不进行链接过程
-o 用于链接生成可执行文件,在其后可以指定输出文件的名称
-g 用于在生成的目标可执行文件中,添加调试信息,可以使用GDB进行调试
-Idir 用于把新目录添加到include路径上,可以使用相对和绝对路径,“-I.”、“-I./include”、“-I/opt/include”
-Wall 生成常见的所有告警信息,且停止编译,具体是哪些告警信息,请参见GCC手册,一般用这个足矣!
-w 关闭所有告警信息
-O 表示编译优化选项,其后可跟优化等级0\1\2\3,默认是0,不优化
-fPIC 用于生成位置无关的代码
-v (在标准错误)显示执行编译阶段的命令,同时显示编译器驱动程序,预处理器,编译器的版本号
GCC链接选项LDFLAGS参数
-llibrary 链接时在标准搜索目录中寻找库文件,搜索名为liblibrary.a 或 liblibrary.so
-Ldir 用于把新目录添加到库搜索路径上,可以使用相对和绝对路径,“-L.”、“-L./include”、“-L/opt/include”
-Wl,option 把选项 option 传递给连接器,如果 option 中含有逗号,就在逗号处分割成多个选项
-static 使用静态库链接生成目标文件,避免使用共享库,生成目标文件会比使用动态链接库大
复杂命令行参数:gcc的-Wl,
-Wl,表示后面的参数将传给link程序ld(因为gcc可能会自动调用ld),
如果后面的ld参数有空格,怎么传呢?比如想加一个-rpath /path?
方法为:
-Wl,rpath,/path
习惯上在命名库文件的时候通常与soname相同
libxxxx.so.major.minor
其中,xxxx是库的名字,major是主版本号,minor 是次版本号
-Wl,-soname
-Wl 告诉编译器将后面的参数传递到连接器。
-soname 指定了共享库的soname,这个soname的存在是为了兼容方便。
比如:
有一个程序ap1,以及一个库libtest.so.1
ap1启动的时候需要libtest.so.1
如果链接的时候直接把libtest.so.1传给了ap1,那么将来库升级为libtest.so.2的时候,ap1仍然只能使用libtest.so.1的代码,并不能得到升级的好处。
而如果指定了soname为libtest.so,那么ap1启动的时候将查找的就是libtest.so而不是其在被链接时实际使用的库libtest.so.1这个文件名。
在开始时我们建立一个链接:ln -sf libtest.so.1 libtest.so
而在库升级后,我们重新:ln -sf libtest.so.2 libtest.so即可,这样ap1不需要任何变动就能享受升级后的库的特性了。
而libtest.so.1,libtest.so.2可以同时存在于系统内,不必非得把libtest.so.2的名字改成libtest.so.1
lib+库名+so.主版本号.小版本号.build号 //共享库本身的文件名
主版本号,代表当前动态库的版本,如果动态库的接口有变化,那么这个版本号就要加1;
后面的两个版本号(小版本号 和 build 号)是告诉你详细的信息,比如为一个hot-fix 而生成的一个版本,其小版本号加1,build号也应有变化。
这个文件名包含共享库的代码。
lib + math +.so //共享库的连接名
link name,顾名思义,就是在编译过程,link 阶段用的文件名。 其将soname 和real name 关联起来。
在共享库编译过程中,连接(link) 阶段,编译器将生成一个共享库及real name,同时将共享库的soname,写在共享库文件里的文件头里面。
当应用程序加载时就会通过soname去给定的路径下寻找该共享库
可以用命令 readelf -d sharelibrary 去查看。
================================================
===============【gcc生成库和使用库】=========================================
gcc:
如何生成静态库:
静态库只是一堆object对象的集合,使用ar命令可以将.o文件打包成.a静态库。
假设gcc已经生成了a.o, b.o, c.o,使用下面的命令即可生成libmylib.a
ar rcs libmylib.a a.o b.o c.o
如何生成动态库:
动态库的生成由gcc直接生成。
假设a.c, b.c两个文件,通过下面的命令可生成libmylib.so
#gcc a.c b.c -o libmylib.so --shared
如何使用库
gcc中关于库的参数有:
-L 指定搜寻库的目录
如指定当前目录 gcc -L .
-l 指定要链接的库的名称
加入库的名称是libmylib.a,则gcc -l mylib,即去头去尾。
--static 组织在链接时使用动态库
--shared 生成动态库
--static-libgcc 链接静态libgcc库
--shared-libgcc 链接动态libgcc库
可见对动态库和静态库的使用方法是一样的,同一个库如果同时存在动态库和静态库,优先链接动态库,除非使用--static强制使用静态库。
=============【库的名称】=======================
静态库的名字一般是libxxx.a,利用静态库编译生成的文件比较大,因为整个静态库所有的数据都会被整合进目标代码中。
动态库的名字一般是libxxx.so,相对于静态库,动态函数库在编译的时候并没有被编译进目标代码中,
你的程序执行到相关函数时才调用函数库里面相应的函数,因此使用动态函数库生成的可执行文件比较小。
由于函数库没有编译进可执行程序,而是程序运行时动态的申请调用,所以程序的运行环境中必须提供相应的库。
动态函数库的改变也不会影响可执行程序,针对可执行程序的动态函数库升级也比较容易。
通常,对函数的链接是在编译期的时候完成的,所有相关的对象文件与相关联的library被链接成一个可执行文件,这就是使用静态库的编译过程,
而动态库技术:对一些库函数的链接载入推迟到程序运行的时候。
但是,不管是使用静态库还是使用动态库,这些库文件都是由*.o文件生成的,在给编译器gcc传不同的参数,就会生成相应的静态库或者是动态库了。
=================================================
https://www.runoob.com/w3cnote/gcc-parameter-detail.html
http://c.biancheng.net/gcc/
https://blog.csdn.net/TheBeatles1994/article/details/81055418