本地代码: 本地代码的内容就是数值的罗列集合(二进制的数据展示)
编译器: 负责将源代码转换为本地代码,每种语言都有其专用的编译器
由于cpu类型不同,本地代码的类型也不同,编译器可以将源代码编译成不同cpu类型的本地代码
交叉编译: 在当前平台编译另外平台的本地代码,例如在win上编译linux平台运行的本地代码
编译器转换源代码后就会生成本地文件,这个文件不能直接运行(处于未完成状态),还需要进行链接处理。
链接器: 将多个目标文件结合,生成最终的可执行程序,这个处理过程称为链接
需要链接的原因: 将源代码编译后只是对程序员书写的代码进行了编译,还需要将在源代码中进行引入的库文件,函数这些引入的功能进行编译整合在一起,整合后的文件才是完整的执行程序,因此需要进行链接操作。当前的语言通常在编译的同时进行了链接,因此只需要编译后就可以得到可执行程序。
库文件: 把多个目标文件集成保存到一个文件中的形式,链接器指定库文件后会将需要用到的库文件函数提取出来与其它目标文件结合生成可执行程序
标准函数: 不是通过源码形式,而是通过库文件和编译器一起提供的。这样的函数称为标准函数,如语言自带的标准库函数
动态链接库DLL及导入库: 程序运行时动态结合,不存在库文件的静态代码(只存在DLL文件目标路径信息),运行时才导入库文件的相关功能,因此也成导入库
静态链接库: 存储着目标文件的实体,并直接与可执行程序结合的库文件形式
可执行程序运行的必要条件: 需要程序内函数与变量的内存地址
虚拟内存地址: 在可执行程序加载到内存执行时,由于每次程序运行时,系统分配给程序内部的变量与函数的内存地址都不相同,因此为了避免这个问题,系统给程序内的变量与函数分配的为虚拟内存地址,
再配置信息: 在程序运行时,虚拟内存地址会转换成实际的内存地址进行运行,链接器会在可执行程序文件的开头,追加转换内存地址所需要的必要信息,称为再配置信息,该再配置信息为变量和函数的相对地址(相对基址的偏移量),通过基址和相对地址就可知道当前的实际内存地址。
当程序加载到内存后,会分配生成堆、栈内存空间来进行运行时的数据保存
堆: 用来存储程序运行时的任意数据及对象的内存领域(全局变量)
栈: 用来存储函数内部临时使用的变量(局部变量),已经函数调用时所用的参数内存领域
内存中的程序构成: 由用于变量的内存空间、用于函数的内存空间(这两部分用于复制可执行程序到内存)、用于栈的内存空间、用于堆的内存空间(程序运行时申请分配用于存储运行时数据)四部分构成
程序运行时所需的内存分配: 程序运行时所需的内存空间分为 固定部分,和可变部分
固定部分的内存消耗 是不会随着代码运行产生变化的, 可变部分则是会产生变化的
更具体一些,一个由C/C++编译的程序占用的内存分为以下几个部分:
栈区(Stack) :由编译器自动分配释放,存放函数的参数值,局部变量的值等,其操作方式类似于数据结构中的栈。
堆区(Heap) :一般由程序员分配释放,若程序员不释放,程序结束时可能由OS收回
未初始化数据区(Uninitialized Data): 存放未初始化的全局变量和静态变量
初始化数据区(Initialized Data):存放已经初始化的全局变量和静态变量
程序代码区(Text):存放函数体的二进制代码
代码区和数据区所占空间都是固定的,而且占用的空间非常小,那么看运行时消耗的内存主要看可变部分
在可变部分中,栈区间的数据在代码块执行结束之后,系统会自动回收,而堆区间数据是需要程序员自己回收
总结:
使用栈的数据内存空间,每当函数调用时都会进行申请分配,并在函数处理完毕后自动释放(编译器管理)
使用堆的内存空,需要根据程序员编写的程序明确进行分配释放(程序员自己管理)
内存泄露: 申请的堆空间没有在程序后明确指定释放,那么即使处理完毕后该内存空间任然会一直残留,这个现象称为内存泄露,如果一直泄露就会导致内存不足,内存溢出,程序崩溃(当前除了c,c++等,其它的高级语言很多拥有自动gc垃圾回收机制,解放程序员手动去释放堆内存)
解释器编译器的区别:
1.编译器是在运行前对所有的源代码进行解释处理
2.解释器是在运行时对源代码内容进行一行一行的解释处理