bingoogolapple.github.io icon indicating copy to clipboard operation
bingoogolapple.github.io copied to clipboard

GCC 编译器

Open bingoogolapple opened this issue 3 years ago • 3 comments

gcc 和 GCC 是两个不同的东西

GNU 编译器套装(英语:GNU Compiler Collection,缩写为 GCC)

指一套编程语言编译器,以 GPL 及 LGPL 许可证所发行的自由软件,也是 GNU 计划的关键部分,也是 GNU 工具链的主要组成部分之一。

  • gcc 是 GCC 中的 GUN C Compiler(C 编译器)
  • g++ 是 GCC 中的 GUN C++ Compiler(C++ 编译器)
  • 一个有趣的事实就是,就本质而言,gcc 和 g++ 并不是编译器,也不是编译器的集合,它们只是一种驱动器,根据参数中要编译的文件的类型,调用对应的 GUN 编译器而已

软件安装

  • Ubuntu 上安装 gcc,g++,gdb
sudo apt install build-essential gdb
gcc --version
g++ --version
gdb --version
  • Mac 上安装 gdb
brew install gdb
gdb --version

bingoogolapple avatar Mar 07 '21 06:03 bingoogolapple

编译过程

  1. 预处理 - Pre Processing,生成 .i 文件
# -E 选项指示编译器仅对输入文件进行预处理
g++ -E HelloWorld.cpp -o HelloWorld.i
  1. 编译 - Compiling,生成 .s 文件
# -S 编译选项告诉 g++ 在为 C++ 代码产生了汇编语言文件后停止编译
# g++ 产生的汇编语言文件的缺省扩展名是 .s
g++ -S HelloWorld.i -o HelloWorld.s
  1. 汇编 - Assembling,生成 .o 文件
# -c 选项告诉 g++ 仅把源代码编译为机器语言的目标代码
# 缺省时 g++ 建立的目标代码文件有一个 .o 的扩展名
g++ -c HelloWorld.s -o HelloWorld.o
  1. 链接 - Linking,生成可执行文件
# -o 编译选项来为将产生的可执行文件用指定的文件名
g++ HelloWorld.o -o HelloWorld

一步到位

g++ HelloWorld.cpp -o HelloWorld

bingoogolapple avatar Mar 07 '21 06:03 bingoogolapple

g++ 重要编译参数

  1. -g 编译带调试信息的可执行文件
# -g 选项告诉 GCC 产生能被 GNU 调试器 GDB 使用的调试信息,以调试程序。
# 产生带调试信息的可执行文件 HelloWorld
g++ -g HelloWorld.cpp -o HelloWorld
  1. -O[n] 优化源代码
## 所谓优化,例如省略掉代码中从未使用过的变量、直接将常量表达式用结果值代替等等,这些操作
会缩减目标文件所包含的代码量,提高最终生成的可执行文件的运行效率。
# -O 选项告诉 g++ 对源代码进行基本优化。这些优化在大多数情况下都会使程序执行的更快。-O2 选项告诉 g++ 产生尽可能小和尽可能快的代码。 如 -O2、-O3、-On(n 常为 0–3)
# -O 同时减小代码的长度和执行时间,其效果等价于 -O1
# -O0 表示不做优化
# -O1 为默认优化
# -O2 除了完成 -O1 的优化之外,还进行一些额外的调整工作,如指令调整等
# -O3 则包括循环展开和其他一些与处理特性相关的优化工作
# 选项将使编译的速度比使用 -O 时慢, 但通常产生的代码执行速度会更快。
g++ -O2 HelloWorld.cpp -o HelloWorld
# 通过 time 统计执行时间
time ./HelloWorld
  1. -l(小写的 L)和 -L(大写的 L)指定库文件 | 指定库文件路径
# -l 参数(小写的 L)就是用来指定程序要链接的库,-l 参数紧接着就是库名
# 在 /lib、/usr/lib、/usr/local/lib 里的库直接用 -l 参数就能链接
# 例如链接 glog 库
g++ -lglog HelloWorld.cpp
# 如果库文件没放在上面三个目录里,还需要使用 -L 参数指定库文件所在目录
# -L 参数跟着的是库文件所在的目录名
# 例如链接 mytest 库,libmytest.so 在 /home/mytestlibfolder目录下
g++ -L/home/mytestlibfolder -lmytest HelloWorld.cpp
  1. -I(大写的 I)指定头文件搜索目录
# /usr/include 目录一般是不用指定的,gcc 知道去那里找,但如果头文件不在 /usr/icnclude
 里我们就要用 -I 参数指定了,比如头文件放在 /myinclude 目录里,那编译命令行就要加上I/myinclude 参数了,如果不加你会得到一个「fatal error: 'xxx.h' file not found」的错
误。-I 参数可以用相对路径,比如头文件在当前目录,可以用 -I. 来指定。上面我们提到的 –cflags 参数就是用来生成 -I 参数的。
g++ -I/myinclude HelloWorld.cpp
  1. -Wall 打印警告信息
# 打印出 gcc 提供的警告信息
g++ -Wall HelloWorld.cpp
  1. -w 关闭警告信息
# 关闭所有警告信息
g++ -w HelloWorld.cpp
  1. -std=c++11 设置编译标准
# 使用 c++11 标准编译 HelloWorld.cpp
g++ -std=c++11 HelloWorld.cpp
  1. -o 指定输出文件名
# 指定即将产生的文件名,不指定时默认输出为 a.out
# 例如指定输出可执行文件名为 HelloWorld
g++ HelloWorld.cpp -o HelloWorld
  1. -D 定义宏
# 在使用 gcc/g++ 编译的时候定义宏
# 常用场景: -DDEBUG 定义 DEBUG 宏,可能文件中有 DEBUG 宏部分的相关信息,用个DDEBUG 来选择开启或关闭
g++ -DDEBUG HelloWorld.cpp -o HelloWorld && ./HelloWorld
#include <iostream>

int main(int argc, char **argv)
{
    std::cout << "Hello World" << std::endl;
#ifdef DEBUG
    std::cout << "DEBUG LOG\n" << std::endl;
#else
    std::cout << "ONLINE LOG\n" << std::endl;
#endif
    return EXIT_SUCCESS;
}

bingoogolapple avatar Mar 07 '21 07:03 bingoogolapple

GDB 调试器

前言

  • GDB(GNU Debugger) 是一个用来调试 C/C++ 程序的功能强大的调试器,是 Linux 系统开发 C/C++ 最常用的调试器
  • 程序员可以使用 GDB 来跟踪程序中的错误,从而减少程序员的工作量
  • Linux 开发C/C++ 一定要熟悉 GDB
  • VSCode 是通过调用 GDB 调试器来实现 C/C++ 的调试工作的
  • Windows 系统中,常见的集成开发环境(IDE),如 VS、VC等,它们内部已经嵌套了相应的调试器

GDB 主要功能

  • 设置断点(断点可以是条件表达式)
  • 使程序在指定的代码行上暂停执行,便于观察
  • 单步执行程序,便于调试
  • 查看程序中变量值的变化
  • 动态改变程序的执行环境
  • 分析崩溃程序产生的 core 文件

常用调试命令参数

调试开始:执行 gdb [exefilename] ,进入 gdb 调试程序,其中 exefilename 为要调试的可执行文件名

  1. 编译程序时需要加上-g,之后才能用gdb进行调试:gcc -g main.c -o main
  2. 回车键:重复上一命令
# 以下命令后括号内为命令的简化使用,比如 run(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())

bingoogolapple avatar Mar 07 '21 08:03 bingoogolapple