您的位置:首页 > 财经 > 产业 > 浙江省建设信息港三类人员成绩查询_至尊传奇手游官方正版下载_seo网站诊断流程_网课免费平台

浙江省建设信息港三类人员成绩查询_至尊传奇手游官方正版下载_seo网站诊断流程_网课免费平台

2024/12/23 12:37:38 来源:https://blog.csdn.net/qq_41921826/article/details/144493083  浏览:    关键词:浙江省建设信息港三类人员成绩查询_至尊传奇手游官方正版下载_seo网站诊断流程_网课免费平台
浙江省建设信息港三类人员成绩查询_至尊传奇手游官方正版下载_seo网站诊断流程_网课免费平台

目录

1 程序的编译过程

2 动态链接的优缺点

2.1 动态链接的优点

2.2 动态链接的缺点

2.3 只使用动态链接

3 函数库链接的5个特殊秘密

4 警惕Interpositioning

5 产生链接器报告文件


1 程序的编译过程

程序的编译过程是将源代码转换成计算机可以执行的机器代码的过程。这个过程通常包括以下几个主要步骤:

(1)预处理(Preprocessing预处理是编译过程的起始步骤,主要针对源代码文件中的预处理指令进行相应处理。

  • 宏定义展开(#define):通过#define指令定义宏,预处理时会按照定义规则对代码里相应的宏进行文本替换,比如定义#define PI 3.14,代码中出现PI的地方就会被替换成3.14。
  • 条件编译指令(#ifdef、#ifndef、#endif 等):条件编译指令可依据是否定义了特定宏等条件,来决定代码段是否参与后续编译。
  • 包含头文件(#include:预处理器会把指定头文件的全部内容插入到源代码中相应的位置。例如,#include <stdio.h>会将标准输入输出头文件里的内容(像printf、scanf等函数的声明等)添加进来。

(2)编译(Compilation):将预处理后的源代码转换为中间代码,并进行优化以及最终生成目标代码的过程,涉及多个子环节。

  • 词法分析:编译器会把预处理后的源代码当作字符流,将其拆解成一个个单词(tokens),这些单词涵盖了编程语言里的关键字(如 C 语言中的int、if、for等)、标识符(变量名、函数名等)、常量(整数常量、浮点数常量、字符常量等)、运算符(+、-、*、/等)以及界符(;、{、}等)。例如对于代码int num = 10;,词法分析器能准确分解出 “int”(关键字)、“num”(标识符)、“=”(运算符)、“10”(常量)和 “;”(界符),为后续分析提供基础元素。
  • 语法分析:基于词法分析得到的单词,编译器依据编程语言既定的语法规则构建语法树,以树形结构清晰呈现程序的语法构成以及语句、表达式之间的层次关系。比如针对if语句if (condition) { statement; },语法树会将if作为根节点,其下细分出条件表达式condition节点以及语句块statement节点。一旦代码存在语法错误,像括号缺失或者关键字拼写有误等情况,语法分析阶段就能检测出来。
  • 语义分析:主要检查程序语义的正确性,包含检查变量是否正确定义与使用、类型是否匹配等关键方面。例如在表达式int a = 3.14;中,语义分析环节会察觉到将双精度浮点数赋值给整型变量属于类型不匹配的错误;同时也会处理变量的作用域问题,确保变量仅在其定义的有效范围内被正确访问,例如函数内部定义的局部变量不能在函数外部随意调用。
  • 中间代码生成:部分编译器会生成中间代码,它处于源语言与目标机器语言之间,具备平台无关性优势,常见形式有三地址码等。例如对于表达式a = b + c,可能生成类似t1 = b + c; a = t1的三地址码形式,这种中间表示形式利于后续进一步的优化以及适配不同目标机器的代码生成工作。
  • 代码优化:编译器会对生成的中间代码实施优化操作,旨在提升程序的执行效率,优化手段多种多样。比如常量折叠,像int a = 2 + 3;可直接优化为int a = 5;,减少不必要的运算;死代码消除,即去除那些永远不会被执行的代码,避免占用资源;循环展开,通过将循环体展开一定次数来降低循环控制方面的开销等,通过这些优化策略让程序在执行时间和存储空间利用上更为高效。
  • 目标代码生成:这是编译阶段的收尾环节,会把经过优化的中间代码(若有)或者直接把处理后的代码转换为目标机器的语言,这种目标代码可能是汇编语言形式,也有可能直接就是机器代码(二进制形式),具体取决于编译器的设计以及编译的配置。

(3)汇编(Assembly):汇编器(Assembler)将汇编语言代码转换成机器代码。汇编语言是一种低级语言,它更接近于机器代码,但仍然包含一些助记符,使得程序员更容易理解和编写。

(4)链接(Linking):链接过程旨在将编译生成的多个目标文件(通常以.o(在 Linux 等系统下)或.obj(在 Windows 系统下)为扩展名)与各类库文件以及其他相关目标文件进行合并整合,最终生成一个单一的可执行文件(像.exe(Windows 系统)或.out(Linux 等系统)文件)。

  • 符号解析:目标文件中包含诸多符号,例如函数名、全局变量名等,链接器的关键任务之一就是确定这些符号的具体定义位置。比如一个源文件中调用了另一个源文件里定义的函数int add(int a, int b),在链接时,链接器必须精准找到该函数实现代码所在的目标文件,若无法找到某个符号的定义,就会出现如 “undefined reference to 'add'” 这类链接错误提示。
  • 重定位:目标文件中的代码与数据部分往往包含一些相对地址或者虚拟地址,在链接阶段,需要依照最终可执行文件的整体布局对这些地址进行相应调整。例如,目标文件中某个函数调用指令原本采用相对地址来指向被调用函数,当链接器把多个目标文件组合到一起时,就需要重新计算这些相对地址,以保障函数调用能够准确无误地跳转到被调用函数的实际内存位置,确保程序执行的正确性。
  • 库链接:程序在开发过程中通常会借助一些库来实现额外的功能,像标准 C 库、数学库等,链接器要把这些库文件与目标文件妥善链接起来。库分为静态库和动态库两种类型,静态库是多个目标文件的集合,链接时,链接器会把程序实际使用的静态库中目标文件的代码和数据全部复制到最终可执行文件里。动态库链接时不会把库文件全部代码复制到可执行文件中,而是在程序运行需调用其函数时,由操作系统加载动态库并完成相应函数调用操作。

(5)加载(Loading):加载器(Loader)负责将可执行文件加载到内存中,进而启动程序的执行流程。

2 动态链接的优缺点

2.1 动态链接的优点

动态链接是一种更为现代的方法,能够更加有效地利用磁盘空间,动态链接只需要处理对动态库的引用关系,不需要像静态链接那样将库的全部代码合并到可执行文件中,因此在编译和链接阶段时间也会缩短。尽管单个可执行文件约启动速度稍受影响,但动态链接可以从两个方面提高性能:

(1)动态链接可执行文件比功能相同的静态链接可执行文件的体积小,相应的会节省磁盘空间和虚拟内存。静态链接在生成可执行文件时,会把程序所用到的函数库(比如标准 C 库、数学库等)中相关目标文件的代码和数据全部复制到可执行文件里面,即便它可能只调用了库中的少数几个函数,而动态链接则不同,动态链接的可执行文件中只是包含了对相应动态库中函数和变量等的引用信息,真正的函数库代码并不嵌入到可执行文件里。

(2)当多个可执行文件都动态链接到同一个特定函数库时,在运行期间,操作系统只会把这个函数库加载到内存中一次,形成一个单独的拷贝。例如,在 Linux 系统下,多个应用程序都动态链接了libc.so(C 标准库对应的动态库),那么在内存中只会存在一份libc.so的实例。操作系统内核通过内存映射机制来实现共享,它会将这份已加载到内存中的函数库映射到每个需要使用它的进程的虚拟地址空间中。各个进程虽然感觉像是自己独占了这个函数库,但实际上它们共享的是同一份物理内存中的代码和数据,这样就避免了重复加载相同的函数库内容到内存里。如果可执行文件是静态链接的,每个文件都将拥有一份函数库的拷贝,显然极为浪费。

动态链接使得函数库的版本升级更为容易。新的函数库可以随时发布,只要安装到系统中,旧的程序就能够自动获得新版本函数库的优点而无需重新链接。

2.2 动态链接的缺点

动态链接是一种“just-in-time(JIT)”链接,这意味着程序在运行时必须能够找到它们所需要的函数库。链接器通过把库文件名或路径名植入可执行文件中来做到这一点。这意味着,函数库的路径不能随意移动。如果把程序链接到/user/ib/libthread.so库,那么就不能把该函数库移动到其他的目录,除非在链接器中进行特别说明。否则,当程序调用该函数库的函数时,就会在运行时导致失败,给出这样一条错误信息:

ld.so.1:main:fatal:libthread.so:can't open file:errno 2

当在一台机器上编译完程序后,把它拿到另一台不同的机器上运行时,也可能出现这种

情况。执行程序的机器必须具有所有该程序需要链接的函数库,而且这些函数库必须位于在链接器中所说明的目录。对于标准系统函数库而言,这并不成问题。

2.3 只使用动态链接

动态链接现在是运行System V release4UNIX的计算机所采用的缺省设置。从作用上

看,静态链接现已过时,只能静静躺在一边睡大觉。

使用静态链接的最大危险在于将来版本的操作系统可能与可执行文件所绑定的系统函数库不兼容。如果应用程序静态链接于版本N的操作系统中,当把程序运行于版本N+1的操作系统上时,它可能会立即崩溃,也可能出现一个不明显的错误。我们无法保证早期版本的系统函数库能够在后期版本的系统上正确地运行。事实上,反过来考虑倒还比较保险一点。但是,如果应用程序动态链接到版本N的系统函数库,当它运行于版本N+1的操作系统上时,它就会正确选取N+1版本的系统函数库。

相反,静态链接的应用程序不得不针对每个新版本的操作系统进行重新生成以保证能够运行。而且,有些函数库(扣libaio.so,libdl.so,libsys..so,libsolv.so以及librpcsvc.so等)只能以动态链接的形式使用。如果在应用程序中使用了这些函数库中的任何一个,你的程序就必须使用动态链接。最好的策略就是所有的应用程序都使用动态链接,这就可以避免可能产生的问题。

3 函数库链接的5个特殊秘密

当使用函数库时,需要掌握5个基本的、不明显的约定。绝大多数C语言书籍或手册对此并没有作出清楚的解释。

(1)动态库文件的扩展名是“.so”,而静态库文件的扩展名是“.a

按照约定,所有动态库的文件名的形式是libname.so。这样,线程函数库便被称作libthread.so。静态库的文件名形式是libname.a,共享archive的文件名形式是libname.sa。共享archive只是一种过渡形式,帮助人们从静态库转变到动态库,现在已过时。

(2)通过-lthread选项,告诉编译链接到libthread.so

传给C编译器的命令行参数里并没有提到函数库的完整路径名,甚至没有提到在函数库目录中该文件的完整名字!函数库名字的呈现形式是把 “lib” 部分和文件的扩展名去掉,然后在前面添加一个 “l”。例如,对于名为 libthread.so 的函数库,在命令行中就是通过 -lthread 选项来告知编译器去链接它。

gcc -o thread_demo thread_demo.c -lthread

(3)编译器期望在确定的目录找到库

这里,你可能会疑惑,编译器是怎么知道该往什么目录寻找函数库呢?就像存在一种特殊的规则用于查找头文件一样,编译器也自有办法来寻找函数库。它查看一些特殊的位置,如在/usr/ib中查找函数库,例如线程库位于usr/Iib/libthread.so。

编译器选项-Lpathname告诉链接器一些其他的目录,如果命令中加入了-l选项,链接器就往这些目录查找函数库。系统中存在几个环境变量,LD LIBRARY_PATH和

LD_RUN_PATH,也是用于提供这类信息。出于安全性、性能和创建/运行独立性方面的考虑,使用环境变量的做法现在己经不提倡。一般还是在链接时使用-Lpathname和-Rpathname选项。

(4)观察头文件,确认所使用的函数库

你有可能遇见的另一个关键问题是“我怎么知道必须链接到哪些函数库?如果观察程序中的源代码,就会发现自己调用了一些自己不曾实现的函数。例如,如果程序跟三角有关,可能会调用像sin()和cos()这样的函数,它们可以在math函数库中找到。

一个很好的建议就是可以观察程序所使片的#include指令。在程序中所包舍的每个头文件都可能代表一个必须链接的库,但需要注意头文件的名字通常并不与它所对应的函数库名相似。函数库链接所存在的另一个不一致性就是函数库所包含的某个函数的原型可能与其他头文件中所声明的函数的原型一样。

(5)与提取动态库中的符号相比,静态库中的符号提取的方法限制更严

最后,在动态链接和静态链接的链接语义上还存在一个额外的大区别,它经常会迷惑不够仔细的用户。

假设我们有一个简单的 main.c 文件,里面调用了一些来自标准 C 库的函数(比如 printf 函数,其所在的库在动态链接时会被自动处理),使用 gcc 编译器进行动态链接编译生成可执行文件的命令示例如下:

gcc main.c -o dynamic_executable

这里,-o 选项指定了输出的可执行文件名是 dynamic_executable。在这个过程中,像 printf 这类库符号所在的动态库(例如 libc.so 等)会在运行时被加载到程序的虚拟地址空间,使得程序可以找到并执行相应的函数,并且多个链接在一起的文件都可以访问到这些动态库中的符号。

同样针对上述 main.c 文件进行静态链接,并且假设有一个自定义的静态库 mylib.a,同时假设 main.c 中调用了 mylib.a 里定义的函数以及标准 C 库中的函数(比如 printf),按照正确顺序进行静态链接编译生成可执行文件的命令示例如下:

gcc main.c mylib.a -o static_executable

这里必须保证先写 main.c ,再写 mylib.a ,因为静态链接时符号是从左到右按顺序解析的,如果写成:

gcc mylib.a main.c -o static_executable

可能就会出现问题,因为在处理 mylib.a 这个静态库时,链接器一开始还没遇到 main.c 里对 mylib.a 中符号的未定义引用(也就是还不知道需要从 mylib.a 里找哪些符号),那么就可能不会正确地从 mylib.a 中提取对应的符号,导致后续链接 main.c 时出现符号未定义等错误。

如果在自己的代码之前引入静态库,又会带来另一个问题。因为此时尚未出现未定义的符号,所以它不会从函数库中提取任何符号。接着,当目标文件被链接器处理时,它所有的对函数库的引用都将是未实现的!

例如,像 “cc -lm main.c” 这样进行静态链接(math 库常以静态链接的 archive 形式存在用于提高运行时性能),若程序使用了如 sin 等数学函数,会得到 “Undefined first referenced symbol in file sin main.o ld:fatal:Symbol referencing errors.No output written to a.out” 这样的错误信息。

为从 math 库中提取所需符号,需要写成 “cc main.c -lm” 这种形式,让文件先包含未解析的引用。个人都习惯了通用的命令形式<命令><选项><文件>,所以让链接器采用<命令><文件><选项>这样的约定是很容易引起混淆。

4 警惕Interpositioning

Interpositioning 是一种通过编写与函数同名的函数来取代库函数行为的技术。可以在特定程序中拦截库函数的调用,以便检查参数、返回值或跟踪程序的执行流程,有助于快速定位问题。在某些情况下,可以通过优化同名的用户函数来提高特定操作的执行效率。

使用Interpositioning需要格外小心。很容易发生自己代码中某个符号的定义取代函数库中的相同符号的意外。这意味着不仅自己对该库函数的调用会被自己版本的函数调用所取代,而且所有调用该库函数的系统调用也会被用户函数取代。例如,假设程序中使用了一个库函数 printf 进行输出,而程序员不小心编写了一个同名的 printf 函数。在这种情况下,程序中所有原本应该调用库函数 printf 的地方都会调用用户自定义的 printf 函数,这可能会导致输出结果与预期不符,甚至可能引发程序错误。

当编译器注意到库函数被另外一个定义覆盖时,它通常不会给出错误信息。这是因为 C 语言遵循“程序员所做的都是对的”的设计哲学,编译器认为这是程序员的意图。这种特性使得在使用 Interpositioning 时,错误很难被及时发现。程序员可能在不经意间使用了这种技术,却没有意识到自己已经覆盖了库函数,从而导致程序出现难以察觉的错误。

由于 Interpositioning 具有较高的风险,只有在确实需要进行调试或提高效率时才考虑使用。对于新手来说,应尽量避免使用这种技术,以免伤害自己。

5 产生链接器报告文件

可以在ld程序中使用“-m”选项,让链接器产生一个报告,里面包括了被Interpose

的符号的说明。通常,带“-m”选项的ld会产生一个内存映射或列表,显示在可执行文件中的什么地方放入了哪些符号。它同时显示了同一个符号的多个实例,通过查看报告的内容,用户可以判断是否发生了Interpositioning。假如你有一个待链接的目标文件叫 main.o,想生成包含符号相关信息的报告,命令可能如下:

ld -m main.o -o output_executable

ld程序中的“-D”选项是随SunOS5.3引入的,日的是提供更好的链接-编辑调试。这个选项允许用户显示链接-编辑过程和所包含的输入文件。如果需要监视从archive中提取对象的过程,这个选项尤其有用,同时可用于显示运行时绑定信息。同样针对 main.o 这个目标文件,想要查看链接编辑过程和涉及的输入文件,命令示例如下:

ld -D main.o -o another_executable

ld是一个复杂的程序,还有很多其他选项和约定未在此处说明。对于绝大多数应用来说,这些说明已经足够了。如需知道更多有关它的知识,下面提供了四条途径,按其复杂程度分列如下:

  • 使用1dd命令,列出可执行文件的动态依赖集。这条命令会告诉你动态链接的程序所需要的函数库。
  • ld程序的-Dhelp选项能提供一些信息,有助于查找链接过程中出现的问题。
  • 查看ld程序的在线文档。

 

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com