都是编译器,二者的选项是重叠的
基本上,gcc专门用于编译c,g++专门用于编译c++
gcc/g++形成的可执行程序默认是release版的。若要debug版,则使用-g
选项。
如:gcc -o mytest test.c -g -std=c99
编译命令格式例:
gcc 选项 要编译的文件 选项 目标文件
头文件与源文件位于同一路径下时,gcc只需要包含源文件,多个源文件使用空格隔开
编译过程:
预处理(宏替换等)
预处理功能主要包括宏定义,文件包含,条件编译,去注释等。
预处理指令是以#号开头的代码行。
实例: gcc –E hello.c –o hello.i
选项-E
的作用是让 gcc 在预处理结束后停止编译过程。选项-o
是指目标文件,“.i”文件为已经过预处理的C原始程序。
编译(翻译成汇编)
在这个阶段中,gcc 首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,gcc 把代码翻译成汇编语言。
用户可以使用-S
选项来进行查看,该选项只进行编译而不进行汇编,生成汇编代码。
实例: gcc –S hello.i –o hello.s
汇编(生成机器码)
汇编阶段是把编译阶段生成的“.s”文件转成目标文件
在此可使用选项“-c”就可看到汇编代码已转化为“.o”的二进制目标代码了
例: gcc –c hello.s –o hello.o
链接(生成可执行文件或库文件)
在成功编译之后,就进入了链接阶段。
例: gcc hello.o –o hello
这里涉及一个重要概念:函数库
我们的C程序中,并没有定义“printf”的函数实现,且在预编译中包含的“stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实“printf”函数的呢?
答案是:系统把这些函数实现都被做到名为 libc.so.6 的库文件中去了,在没有特别指定时,gcc 会到系统默认的搜索路径“/usr/lib”下进行查找,也就是链接到 libc.so.6 库函数中去,这样就能实现函数“printf”了,而这也就是链接的作用
库是在链接的时候使用的
动态和静态的区别在于链接阶段处理库的方式
静态 把库中的需要的方法直接拷贝到程序中,拷贝后(静态链接成功后)不再依赖原库
动态 将库中的需要的方法的地址拷贝到程序中,程序调用方法时需要根据地址在原库中寻找方法
这两种处理方式分别被称为静态链接方式和动态链接方式,
用静态库比较容易浪费空间(与动态链接的程序相比体积大一百倍左右算正常的),用动态库比较依赖原库的存在
静态库专门用于让编译器对用户的程序进行静态链接,
动态库专门用于让编译器对用户的程序进行动态链接
windows:动态库为.dll,静态库为.lib
linux:动态库.so,静态库.a,默认使用动态链接
使用file指令可查看文件类型与详细属性,对可执行程序使用可以看到链接方式:
若要指定使用静态链接,则在链接命令最后加 -static
:
一般的云服务器默认只有动态库,可能无法执行-static,
,安装c与c++静态库:yum install glibc-static libstdc++-static -y
ldd指令是list dynamic dependencies的缩写,意思是列出动态库依赖关系,
对静态链接的可执行文件使用则会提示该文件非动态:
make与makefile
make是一个命令
makefile是一个文件,需要创建在当前源代码路径下,
通过编辑该文件,可以实现自动化构建代码。
makefile是一个围绕依赖关系和依赖方法构建的自动化编译工具
myfile:myfile.c是依赖关系,意思是要用依赖文件myfile.c生成目标文件myfile
后续内容是依赖方法
依赖关系中,目标文件对应的依赖文件列表可以为空
指令:
make myfile
make clean
可使用对应方法
正确的依赖关系配合正确的依赖方法才能完成一件事
.PHONY
重复执行编译时会提示已为最新:
若需要强制重复执行应使用.PHONY修饰相应方法:
.PHONY修饰的方法总是会被执行,被修饰的符号称为伪目标
通常clean会需要总是执行,而编译并不需要
判断可执行程序是否最新,是通过对比源代码文件与可执行文件的修改时间的先后来实现的