- 预处理(进行宏替换)
- 编译(生成汇编)
- 汇编(生成机器可识别代码)
- 连接(生成可执行文件或库文件)
gcc和cc是一样的,c++和g++是一样的,一般c程序就用gcc编译,c++程序就用g++编译
格式 gcc [选项] 要编译的文件 [选项] 【目标文件】
- 预处理功能主要包括头文件替换 宏展开 去掉注释 条件编译 其他预处理指令
- 预处理指令是以#号开头的代码行。
- 实例:
gcc –E hello.c –o hello.i
- 选项“-E”,仅执行编译预处理
- 选项“-o”是指目标文件,“.i”文件为已经过预处理的C原始程序
- 在这个阶段中,gcc 首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,gcc 把代码翻译成汇编语言。
- -c:只编译,而不连接成为可执行文件,生成同名字的 .o 目标文件。通常用于编译不包含主程序的子程序文件。调用了C编译器和汇编器而连接器并没有被执行。
gcc -c test.c
生成:test.ogcc test.c
如果没有指定输出的文件,默认将编译出一个名为a.out的程序,输入./a.out即可执行成功 。 a.out是”assembler output”的缩写格式,代表汇编程序输出。gcc test.c -o test
-o参数用来指定生成目标程序的名字,这样将编译出名为test的程序。- -g: 编译的时候保留调试信息
- 用户可以使用“-S”选项来进行查看,该选项只进行编译而不进行汇编,生成汇编代码。
- 实例: gcc –S hello.i –o hello.s
- 汇编阶段是把编译阶段生成的“.s”文件转成目标文件
- 读者在此可使用选项“-c”就可看到汇编代码已转化为“.o”的二进制目标代码了
- 实例: gcc –c hello.s –o hello.o
在成功编译之后,就进入了链接阶段。
-o参数用于说明输出(output)文件名,gcc将生成一个目标(object)文件xx
gcc test.c -o xx
/gcc -o xx test.c
(顺序可以调换) 输出:xx为程序可执行文件
- 我们的C程序中,并没有定义“printf”的函数实现,且在预编译中包含的“stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实“printf”函数的呢?
- 最后的答案是:系统把这些函数实现都被做到名为 libc.so.6 的库文件中去了,在没有特别指定时,gcc 会到系统默认的搜索路径“/usr/lib”下进行查找,也就是链接到 libc.so.6 库函数中去,这样就能实现函数“printf”了,而这也就是链接的作用
- 静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。其后缀名一般为“.a”
- 动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为“.so”,如前面所述的 libc.so.6 就是动态库。gcc 在编译时默认使用动态库。完成了链接之后,gcc 就可以生成可执行文件,如下所示。 gcc hello.o –o hello
- gcc默认生成的二进制程序,是动态链接的,这点可以通过 file 命令验证
- -E 只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面
- -S 编译到汇编语言不进行汇编和链接
- -c 编译到目标代码
- -o 文件输出到 文件
- -static 此选项对生成的文件采用静态链接
- -g 生成调试信息。GNU 调试器可利用该信息。
- -shared 此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库.
- -O0
- -O1
- -O2
- -O3 编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高
- -w 不生成任何警告信息。
- -Wall 生成所有警告信息
为什么用gcc进行调试的时候需要编译选项中添加 -g?
默认编译生成的可执行文件是无法使用 gdb 来跟踪或调试的,因为可执行程序中没有可供 gdb 调试使用的特殊信息,为了将必要的调试信息整合到可执行文件中,需要用到 -g 选项,这样生成的可执行程序,倘若出现问题,便可以使用 gdb 找出问题具体出现的位置,便于问题的解决。
“-g”标志是对程序进行调试性编译时常用的选项,它将使用特殊版本的C语言标准库完成编译和链接操作,给库函数加上程序调试方面的支持。编译器会把这些标志自动传给链接器。
加上-g选项以后,gcc在编译是会做以下额外的操作:
程序的发布方式有两种,debug模式和release模式
Linux gcc/g++出来的二进制程序,默认是release模式
要使用gdb调试,必须在源代码生成二进制程序的时候, 加上 -g 选项[重要]
vs中debug模式和release模式的区别
Debug通常称为调试版本,通过一系列编译选项的配合,编译的结果通常包含调试信息,而且不做任何优化,以为开发人员提供强大的应用程序调试能力。而Release通常称为发布版本,是为用户使用的,一般客户不允许在发布版本上进行调试。所以不保存调试信 息,同时,它往往进行了各种优化,以期达到代码最小和速度最优。为用户的使用提供便利。
debug程序通常比release程序要慢,尤其是处理视频方便release要比debug快很多。在release模式对程序进行调试的时候经常会遇到变量虽然初始化了,但是在查看其值的时候却发现是一个随机的数并不是初始化的值,有时候在对变量进行监视的时候了,会出现找不到变量的情况,原因大致如下:debug跟release在初始化变量时所做的操作是不同的,debug是将每个字节位都赋成0xcc, 而release的赋值近似于随机。如果你的程序中的某个变量没被初始化就被引用,就很有可能出现异常:用作控制变量将导致流程导向不一致;用作数组下标将会使程序崩溃;更加可能是造成其他变量的不准确而引起其他的错误。所以在声明变量后马上对其初始化一个默认的值是最简单有效的办法,否则项目大了你找都没地方找。代码存在错误在debug方式下可能会忽略而不被察觉到。debug方式下数组越界也大多不会出错,在release中就暴露出来了,这个找起来就比较难了。
只有DEBUG版的程序才能设置断点、单步执行、使用 TRACE/ASSERT等调试输出语句。REALEASE不包含任何调试信息,所以体积小、运行速度快
调试技巧: 打断点 和 F5(开始调试/停止运行)
调试命令:
list/l 行号:显示binFile源代码,接着上次的位置往下列,每次列10行。[重要]
list/l 函数名:列出某个函数的源代码。
n 或 next:单条执行。(逐过程,跳过函数)
s或step:(逐语句,进入函数调用)
break(b) [参数]:给某个位置的代码打断点,参数可以是一个行号,也可以是一个函数
info break :查看断点信息。
finish:执行到当前函数返回,然后挺下来等待命令
print§:打印表达式的值,通过表达式可以修改变量的值或者调用函数
p 变量:查看变量内容
set var:修改变量的值
continue(或c):从当前位置开始连续而非单步执行程序,继续运行,,
run(或r):从开始连续而非单步执行程序
delete breakpoints:删除所有断点
delete breakpoints n:删除序号为n的断点
disable breakpoints:禁用断点
enable breakpoints:启用断点
info(或i) breakpoints:参看当前设置了哪些断点
display 变量名:跟踪查看一个变量,每次停下来都显示它的值
undisplay:取消对先前设置的那些变量的跟踪
until X行号:跳至X行
breaktrace(或bt):查看各级函数调用及参数
info(i) locals:查看当前栈帧局部变量的值
quit / ctrl + d :退出gdb
bt /backtrace 打印调用栈, 查看函数的调用栈帧,可以看到代码的调用关系
frame/f n 切换到某个指定栈帧,代码并没有继续执行,切换状态而已
上面的调试命令都是让gdb启动一个程序,很少用,常用:
1.gbd运行一个程序的方式调试
2.gdb 调试coredump文件
ulimit -a
coredump 就是车祸现场(照片),是程序的临终遗言
核心转储文件 事后调试:(绝大多数场景)
3.gdb附件的方法调试(attcah) 使用gdb attach(附加) 上一个程序
TODO(进程相关的内容) cgdb 可以方便一点 ;有更方便的,但当前还不太成熟。
gcc -g hello.c -o hello
gdb hello
工程管理工具:解决大型项目中的模块之间的依赖关系
makefile:40年前的技术,当前在实际工作中几乎不会手写makefile
当前的现代c/c++工程管理工具本质都是通过自动化的方式生成makefile
一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作
makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率
make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefile都成为了一种在工程方面的编译方法
make是一条命令,是一个文件,两个搭配使用,完成项目自动化构建
vim hello.c
include <stdio.h>
int main()
{
printf("hello Makefile!\n");
return 0;
}
vim Makefile
hello:hello.o
gcc hello.o -o hello
hello.o:hello.s
gcc -c hello.s -o hello.o
hello.s:hello.i
gcc -S hello.i -o hello.s
hello.i:hello.c
gcc -E hello.c -o hello.i
.PHONY:clean
clean:
rm -f hello.i hello.s hello.o hello
三要素 target—目标 dependent—依赖 command— 命令
目标(target):目标文件1 目标文件2
命令列表 欲建立的执行文件 目标文件1 目标文件2
在 makefile 当中的 # 代表批注
make 【选项】 【目标】 【宏定义】
通过命令行参数中的target,可指定make要编译的目标,并且允许同时定义编译多个目标,操作时按照从左向右的顺序依次编译target选项中指定的目标文件。如果命令行中没有指定目标,则系统默认target指向描述文件中第一个目标文件。
-d 显示调试信息
-n 不运行任何makefile文件,只打印需要执行的命令
-p 输出所有宏定义和目标文件描述
-s 静止状态下运行,不显示任何命令行信息
-f file 通知make程序从file中读取内部依赖说明,缺省情况下会读取makefile或者Makefile文件处理,文件名-表示读取标准输入,在Linux中,GNU make工具在当前工作目录中按照GNUmakefile、makefile、Makefile的顺序搜索makefile文件
文件 hello
,它依赖 hell.o
hello.o
, 它依赖 hello.s
hello.s
, 它依赖 hello.i
hello.i
, 它依赖 hello.c
gcc hello.* -option hello.*
,就是与之对应的依赖关系
- make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
- 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“hello”这个文件,并把这个文件作为最终的目标文件。
- 如果hello文件不存在,或是hello所依赖的后面的hello.o文件的文件修改时间要比hello这个文件新(可以用 touch 测试),那么,他就会执行后面所定义的命令来生成hello这个文件。
- 如果hello所依赖的hello.o文件不存在,那么make会在当前文件中找目标为hello.o文件的依赖性,如果找到则再根据那一个规则生成hello.o文件。(这有点像一个堆栈的过程)
- 当然,你的C文件和H文件是存在的,于是make会生成 hello.o 文件,然后再用 hello.o 文件声明make的终极任务,也就是执行文件hello了。
- 这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。
- 在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make根本不理。
- make只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起,我就不工作了
- 工程是需要被清理的
- 像clean这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行,不过,我们可以显示要make执行。即命令——“make clean”,以此来清除所有的目标文件,以便重编译。但是一般我们这种clean的目标文件,我们将它设置为伪目标,用 .PHONY 修饰,伪目标的特性是,总是被执行的。
- 一个makefile中可以存在多个目标,有点目标并不是为了要生成个东西,而仅仅是为了执行一些特定的操作,最典型的就是clean方式(清空之前生成的目标)
- make指令后面带上参数(要去生成的目标)
- 如果Makefile中包含了多个目标,直接敲make默认生成第一个目标
- clean这样的目标,只是为了执行一个动作,而不是为了生成文件,如果此时在目录中碰巧有一个同名的文件,就会影响到动作的执行,就可以使用.PHONY把clean 声明成:“伪目标”不加任何依赖对象,声明这是一个对人恶化文件都有效的方法,因为make不会寻找依赖对象,二十直接执行后面的方法。
vim中批量注释代码:
1.ctrl +v 进入可视列模式
2.拖动光标,吧要注释的代码的第一列都选中
3.shift +i 进入插入模式
4.输入//
5.按esc回到普通模式
有方便的插件,可配置,完成更快速的注释。
调试一个问题的步骤和思路?
1.确认是不是bug;唯一依据产品需求
2.定位问题:找到哪行代码引起的bug 二分法
3.分析问题的原因(幸福感)
4.提出方案并修改问题
5.测试(回归测试,防止代码的修改引入新问题),由于回归测试,成本比较高,往往以自动化测试为主。
包管理器:yum 命令行版本的应用商店
在有些情况下用不了——依赖图形界面:
主流 IDE 也支持远程开发,当前远程开发的用户体验很差
主要矛盾就是解决在没有图形界面的Lnuix环境下来开发调试代码。IDE往往效果差强人意,只能靠一组基于命令行的工具软件来完成日常开发
笔记本 16G 8核 工作机 Windows IDE
开发机 Linux 服务器 128G 28核 代码
-1.首先要创建一个仓库,git最初就是在命令行上使用的
0.git clone 克隆,把服务器的数据下载到本地
git clone [链接]
1.
git add [文件名]
添加 可多个2.
git commit -m " 说明 "
提交到本地 第一次要设置用户名和email 邮箱配置最好和git申请的时候相同 最后的 “.” 表示当前目录3.
git push
同步到服务器
可以保存密码 :
例如,取名yhs
git config --global user.name "yhs"
git config --global user.email "[email protected]"
检查是否配置成功,输入:
git config --global user.name
git config --global user.email