开发工具——gcc/g++

开发工具gcc/g++

完成代码的编写完后,要形成可执行程序,需要编译工具进行对代码的编译。

C语言的编译工具是gcc,c++的编译工具是g++。

如果g++没有的话,可以切换到root执行命令yum  install  -y  gcc-c++

C语言和C++的编译:

gcc只能编译C语言,而g++可以编译c++也可以编译C语言。

在C语言的最后一章,已经学习了源代码到可执行程序的的简单步骤。下面来详细一点的说明。

源代码,即程序,本质上是文本;可执行程序是机器语言,也就是二进制组成的。

文本翻译成二进制要经过以下几个步骤:

预处理,编译,汇编,链接。

预处理要进行:宏替换,头文件展开,去注释,条件编译。

gcc和g++编译的过程和选项是一样的。

下面写一个包含宏,头文件,注释,条件编译的代码:

对代码进行编译,gcc test.c

如果要指定输出的可执行文件文件名需要 -o选项:gcc test.c -o mytest,-o的含义是,将程序处理后的结果写入mytest中。

这里gcc的编译是一步到位,想要得到预处理的结果,需要用到-E选项:gcc -E test.c -o test.i

-E的意思是:进行程序翻译的过程中,预处理完成就停下来。这里不带-o会把处理的结果显示到界面上,不方便查看。

vim打开test.c,进入底行模式,输入vs test.i就能比较两种有什么不同。

图示框出的部分即头文件所在路径。头文件要被拷贝进test.i中,就必须能被系统找到,要找到就必须知道头文件所在路径。

由图可知宏替换,注释,条件编译,头文件展开的效果

Ctrl + w + w可以在两个窗口之间来回切换。

下面来看一下系统的头文件:

注意:是usr路径不是user路径

所以,写代码不是包含一下头文件就完了,首先要有头文件,其次要能找到头文件。之前安装vs2019时,除了安装编译器,还要安装编译器所需要的库,也就是说老的编译器不支持新语法是因为附带安装的库中,没有新语法的库。

任何编译器都必须通过一定方式,知道包含的头文件所在路径。

在预处理完后,test.i中还是C语言,也就是说,预处理只是进行文本层面的操作。目的是为了编译更顺畅。

编译的作用:将C语言翻译成汇编语言。

关于汇编语言,不需要了解太多,只需要知道,和C语言不一样就行了。

得到编译的结果要使用-S选项:gcc -S test.i -o test.s

-S的含义:对文件进行处理的过程中,编译完成后就停下来。汇编语言的后缀是.s。

汇编的作用:将汇编语言文件翻译成二进制文件,更准确的说法是,可重定位的二进制文件。以.o或.obj结尾。

这个重定位和重定向(>、<、>>)没有关系。

查看汇编的结果需要使用-c选项:gcc -c test.s -o test.o

-c的含义:对文件进程处理的过程中,会汇编完成后就停止。

这里已经变成二进制文件了

但是仍然无法执行,即使加上x权限后依然无法执行。

原因很简单,虽然转换成二进制后可以被系统识别了,但是其中的一些函数符号,还没有和库关联起来

包含头文件是为了代码能使用头文件中包含的方法(函数)。

而在进行-E -S -c操作的对象始终是test.c,也就是说,从头到尾,只进行了test.c代码的编译。比如printf,包含的头文件,传了参数,但printf是如何实现的?printf实现的操作没有写在test.c中。printf的实现实际上是由C语言的库完成的,但刚才的过程中只编译了test.c的代码。

所以代码中需要的printf实现在哪里?代码中使用的printf如何和printf的实现产生关联?

头文件中是不包含所有函数的实现的,函数的实现存放在头文件所对应的库中。包含头文件只是方便编译。

printf的实现在C语言的标准库中,标准库一般在/lib64/libc*路径下的第一个文件中

只要调用了函数,就还需要最后一步:将使用的printf函数和库中的printf函数通过链接操作关联起来。才能实现可执行程序。

gcc不用添加任何选项,系统就会自动到特定路径下搜索所要使用的库

gcc test.o -o mytest的隐含操作就是链接自己和程序和库,形成可执行程序。

使用ldd指令可以查看可执行程序所依赖的库。

libc.so.6是一个链接文件,指向C语言标准库

Linux平时使用的命令也可能是由C语言写的,可以查看一下。

还有其他的很多文件都会用到C语言标准库。如果删除C语言标准库,那么很多文件和命令都会无法实现。

如果需要记忆预处理,编译,汇编指令的选项,有一个简单的记忆技巧:-E-S-c和esc键只有大小写的区别。形成的对应文件后缀合在一起则是iso,国际标准化组织的简称,也是镜像文件的后缀。

在最开始的学习中,包含头文件被认为是函数声明;在编写代码的时候,比如输入一个函数,会有语法提示,本质上就是通过对头文件进行搜索来完成语法提示的。

而库文件中则存在函数的实现,以供链接,形成自己的可执行程序。

动态库和静态库

动态库和静态库的内容也很多,先基本的认识一下

Linux下动态库后缀:.so;Windows下动态库后缀:.dll

Linux下静态库后缀:.a;Windows下静态库后缀:.lib

下面用一个故事简单介绍一下动态链接,静态链接:

假设你是一个科研人员,你所在的科研机构有一台超级计算机,在进入科研机构的时候,你就被告知超级计算机的位置。一天的日程安排如下:起床,吃饭,做实验,收集数据,用超级计算机分析数据,写报告,和同事聊天……在分析数据之前的行为自己可以独立完成,要分析数据,必须借助于超级计算机,好在知道超级计算机的位置,找到超级计算机,找出对应实验的分析方法,完成数据分析后,分析数据的任务完成,再返回到日程安排中,进行下一项任务。

一天的日程安排就相当于自己写的代码,分析数据就相当于某个需要动态库的函数。在还没有分析数据前就已经知道动态库的位置,这叫做将函数和动态库关联起来,也就是链接的过程(链接不是执行,链接是产生关系)。当开始执行代码时,执行到需要动态库的函数时,就跳转库中(超级计算机),找到对应的函数实现(找出对应实验的分析方法) ,执行完毕后再返回,继续向后运行。

自己的日程行为就相当于自己写的代码,超级计算机中的实验分析方法就相当于库提供的函数实现,这种链接方式就叫做动态链接。动态链接的本质就是,在链接的时候,把需要的函数和库中的位置通过地址的方式关联起来。

科研机构中,需要使用超级计算机的不止一人,超级计算机是被所有科研人员所共享的。类比到系统当中,动态库就相当于超级计算机,被系统中所有文件和命令所共享。

静态链接本质上就是将库中的相关代码直接拷贝到相关程序中。比如有十个printf,每个printf都要把实现自己拷贝一份。静态链接后的程序,只要编译通过,就可以不要库,库只会使用一次。

动态链接的优缺点

优点:大家共享一个库,可以节省资源

缺点:一旦库缺失,会导致几乎所有程序失效。

静态链接的优缺点

优点:不依赖任何库,程序可以独立执行。

缺点:浪费资源。

可以通过file指令查看可执行程序的构成:

从框出来的内容来看,mytest是一个可执行程序,通过动态链接完成。

默认情况下,生成的可执行程序是通过动态链接完成的。

如果想要通过静态链接生成可执行程序,那么需要使用-static选项:gcc test.c -o mytest2 -static

-static选项需要安装静态库

安装指令:yum install -y glibc-static,这是C语言的静态库

安装指令:yum install -y libstdc++-static,这是C++的静态库

在仅仅使用printf函数的情况下,动态链接和静态链接的可执行文件大小差了近100倍。

再对mytest2使用ldd指令,就会弹出mytest2不是动态可执行的,file mytest2可以看到,静态链接。

正常情况下,动态链接的使用情况更多。默认情况下使用的是动态链接,甚至默认状态下是不带静态库的。但是如果软件要安装到别人的系统下,而别人的系统没有安装对应的库,这时候静态库就是必要的了。进行动态链接使用动态库,进行静态链接使用静态库。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值