首页 > 其他分享 >编译和链接以及makefile

编译和链接以及makefile

时间:2024-09-18 11:21:35浏览次数:3  
标签:文件 makefile 我们 编译 add main 链接

编译和链接以及makefile

问题引出,为什么我们会忽略编译和链接这个步骤

在这里插入图片描述

  • 一定都会用到但却很少被重视的步骤——编译和链接,通常这两个步骤被我们的IDE封装的很完美,我们一般都是一件构建。

在这里插入图片描述

  • 但是一旦遇到错误的时候,尤其是链接相关的错误,很多人就束手无策了,所以今天就跟大家一起深入探讨一下编译和链接的整个过程。

编译

在这里插入图片描述

  • 首先,什么是编译呢,编译的过程其实就是将我们程序的源代码,翻译成CPU能够直接运行的机器代码。
#include <stdio.h>

int add(int a, int b);

int main()
{
    printf("Hello, World!\n");
    int result = add(5, 5);
    return 0;
}

  • 比如我们写了一个源文件main.c,里面简单输出了一行文字,并且调用了一个函数add,而这个函数add被定义在另一个源文件math.c中。
int add(int a,int b)
{
	return a+b;
}
  • 这里我们就可以调用gcc -c来分别编译这两个源文件,需要注意的是,编译永远都是以单个源文件为单位的,在实际开发中,我们通常会将不同功能的代码分散到不同的源文件,一方面方便代码的阅读和维护,同时也提升了软件构建的速度。、

在这里插入图片描述

  • 比如你修改了一个源文件,那么只需要单独编译它这一个,而不需要浪费时间重新编译整个工程,可以看到在编译之后会生成两个扩展名为.o的文件,它们被称作目标文件(Object File),目标文件是一个二进制的文件,文件格式是ELF(Executable and Linkable Format),linux下所有可执行文件的通用格式,相应的Windows使用的是另一种格式PE,它们虽然互不兼容但在结构上非常相似,都是对二进制代码的一种封装。

在这里插入图片描述

  • 我们可以在文件头部找到可执行文件的基本信息比如支持的操作系统,机器类型等等。

在这里插入图片描述

  • 后面是一系列的区块,被叫做sections,里面有我们的机器代码还有程序的数据等等,比如.text是代码区,里面是之前编译好的机器代码,.data是数据区,里面保存了我们初始化的全局变量、局部静态变量等等,后面还有一些其他的区块稍后再说。

在这里插入图片描述

  • 需要注意的是,目标文件虽然包含了编译之后的机器代码,但它并不能够直接执行,操作系统也不允许你去执行它。

在这里插入图片描述

  • 因为在编译的过程中,我们用到了尚未定义的函数add,而我们主程序中的add其实只是一句声明而已,它被定义在另一个模块math.c中,这同样也包括我们用到的标准库中的printf,如果我们去查看stdio.h头文件,其中的printf只是一句函数的声明而已,换句话说,我们在编译main.c的时候,编译器是完全不知道printf和add函数的存在的,比如它们位于内存的哪个区块,代码长什么样,都是不知道的,因此编译器只能将这两个函数的跳转地址暂时先设为0,随后在连接的时候再去修正它。

在这里插入图片描述

  • 比如我们来看一下main.o这个目标文件中的内容,这里的mian是编译之后的主函数代码,左边是机器代码右边是对应的反汇编,可以看到这里的两个call指令,很明显它们分别对应之前调用的printf和add函数,但是你会发现它们的跳转地址都被设成了0,而这里的0会在后面链接的时候被修正。

链接

在这里插入图片描述

  • 另外为了让链接器能够定位到这些需要被修正的地址,在代码块中我们还可以找到一个重定位表,比如在.text区块中,需要被重定位的两个函数printf和add,它们位于偏移量14和23的位置,后面是地址的类型和长度,这和我们之前看到的机器代码是一一对应的。

在这里插入图片描述

  • 我们将另一个源文件math.c也编译出来,最后连同main.o一起链接生成一个独立的可执行文件。我们用到的还是gcc命令,后面传入之前编译的这两个目标文件,随后在目录下可以找到生成的可执行文件main,而这个main就可以直接运行了。

在这里插入图片描述

  • 所以链接其实就是将编译之后的所有目标文件,连同用到的一些静态库、运行时库,组合拼装成一个独立的可执行文件,其中就包括我们之前提到的地址修正,在这个时候,链接器就会根据我们的目标文件或者静态库中的重定位表,找到那些需要被重定位的函数、全局变量,从而修正它们的地址。

在这里插入图片描述

  • 但如果我们在链接的时候,忘记提供必须的目标文件,比如这里的math.o,由于链接器找不到add函数的实现,于是报错“引用未定义”,或者有的编译器也叫它“符号未定义(undefined symbols)”,意思就是我们用到了add但链接器却无法找到它的定义,因此只能报错。

makefile

在这里插入图片描述

  • 如果我们每次都手动编译再链接显然不够高效,实际开发也没有人这么做,通常我们都是用各种各样的IDE或者构建工具帮我们自动化了。

在这里插入图片描述

  • 所以这里引出一种最简单的构建工具makefile(make),可能很多人对它的印象是很古老。但其实makefile除了软件构建之外还有许多其他的奇技淫巧,比如用它来自动生成文档等等,像很多现在的项目也都还在用它,譬如Android OS的构建等等,makefile的核心其实是对“依赖(Dependency)”的管理,比如要构建main则需要main.o和math.o这两个文件,同时执行下面这条链接指令,如果要构建main.o又需要main.c这个文件,同时执行下面这条“编译”指令。

在这里插入图片描述

  • 然后你会发现,makefile其实就是在定义一颗依赖树,你要构建最上方的这个目标就需要提供下面这些节点的文件,然后这样层层地递归下去。

在这里插入图片描述

  • 有了makefile以后我们可以调用make命令,后面跟上目标的名称main,它会自动根据我们的“依赖树”递归地去构建这个可执行文件,当然第一次运行由于所有叶子节点都不存在,make会自动构建所有的依赖,包括其中的目标文件,但如果我们再运行一次make由于所有的文件都已经存在并且是最新的,make就不会在重复构建了。

在这里插入图片描述

在这里插入图片描述

  • 此时如果我们再单独修改一下main.c文件,由于main.c只会影响main.o从而影响最后的可执行文件main,所以make只会去重新生成这两个相关的文件,从而避免了其他不必要的文件编译,其实所有的现代化构建工具,都用到了相同的原理——对依赖的管理,只不过加入了一些更实用的功能比如脚本语言的支持,第三方库的管理等等。

标签:文件,makefile,我们,编译,add,main,链接
From: https://blog.csdn.net/m0_73640344/article/details/142328584

相关文章

  • 如何批量缩短长链接?简单三步,快速提升工作效率!
    在如今的线上推广领域,链接扮演着至关重要的角色。然而,长链接却存在诸多弊端,比如占用字符过多、影响排版美观、点击率欠佳,且生成的二维码过于复杂影响识别。因此,许多小伙伴会借助短网址生成器,将长链接转化为短链接后加以使用。但不少生成器要么只能逐条生成,要么一次最多生成10......
  • 网站友情链接,设置新窗口打开无效
    如果网站的友情链接设置为在新窗口中打开但实际点击时并未如此,可能的原因有几个方面:HTML代码问题:确保 <a> 标签中包含了 target="_blank" 属性。这个属性告诉浏览器在点击链接时应在新窗口或新标签页中打开链接。html <ahref="http://example.com"target="_bla......
  • 【CMake】使用CMake在Visual Stdudio编译资源文件和多目标编译
    一、资源文件的编译首先,我们的项目结构如下,存在图片和第三方库:配置主CMakel......
  • 全网最简单最详细的反编译小程序教程
    一、准备工具小程序解密包(百度网盘)下载链接node.js提前下载,我上一个文章有教程二、获取wxapkg包在电脑在登录微信,找到存放小程序文件源代码的位置打开文件夹后一定要返回上一个文件打开Applet,删除所有的wx+数字的文件,确保等下找到最新运行的小程序文件,文件夹不用退出......
  • 信息学奥赛初赛天天练-91-CSP-S2023基础题3-编译命令、树的重心、拓扑排序、进制转换
    PDF文档公众号回复关键字:202409172023CSP-S选择题1单项选择题(共15题,每题2分,共计30分:每题有且仅有一个正确选项)11以下哪个命令,能将一个名为main.cpp的C++源文件,编译并生成一个名为main的可执行文件?()Ag++-omainmain.cppBg++-omain.cppmainCg++......
  • IP地址判断-编译原理
    分析给定字符串是否是IP,我们先考虑IP地址如何构成a.b.c.d,其中\(a,b,c,d\)均为\(0\)到\(255\)之间的整数那么我们考虑设计代码框架,如果字符串是IP地址,返回True,否则返回Falseboolsolve(strings){}接下来考虑检查IP字符串的基本格式,包含以下规则所有字符均为阿拉伯数......
  • Oliver编译安装(Windows10+VisualStudio2022)
    Oliver是一个开源的非线性视频编辑器。主要基于Qt和FFmpeg开发。前置条件电脑上需要的环境Qt(>=5.15)VisualStudio(2022,其他版也可)vcpkg软件安装安装Qt5.15令人糟糕的是,Qt如今变得不太容易安装。自从Qt5.15以后的版本,就取消了离线安装。所有的Qt后序版本就只能通......
  • day05_编译原理学习
    第四章语法分析和的计算和定义定义:被定义为从推导得到的串首符号的集合(其中是任意的文法符号)。算法:求解的方法:不断应用以下规则,直到没有新的终结符号或空集被加入到任何集合中为止。1)如果X是一个终结符号,那么;2)如果X是一个非终结符,且是一个产生式,在中且在所有的中......
  • day07_编译原理学习
    第四章语法分析LR文法的概述LR文法的概念LR文法是最大的,可以构造出相应移入-归约语法分析器的文法类L:对输入进行从左到右的扫描R:反向构造出一个最右推导序列LR(k)分析需要向前查看k个输入符号的LR分析k=0和k=1这两种情况具有实践意义,当省略k时,k=1LR分析法的......
  • Linux 基础入门操作-实验二 makefile使用介绍 和 实验三 hello 输出
    1介绍Makefile是linux下的项目管理工具,想象一下当有很多源文件需要编译、链接时,你只需执行make命令即可完成编译操作,这样是不是很方便呢。make命令执行时,需要一个Makefile文件,用来告诉make命令需要怎么样的去编译和链接程序,下面详细介绍Makefile的使用与书写规......