目录
一. 什么是gcc和g++
二. gcc的基本使用方法
三. 库和链接
3.1 动态库和静态库
3.2 动态链接和静态链接
四. Debug和Release
五. makefile和make
六. 总结
一. 什么是gcc和g++
- gcc:Linux下编译C语言程序的编译器
- g++:Linux下编译C++代码的编译器
由于C++兼容C语言,因此g++既可以编译C++也可以编译C语言。但是,一般建议使用gcc编译C语言,使用g++编译C++。
gcc和g++的使用方法完全一致,因此,本文以gcc为例讲解gcc/g++的使用方法。
二. gcc的基本使用方法
以mycode.c代码为例,生成可执行文件的一般指令为:
- gcc mycode.c -std=c99
生成可执行文件的默认文件名为a.out,程序运行的指令为:./a.out
如果我们希望自定义可执行文件的文件名,就需要用到-o选项:
- -o选项:将生成的文件,输出重定向的特定文件
假设我们希望生成的可执行文件文件名为mytest,那么指令应为:gcc mycode.c -o mytest.txt -std=c99,通过ll指令,可以看到生mytest文件生成成功。
我们知道,源文件(.c/.cpp)到可执行程序,需要经过四个步骤(预编译、编译、汇编、链接),每一步完成的工作为:
- 预编译(预处理):头文件的包含、注释的删除、宏替换、条件编译,生成.i文件。
- 编译:将C/C++代码转换为对应汇编代码,生成,s文件。
- 汇编:将汇编代码转换为可识别的机器码(二进制指令)。
- 链接:将多个目标文件和链接库进行链接。
由可执行文件生成的四个步骤,引出gcc/g++的三个选项:
- -E:从当前开始翻译,完成预处理就停止。
- -S:从当前开始翻译,完成到编译就停止。
- -c:从当前开始翻译,完成到汇编就停止。
通过指令gcc -E mycode.c -o mycode.i -std=c99,生成预处理后的文件mycode.i,用vim打开两份文件,可以看出所有的条件编译、注释、宏和头文件,均按规则进行了处理(见图2.3)
通过gcc -S mycode.i -o mycode.s -std=c99,生成编译后的文件mycode.s,使用vim打开mycode.s文件,可以看到程序对应的汇编代码。
通过指令gcc -c mycode.s -o mycode.o -std=c99,生成二进制的目标文件mycode.o,这里由于vim是文本编辑器,查看二进制文件mycode.o看到的会是乱码,可以使用od来查看二进制文件内容,od查看二进制文件指令为:od 二进制文件名。
如果不使用-o、-i、-s选项中的任意一个,那么直接默认完成到链接工作,生成可执行文件。
可执行程序形成的时候,不是无序的二进制构造,而是遵循自己的格式 -- ELF格式。通过指令:readelf -S 可执行文件名,可以查看可执行文件的二进制构成。
三. 库和链接
3.1 动态库和静态库
链接,就是将汇编之后生成的二进制目标文件,与标准库进行链接。
库,本质上就是源文件(.c/.cpp)经过一定的翻译后进行打包后的文件,库有自己的路径。使用库可以达到隐藏源文件的目的, 库中提供函数方法的具体实现。
可以这样认为:头文件提供方法声明 + 库提供方法实现 + 用户代码 = 软件。
库,可分为动态库和静态库,在Window和Linux系统中,根据后缀名不同来区分动静态库:
- 在Linux环境下:.so(动态库)、.a(静态库)
- 在Windows环境下:.dll(动态库)、.lib(静态库)
库有自己的命名规则:libname.so.XXX,libname.a.XXX,其中库的实际名称只有中间name那一小部分,lib为前缀,.so.XXX/.a.XXX为后缀。
在Liunx系统中,库文件默认存储在路径/lib64/下,使用指令ls /lib64/libc.so*和ls /lib64/libc.s*,可以分别查看C语言的动态库和静态库。
Linux系统中,默认只有C/C++的动态库,没有静态库,静态库需要自行安装,安装方法为:
- C语言静态库:yum install -y glibc-static
- C++静态库:yum install -y libstdc++-static
3.2 动态链接和静态链接
- 动态链接,就是在代码执行过程中,通过链接关系,让执行流跳转到库中去执行。
- 静态链接,就是将库中的方法,拿到源文件调用库中方法的位置,执行流不会跳转到库的内部执行。
如果动态库缺失,那么涉及到动态库的所有程序,都不能正常运行。如果静态库缺失,方法的实现已经被提取到了可执行文件中,程序依旧能正常运行。
动态链接和静态链接各有其优缺点:
- 动态链接可执行文件小,但如果动态库缺失,可能大范围造成程序不可运行
- 静态链接静态库缺失不影响程序运行,安全性更高,但是可执行程序的占用空间会大幅膨胀。
在默认情况下,gcc采用动态链接的方式编译源文件,如果希望采用静态链接的方式编译代码,首先要保证静态库存在,然后显示声明选项-static, 即可执行静态链接。
- gcc mycode.c -o mytest-static - std=c99 -static -- 采用静态链接方式编译mytest.c文件
如图3.2所示,分别采用动态链接和静态链接的方式,编译mycode.c源文件,生成可执行文件mytest和mytest-static,通过ll查看文件属性,看到采用静态链接的可执行文件占用空间明显大于采用动态链接的。
关于查看可执行程序的动静态链接情况,有以下两条指令:
- file指令:查看可执行文件的动静态链接情况。
- ldd指令:查看可执行文件动静态链接所依赖的动态库。
关于动静态库是否存在与动静态链接是否能够完成的关系,有下面三条规律:
- 如果静态库不存在,那么一定不能完成静态链接。
- 如果动态库不存在,静态库存在,gcc不使用-static选项,那么依旧能够生成可执行文件,只不过是以静态链接的方式生成的。
- 根据第1和第2条规律,如果gcc不使用-static选项,那么可执行文件可能是动态链接和静态链接并存的。
四. Debug和Release
Debug为调试版本,以Debug版本发布的可执行程序,会带有调试信息。Release为发布版本,以Release版本发布的可执行程序,不会带有调试信息。Release相比于Debug,运行效率高,运行内存消耗低,占用空间少,整体的性能较优,但是Release版本不能调试。
gcc默认情况采用Release版本发布可执行文件,如果要采用Debug版本发布,那么需要-g选项。
图4.1对比了采用Release版本可Debug版本发布的可执行文件mytest和mytest-debug的占用空间大小,可以看出Debug发布的可执行程序相比于Release占用更多空间。
五. makefile和make
如果源文件过多,那么手动输入文件名就会十分麻烦,并且如果要删除可执行程序,采用rm批量删除容易误删。通过makefile和make配合使用,可以避免上述问题。
- makefile:是一个文件。
- make:是一条指令。
我们需要在源文件的路径下面,通过touch makefile,创建名为makefile的文件。vim makefile打开文件,在首行输入依赖关系,另起一行输入依赖方法(见图5.1),保存并退出makefile文件。我们只需要在命令行中输入指令make,即可生成可执行文件,输入make clean,即可清除可执行文件。
通过指令 read -S mytest-static | grep -i debug,可以筛选出可执行文件中的调试信息。
六. 总结
- gcc/g++都是Linux下的代码编译器,gcc只能编译C语言代码,g++既能编译C语言代码也能编译C++代码,因为C++兼容C语言。
- gcc/g++能够实现从源文件到可执行文件的翻译,有-E/-S/-c选项,功能分别为翻译到 预编译/编译/汇编 就终止翻译,如果不显示声明-E/-S/-c的任意一个,那么直接完成链接操作,生成可执行文件。
- gcc默认生成的可执行文件的文件名为a.out,如果希望自定义可执行文件文件名,需要-o选项实现,-o的功能为:将生成的文件,输出重定向的特定文件。
- 库可以分为动态库和静态库,通过后缀名来区分动态库和静态库,库本质上是源文件经过翻译后生成的文件,具有自己的路径,同时,库遵循特定的命名规范。在Linux下,各种库文件存储在路径/lib64/下。
- 链接分为动态链接和静态链接,动态和静态链接要依赖动态库和静态库。动态链接的可执行文件体积小,但是如果动态库丢失就无法正常运行,安全性相对较低。静态链接的可执行文件体积大,但静态库丢失以及能正常运行。
- gcc默认采用动态链接的方式生成可执行文件,如要采用静态链接,应使用-static选项。
- gcc默认采用Release版本生成可执行文件,如果要使用Debug版本,应当使用-g选项。
- 通过makefile和make、make clean的配合使用,可以快捷实现翻译源文件和删除可执行程序。