1.gcc编译C源码的四个步骤:
预处理->编译->汇编->链接(test.c test.h => test.i => test.s => test.o => test)
1)预处理 gcc -E test.c -o test.i
2)编译 gcc -S test.i -o test.s
3)汇编 gcc -c test.s -o test.o
4)链接 gcc test.o -o test
执行可执行文件test ./test
注:c文件中的#include宏处理,会在预处理的阶段将c中引用的h文件的内容全部写到c文件中,最后生成.i中间文件,这时h 文件中的内容就相当于被写道c文件中。这也为代码的复用提供了渠道,很多的c文件可以去引用同一个h文件,这样这个h文件就会被放到多个c文件中被编译多 次,这也是h文件中不能放定义只能放声明的原因,放定义时被编译多次,在程序链接的时候(系统中定义了多个int a;强符号定义)会出现错误, 声明就不一样,声明表示对定义的扩展,最终都会终结到一个定义上,所以不会出现link时重复定义的错误。
2.gcc和g++的区别:
GCC:GNU Compiler Collection(GUN 编译器集合),它可以编译C、C++、JAV、Fortran、Pascal、Object-C等语言。
gcc是GCC中的GUN C Compiler(C 编译器)
g++是GCC中的GUN C++ Compiler(C++编译器)
由于编译器是可以更换的,所以gcc不仅仅可以编译C文件
所以,更准确的说法是:gcc调用了C compiler,而g++调用了C++ compiler
gcc和g++的主要区别:
1. 对于 *.c和*.cpp文件,gcc分别当做c和cpp文件编译(c和cpp的语法强度是不一样的)
2. 对于 *.c和*.cpp文件,g++则统一当做cpp文件编译
3. 使用g++编译文件时,g++会自动链接标准库STL,而gcc不会自动链接STL
4. gcc在编译C文件时,可使用的预定义宏是比较少的
5. gcc在编译cpp文件时/g++在编译c文件和cpp文件时(这时候gcc和g++调用的都是cpp文件的编译器),会加入一些额外的宏。
6.在用gcc编译c++文件时,为了能够使用STL,需要加参数 –lstdc++ ,但这并不代表 gcc –lstdc++ 和 g++等价,它们的区别不仅仅是这个。
3.多个文件编译在linux下编译:
例如:下面有三个文件,分别是1.cpp 和 2.cpp 和myhead.h 文件。
1.cpp
#include
#include "myhead.h"
using namespace std;
int main(){
print();
cout<
return 0;
}
2.cpp
#include
#include "myhead.h"
using namespace std;
void print(){
std::cout<
}
myhead.h
#ifndef __myhead_h
#define __myhead_h
void print();
#endif
假如他们都在一个目录下面,那么编译流程:
g++ -c 2.cpp #将2.cpp 编译成2.o 文件
g++ 1.cpp -o a.out 2.o #多个文件一起链接
#或者
g++ -c 2.cpp
g++ -c 1.cpp
g++ 1.o 2.o -o test
如果没有头文件,两个.c文件编译的。例如:
1.cpp
#include
using namespace std;
void fn();
int main(){
cout<
fn();
return 0;
}
2.cpp
#include
void fn(){
std::cout<
}
编译
g++ -c 1.cpp
g++ -c 2.cpp
g++ -o test 1.o 2.o
在稍微大一点的项目里面,一般都会包含多个文件。尤其是包含多个头文件,各自头文件的实现,和包含main函数的文件。这样的好处就是更容易开发和维护。例如:
main.cpp 文件是包含main函数的文件,在myinclude的文件下,包含了myhead.h 和 myhead.cpp 文件。分别是头文件的定义和实现。
tree:
main.cpp
#include
#include
using namespace std;
int main(){
//fun_head();
cout<
int x=100;
int y=200;
cout<
return 0;
}
myhead.h
#ifndef __myhead_h
#define __muhead_h
void print();
int sum(int a,int b);
#endif
myhead.cpp
#include "myhead.h"
#include
using namespace std;
void print(){
cout<
}
int sum(int a,int b){
return a+b;
}
这种情况先编译头文件
g++ -c myhead.cpp -o myhead.o
之后直接编译g++ main.cpp 会报错
error: myhead.h: No such file or directory
因为在LINUX下默认搜索头文件及库文件的路径 找不到myhead.h头文件,自己写的头文件不在默认库中,
所以需要-I+头文件路径,
g++ main.cpp -o main -I ../myinclude/ ../myinclude/myhead.o
工程涉及到c、c++文件,要分别编译,最后再链接,所以可用Makefile来操作
4.将cpp文件编译成动态链接库:
g++ test.cpp -fPIC -shared -o libtest.so
5.链接动态链接库:
将main.cpp与libtest.so链接成一个可执行文件main
#测试可执行程序main是否已经链接的动态库libtest.so,
#如果列出了libtest.so,那么就说明正常链接了。可以执行以下命令:
$ g++ main.cpp -L. -ltest -o main
#执行main可以看看main是否调用了动态链接库中的函数。
$ ldd main
编译参数:
-shared: 该选项指定生成动态连接库(让连接器生成T类型的导出符号表,有时候也生成弱连接W类型的导出符号),不用该标志外部程序无法连接。相当于一个可执行文件
-fPIC:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。
-L.:表示要连接的库在当前目录中
-ltest:编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.so来确定库的名称
-LD_LIBRARY_PATH:这个环境变量指示动态连接器可以装载动态库的路径。它的作用是让动态链接库加载器(ld.so)在运行时(run-time)有一个额外的选项,即增加一个搜索路径列表。注意,LD_LIBRARY_PATH是在运行时,才起作用。这个环境变量中,可以存储多个路径,用冒号分隔。它的厉害之处在于,搜索LD_LIBRARY_PATH所列路径的顺序,先于嵌入到二进制文件中的运行时搜索路径,也先于系统默认加载路径
注:
调用动态库的时候有几个问题会经常碰到,有时,明明已经将库的头文件所在目录 通过 “-I” include进来了,库所在文件通过 “-L”参数引导,并指定了“-l”的库名,但通过ldd命令察看时,就是死活找不到你指定链接的so文件,这时你要作的就是通过修改 LD_LIBRARY_PATH或者/etc/bashrc 文件来指定动态库的目录,通常这样做就可以解决库无法链接的问题了,设置方法:
#打开文件
$ vim /etc/bash.bashrc
#在文件末尾添加
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
#修改完后,记得关掉当前终端并重新打开一个新的终端,从而使上面的配置生效
【参考】
https://www.cnblogs.com/457220157-FTD/p/4062794.html
https://blog.csdn.net/farmwang/article/details/72862088
https://zhuanlan.zhihu.com/p/100050970
https://blog.csdn.net/wads23456/article/details/100099543