• 开发工具——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可以看到,静态链接。

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

  • 相关阅读:
    【Android笔记32】Android中数据存储技术之SQLite事务操作以及存储大文件
    【C++】继承
    java正则表达式 及应用场景爬虫,捕获分组非捕获分组
    快速上手使用本地测试工具postman
    Python入门教程36:urllib网页请求模块的用法
    Baklib协作文档平台|企业如何进行文档协同?
    Golang 语言学习 01 包含如何快速学习一门新语言
    Kernel编译报错:对yaml_emitter_initalize“未定义引用
    微服务开发,这10个点你要知道
    Kotlin apply 交换两个数
  • 原文地址:https://blog.csdn.net/travel_dream/article/details/128164184