首先我们要了解gcc的编译过程。
gcc编译过程分为4步:预处理、编译、汇编、链接
下面以一个简单的输出文件test.cpp来演示gcc的这四步编译过程。
test.cpp代码如下:
#include <iostream> using namespace std; int main() { cout << "This my first cpp programer in Linux" << endl; return 0; }
1.预处理-Pre-Procession
预处理生成的是.i文件,实际上就是将include的头文件展开。
# -E 选项指示编译器对输入文件进行预处理 g++ -E test.cpp -o test.i
预处理文件的最后结果如下:
我们可以看到文件比原来大了好多,预处理将头文件iostream展开,其他部分基本不变。
2.编译-Compiling
编译生成.s文件,编译过程是将C代码转换成汇编语言。
# -S 编译选项告诉 g++ 为 C++ 代码产生汇编语言后停止编译 # g++ 产生的汇编语言文件的缺省扩展名是 .s g++ -S test.i -o test.s
产生的.s文件的部分结果如下:
可以看多很多汇编指令。
3.汇编-Assembling
汇编是将编译生成的.s文件转换为二进制语言,生成一个.o文件。
# -c 编译选项告诉 g++ 为 C++ 代码编译为机器语言停止编译 # 缺省时 g++ 建立的目标代码有一个 .o的扩展名 g++ -c test.s -o test.o
test.o文件部分结果如如下:
可以看到是乱码,这是因为vim不识别机器语言导致的。
4.链接-Linking
链接需要的其他文件后生成可执行文件。
# -o 编译选项来为将产生的可执行文件用指定的文件名 g++ test.o -o test
1.-g 编译带调试信息的可执行文件
# -g 选项告诉 GCC 产生能被 GNU 调试器使用的调试信息,以调试程序。 # 产生带调试信息的可执行文件test g++ -g test.cpp -o test
2.-O[n] 优化源代码
## 所谓优化,例如省略掉代码中从未使用的变量、直接将常量表达式用结果值代替等等,这些操作会所见所包含的代码量,提高最终生成的可执行文件的运行效率 # -O 选项告诉 g++ 对源代码进行基本的优化。这些优化在大多数情况下都会是程序执行的更快。 -O 选项告诉g++产生尽可能小和尽可能快的代码。 # -O 同时减小代码的长度和执行时间,效果等价与-O1 # -O0 表示不做优化 # -O1 为默认优化 # -O2 完成-O1的优化之后,进行一些额外的调整工作,如指令调整等 # -O3 包括循环展开和其他一些与处理特性相关的优化工作 # 选项将使编译的速度比 -O 时慢,但通常产生的代码执行速度更快。 # 使用 -O2优化源代码,并输出可执行文件 g++ -O2 test.cpp -o test_with_O2
3.-l和 -L 指定库文件 | 指定库文件路径
# -l参数(小写)就是用来指定程序要链接的库,-l参数紧接着就是库名 # 在/lib和/usr/lib/和usr/local/lib里的库直接用-l参数就能链接 # 链接glog库 g++ -lglog test.cpp # 如果库文件没有放在上面三个目录里,需要使用-L参数(大写)制定库文件所在的目录 # -L参数跟着的是库文件所在的目录名 # 链接mytest库,libmytest.so在/home/bing/mytestlibfolder目录下 g++ -L/home/bing/mytestlibfolder -l libmytest test.cpp
4.-I 指定头文件搜索目录
# -I # /usr/include目录一般不用指定,gcc知道去那里找,但是如果头文件不在/usr/include里我们就要用-I参数指定了,比如头文件放在/myinclude目录里,那编译命令行就要加上-I/myinclude参数了,不加会得到一个“xxxx.h:No such file or directory”的错误,-I也可以用相对路径来指定。 g++ -I/myinclude test.cpp
# 在使用gcc/g++编译的时候定义宏 # 常用场景 # -DDEBUG 定义DEBUG宏,可能文件中有DEBUG宏的相关信息,用个DDEBUG来选择开启或关闭DEBUG
样例如下:
// -Dname 定义宏name,默认定义内容为字符串“1” # include <stdio.h> { using std::cout; #ifdef DEBUG printf("DEBUG LOG\n"); #endif printf("in\n"); }
使用g++编译器对目录结构如下的多文件程序进行编译。
三个文件的内容如下:
// include file and definition of prototype #include <iostream> void Swap(int &a, int &b);
// definition of function #include "Swap.h" void Swap(int &a, int &b) { int temp; temp = a; a = b; b = temp; }
// main function #include "Swap.h" int main() { using namespace std; int a = 10; int b = 20; cout << "a = " << a << endl; cout << "b = " << b << endl; Swap(a,b); cout << "After Swap(a,b)" << endl; cout << "a = " << a << endl; cout << "b = " << b << endl; return 0; }
在终端输入如下指令,
g++ main.cpp ./src/Swap.cpp -Iinclude
## 进入src目录下 cd src # 汇编,生成Swap.o文件 -c和-I的顺序可以颠倒 g++ Swap.cpp -c -I../include # 生成静态库libSwap.a ar rs libSwap.a Swap.o ## 回到上级目录 cd .. # 链接,生成可执行文件:staticmain g++ main.cpp -Iinclude -lSwap -Lsrc -o staticmain
通过./staticmain运行该文件,运行结果如下:
## 进入src目录下 cd src # 生成动态库libSwap.so g++ Swap.cpp -I../include -fPIC -shared -o libSwap.so ## 上面的命令等价于以下两条命令 # g++ Swap.cpp -I../include -c -fPIC # gcc -shared -o libSwap.so Swap.o ## 回到上级目录 cd .. # 链接,生成可执行文件:dyna_main g++ main.cpp -Iinclude -lSwap -Lsrc -o dyna_main
输入./dyna_main,来运行该文件,会提示运行错误:
./dyna_main: error while loading shared libraries: libSwap.so: cannot open shared object file: No such file or directory
出现该错误的原因是需要添加libSwap.so的路径,通过以下命令运行:
LD_LIBRARY_PATH=src ./dyna_main
运行结果如下:
GDB介绍:
GDB主要功能:
调试执行: 执行gdb[exefilename],进入gdb调试程序,其中exefilename为要调试的可执行文件名
## 以下命令后括号内为命令的简化使用,比如rur(r),输入r就代表run (gdb)help(h) # 查看命令帮助, 在gdb中输入help + 命令 (gdb)run(r) # 重新开始运行文件(run-text:加载文本文件,run-bin:加载二进制文件) (gdb)start # 单步执行,运行程序,停在第一行执行语句 (gdb)list(l) # 查看源代码(list-n,从第n行开始查看代码。list + 函数名:查看具体函数) (gdb)set # 设置变量的值 (gdb)next(n) # 单步调试(逐过程,函数直接执行) (gdb)step(s) # 单步调试(逐语句:跳入自定义函数内部执行) (gdb)backtrace(bt) # 查看函数的调用的栈帧和层级关系 (gdb)frame(f) # 切换函数的栈帧 (gdb)info(i) #查看函数内部变量的数值 (gdb)finish # 结束当前函数,返回到函数调用点 (gdb)continue(c) # 继续运行 (gdb)print(p) #打印值及地址 (gdb)quit(q) # 退出gdb
(gdb)break+num(b) # 在num行设置断点 (gdb)info breakpoints # 查看当前设置的所有断点 (gdb)delete breakpoints num(d) #删除第num个断点 (gdb)display # 追踪查看具体变量值 (gdb)undisplay # 取消追踪观察变量 (gdb)watch # 被设置观察点的变量发生修改时,打印显示 (gdb)i watch # 显示观察点 (gdb)enable breakpoints # 启用断点 (gdb)disable breakpoints # 禁用断点 (gdb)x # 查看内存x/20xw 显示20个单元,16进制,4字节没单元 (gdb)run argv[1] argv[2] # 调试时命令行传参 (gdb)set follow-fork-mode child # Makefile项目管理:选择跟踪父子进程(fork())
Tips:
1.编译程序时加上-g,之后才能用gdb -g main.c -o main
2.回车键:重复上一命令