Linux开发工具:gcc和g++

目录

一. 什么是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

图2.1 使用gcc编译C语言代码

如果我们希望自定义可执行文件的文件名,就需要用到-o选项:

  • -o选项:将生成的文件,输出重定向的特定文件

假设我们希望生成的可执行文件文件名为mytest,那么指令应为:gcc mycode.c -o mytest.txt -std=c99,通过ll指令,可以看到生mytest文件生成成功。

图2.2 使用-o选项自定义可执行文件文件名

我们知道,源文件(.c/.cpp)到可执行程序,需要经过四个步骤(预编译、编译、汇编、链接),每一步完成的工作为:

  1. 预编译(预处理):头文件的包含、注释的删除、宏替换、条件编译,生成.i文件。
  2. 编译:将C/C++代码转换为对应汇编代码,生成,s文件。
  3. 汇编:将汇编代码转换为可识别的机器码(二进制指令)。
  4. 链接:将多个目标文件和链接库进行链接。

由可执行文件生成的四个步骤,引出gcc/g++的三个选项:

  • -E:从当前开始翻译,完成预处理就停止。
  • -S:从当前开始翻译,完成到编译就停止。
  • -c:从当前开始翻译,完成到汇编就停止。

通过指令gcc -E mycode.c -o mycode.i -std=c99,生成预处理后的文件mycode.i,用vim打开两份文件,可以看出所有的条件编译、注释、宏和头文件,均按规则进行了处理(见图2.3)

图2.2 预处理完成后就停止
图2.3 预处理前(右)后(左)的文件内容对比

通过gcc -S mycode.i -o mycode.s -std=c99,生成编译后的文件mycode.s,使用vim打开mycode.s文件,可以看到程序对应的汇编代码。

图2.3 完成编译工作就停止
图2.4 对mycode.i编译生成的汇编代码

通过指令gcc -c mycode.s -o mycode.o -std=c99,生成二进制的目标文件mycode.o,这里由于vim是文本编辑器,查看二进制文件mycode.o看到的会是乱码,可以使用od来查看二进制文件内容,od查看二进制文件指令为:od 二进制文件名

图2.5 完成汇编工作就停止
图2.6 使用od查看二进制文件

如果不使用-o、-i、-s选项中的任意一个,那么直接默认完成到链接工作,生成可执行文件。

可执行程序形成的时候,不是无序的二进制构造,而是遵循自己的格式 -- ELF格式。通过指令:readelf -S 可执行文件名可以查看可执行文件的二进制构成。

图2.7 readelf查看可执行文件的二进制构成

三. 库和链接

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语言的动态库和静态库。

图3.1 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查看文件属性,看到采用静态链接的可执行文件占用空间明显大于采用动态链接的。

图3.2 采用动态链接和静态链接的方法生成可执行文件

关于查看可执行程序的动静态链接情况,有以下两条指令:

  • file指令:查看可执行文件的动静态链接情况。
  • ldd指令:查看可执行文件动静态链接所依赖的动态库。
图3.3 file和ldd指令查看

关于动静态库是否存在与动静态链接是否能够完成的关系,有下面三条规律:

  1. 如果静态库不存在,那么一定不能完成静态链接。
  2. 如果动态库不存在,静态库存在,gcc不使用-static选项,那么依旧能够生成可执行文件,只不过是以静态链接的方式生成的。
  3. 根据第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占用更多空间。 

图4.1 采用Release和Debug发布可执行程序

通过指令 readelf -S mytest-static | grep -i debug,可以筛选出可执行文件中的调试信息。

图4.2 可执行文件调试信息的提取

五. makefile和make

如果源文件过多,那么手动输入文件名就会十分麻烦,并且如果要删除可执行程序,采用rm批量删除容易误删。通过makefile和make配合使用,可以避免上述问题。

  • makefile:是一个文件。
  • make:是一条指令。

我们需要在源文件的路径下面,通过touch makefile,创建名为makefile的文件。vim makefile打开文件,在首行输入依赖关系,另起一行输入依赖方法(见图5.1),保存并退出makefile文件。我们只需要在命令行中输入指令make,即可生成可执行文件,输入make clean,即可清除可执行文件。

图5.1 makefile文件的内容
图5.2 通过makefile和make实现对源文件的翻译和删除可执行程序

makefile真正的价值,在于同时对多个文件的编译,如果每次gcc/g++操作都输入全部的源文件,容易出错且麻烦,在makefile中设置相应的操作,可以直接使用make指令对多份源文件编译。如图5.3,通过cat指令输出head.h、test.cpp以及main.cpp文件的内容,在设置makefile文件内容如图5.4,此时直接执行make指令,生成可执行文件mytest.exe。

图5.3 文件内容
图5.4 makefile文件内容

如图5.5所示,连续多次执行make指令,只有第一次能生成可执行文件。但是,在使用追加重定向,向main.cpp文件中写入新内容,再次执行make,就又可以编译生成可执行程序了,我们可以认为这是不允许make连续编译,是因为源文件内容没有改变,不会对可执行程序造成影响,更改源文件内容后,编译会生成不同的可执行文件,就允许make执行编译操作。

图5.5 make指令生成可执行文件

问题:make如何判断是否执行编译操作?

答:根据源文件和可执行文件最后一次修改的时间来判断是否编译,如果源文件最后一次修改时间晚于可执行文件,那么make就会对源文件进行编译,否则不会。

这里介绍一条指令:

  • stat 文件名 -- 显示文件状态信息

如果5.6所在,使用stat输出main.cpp文件的状态信息后,可以看到有Access、Modify、Change三个时间状态信息,他们所代表的含义分别为:

  1. Access -- 文件最后一次访问时间。
  2. Modify -- 文件最后一次修改内容的时间。
  3. Change -- 文件最后一次修改属性的时间。
图5.6 stat输出文件状态信息

六. 总结 

  • 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的配合使用,可以快捷实现翻译源文件和删除可执行程序。
  • 11
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值