Java教程

《程序是怎样跑起来的》--第5章至第8章学习笔记

本文主要是介绍《程序是怎样跑起来的》--第5章至第8章学习笔记,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

第5章:内存和磁盘的亲密关系

游戏中的Loading就是把游戏资源复制到内存
盒式卡带的情况下,可以将游戏机主存的内存完全置换,所以不需要往内存中复制数据

问题:

1:存储程序方式指的是什么

在存储装置中保存程序,并逐一运行

2:通过使用内存来提高磁盘访问速度的机制称为什么

Disk Cache(磁盘缓存)
磁盘缓存是指,把从磁盘中读取的数据存储在内存中,当该数据再次被读取时,不是从磁盘而是直接从内存中高速读出

3:把磁盘的一部分作为假想内存来使用的机制是什么

虚拟内存
借助虚拟内存,哪怕是内存容量不足的计算机,也可以运行很大的程序

4:windows中,在程序运行时,存储着可以动态加载调用的函数和数据的文件称为什么

DLL(Dynamic Link Library)

5:在EXE程序文件中,静态加载函数的方式称为什么?

静态链接
函数的加载方式有动态链接和静态链接

6:在windows计算机中,一般磁盘一个扇区是多少字节

512字节
扇区是磁盘保存数据的物理单位

本章重点

计算机的5大部件:输入装置、输出装置、存储器、运算器、控制器
内存和磁盘的异同点
都用来存储程序命令和数据
内存:通过电流实现 高速高价小容量
磁盘:通过磁效应实现 低速廉价大容量
内存主要指主内存(负责存储CPU中运行的程序指令和数据的内存)
磁盘:硬盘

5.1 不读入内存就无法运行

程序保存在存储设备中,通过有序地被读出来实现运行。这一机制称为存储程序方式(程序内置方式)在此以前的程序都是通过改变计算机的布线等来变更程序的。

计算机中主要的存储部件是内存和磁盘。磁盘中存储的程序,必须要加载到内存后才能运行。在磁盘中保存的原始程序是无法直接运行的。这是因为,负责解析和运行程序内容的CPU,需要通过内部程序计数器来指定内存地址,然后才能读出程序”。即使CPU可以直接读出并运行磁盘中保存的程序,由于磁盘读取速度慢,程序的运行速度还是会降低。

在这里插入图片描述

5.2 磁盘缓存加快了磁盘访问速度

磁盘缓存指的是把从磁盘中读出的数据存储到内存空间中的方式。这样一来,当接下来需要读取同一数据时,就不用通过实际的磁盘,而是从磁盘缓存中把内容读出。使用磁盘缓存可以大大改善磁盘数据的访问速度

在这里插入图片描述

缓存把低速设备的数据保存在高速设备中,需要时可以直接将其从高速设备中读出

例:Web浏览器通过网络来获取远程Web 服务器的数据并将其显示出来。因此,在显示较大的图片等文件时,会花费不少时间。于是,Web浏览器就可以把获取的数据暂时保存在磁盘中,然后在需要时再显示磁盘中的数据。也就是说,把低速的网络数据保存到相对高速的磁盘中。

5.3 虚拟内存把磁盘作为部分内存来使用

虚拟内存是假想的内存,实际上是磁盘
通过借助虚拟内存,在内存不足时也可以运行程序。例如,在只剩下5MB内存空间的情况下也能运行10MB大小的程序。不过,CPU只能执行加载到内存中的程序。虚拟内存虽说是把磁盘作为内存的一部分来使用,但实际上正在运行的程序部分,在这个时间点上是必须存在在内存中的。也就是说,为了实现虚拟内存,就必须把实际内存(也可称为物理内存)的内容,和磁盘上的虚拟内存的内容进行部分置换( swap),并同时运行程序

Windows提供了虚拟内存机制作为操作系统。在当前的Windows 中,虚拟内存依然发挥着很大的作用。虚拟内存的方法有分页式分段式两种。Windows采用的是分页式。该方式是指,在不考虑程序构造的情况下,把运行的程序按照一定大小的页( page )进行分割,并以页为单位在内存和磁盘间进行置换。在分页式中,我们把磁盘的内容读出到内存称为Page In,把内存的内容写入磁盘称为Page Out。一般情况下, Windows计算机的页的大小是4KB。也就是说,把大程序用4KB的页来进行切分,并以页为单位放人磁盘(虚拟内存)或内存中。

在这里插入图片描述

分段式虚拟内存是指,把要运行的程序分割成以处理集合及数据集合等为单位的段落,然后再以分割后的段落为单位在内存和磁盘之间进行数据置换

为了实现虚拟内存功能,Windows在磁盘上提供了虚拟内存用的文件( page file,页文件)。该文件由 Windows自动做成和管理。文件的大小也就是虚拟内存的大小,通常是实际内存的相同程度至两倍程度。通过 Windows的控制面板,可以查看或变更当前虚拟内存的设定。

Windows查看虚拟内存的方法

在命令行输入sysdm.cpl

在这里插入图片描述

因为我的内存是8G,所以,这里虚拟内存也是8G
在这里插入图片描述

5.4 节约内存的编程方法

最初的windows可以在128kb内存上运行,但随着windows体量加大,在windows系统可以运行多个应用,至少需要512M内存,有时候就需要使用虚拟内存,但是虚拟内存发生的PageIn和PageOut往往伴随着低速的磁盘访问,应用运行较慢

硬盘访问灯一直亮着就是在PageIn和PageOut

然而,虚拟内存无法彻底解决内存不足的问题(为了解决内存不足问题,可以增大内存容量(花费较高),或者尽量把运行的应用文件变小)

GUI:图形用户界面
CLI:命令行界面

把运行的应用文件变小的编程方法

  • 1)通过DLL文件实现函数共有

DLL( Dynamic Link Library )文件,是在程序运行时可以动态加载Library(函数和数据的集合)的文件。那就是多个应用可以共有同一个DLL文件。而通过共有同一个DLL文件则可以达到节约内存的效果。假设我们编写了一个具有某些处理功能的函数MyFunc()。应用A和应用B都会使用这个函数。在各个应用的运行文件中内置函数MyFunc()(这个称为Static Link,静态链接)后同时运行这两个应用,内存中就存在了具有同一函数的两个程序。但这会导致内存的利用效率降低。

在这里插入图片描述

那么,如果函数MyFunc(是独立的 DLL文件而不是应用的执行文件(EXE文件“),那结果会怎样呢?由于同一个DLL文件的内容在运行时可以被多个应用共有,因此内存中存在的函数 MyFunc()的程序就只有1个,内存的利用效率就提高了
在这里插入图片描述

Windows的操作系统本身也是多个DLL文件的集合体。有时在安装新应用时,DLL文件也会被追加。应用则会通过利用这些DLL文件的功能来运行。像这样,之所以要利用多个DLL文件,其中一个原因就是可以节约内存。而且 DLL文件还有一个优点就是,在不变更EXE文件的情况下,只通过升级 DLL文件就可以更新。

  • 1)通过_stdcall来减小程序文件的大小

stdcall是standard call(标准调用)的略称。Windwos提供的 DLL文件内的函数,基本上都是_stdcall调用方式。这主要是为了节约内存。另一方面,用C语言编写的程序内的函数,默认设置都不是_stdcall。C语言特有的调用方式称为C调用。C语言之所以默认不使用_stdcall,是因为C语言所对应的函数的传入参数是可变的(可以设定任意参数),只有函数调用方才能知道到底有多少个参数,而这种情况下,栈的清理作业便无法进行。不过,在C语言中,如果函数的参数数量固定的话,指定_stdcall是没有任何问题的。

C语言中,在调用函数后,需要执行栈清理处理指令。栈清理处理是指,把不需要的数据从接收和传递函数的参数时使用的内存上的栈区域中清理出去。该命令不是程序记述的,而是在程序编译时由编译器自动附加到程序中的。编译器默认将该处理附加在函数调用方

例如,下面的代码中,从函数main()中调用了函数MyFunc()。按照默认设定,栈的清理处理会附加在函数main()这一方。在同一个程序中,同样的函数可能会被多次反复调用。而如果是同样的函数,栈清理处理的内容也是一样的。由于该处理是在调用函数一方,因此就会导致同一处理被反复进行。这就造成了内存的浪费。

//函数调用方
void main(){
int a;
a = MyFunc (123,456);
}
//被调用方
int MyFunc(int a, int b)
{}

在C语言中,函数的返回值是通过寄存器而非栈来完成的

栈清理处理,比起在函数调用方进行,在反复被调用的函数一方进行时,程序整体要小一些。这时所使用的就是_stdcall。在函数前加上_stdcall,就可以把栈清理处理变为在被调用函数一方进行。

在这里插入图片描述

5.5 磁盘的物理结构

磁盘是通过把其物理表面划分成多个空间来使用的。划分的方式有扇区方式可变长方式两种,前者是指将磁盘划分为固定长度的空间,后者则是指把磁盘划分为长度可变的空间。一般的 Windows计算机所使用的硬盘和软盘,采用的都是扇区方式。扇区方式中,把磁盘表面分成若干个同心圆的空间就是磁道,把磁道按照固定大小(能存储的数据长度相同)划分而成的空间就是扇区
在这里插入图片描述
扇区是对磁盘进行物理读写的最小单位。Windows中使用的磁盘,一般1个扇区是512字节。不过,Windows在逻辑方面(软件方面)对磁盘进行读写的单位是扇区整数倍簇。根据磁盘容量的不同,1簇可以是512字节(1簇=1扇区)、1KB(1簇=2扇区)、2KB、4KB、8KB、16KB、32KB(1簇= 64扇区)。磁盘的容量越大,簇的容量也越大。不过,在软盘中,1簇=512字节=1扇区,簇和扇区的大小是相等的。
不管是硬盘还是软盘,不同的文件是不能存储在同一个簇中的,否则就会导致只有一方的文件不能被删除。因此,不管是多么小的文件,都会占用1簇的空间这样一来,所有的文件都会占用1簇的整数倍的磁盘空间

以簇为单位进行读写时,1簇中没有填满的区域会保持不被使用的状态。虽然这看起来是有点浪费,不过该机制就是如此规定的,所以我们也没有什么好办法。另外,如果减少簇的容量,磁盘访问次数就会增加,就会导致读写文件的时间变长。由于在磁盘表面上,表示扇区区分的领域是必要的,因此,如果簇的容量过小,磁盘的整体容量也会减少。扇区和簇的大小,是由处理速度和存储容量的平衡来决定的。

第6章 亲自尝试压缩数据

问题

1.文件储存的基本单位是什么?

字节,文件是字节数据的集合体

2.DOC、LZH 和 TXT这些扩展名中,哪一个是压缩文件的扩展名?

LZH是LHA等工具压缩过的文件的扩展名

3.文件内容用“数据的值×循环次数”来表示的压缩方法是RLE算法还是哈夫曼算法?

RLE算法,比如AAABB变成A3B2

4.在 Windows计算机经常使用的 SHIFT JIS字符编码中,1个半角英数是用几个字节的数据来表示的?

1个字节,汉字等全角字符是用2个字节表示的

5.BMP( BITMAP)格式的图像文件,是压缩过的吗?

BMP没有压缩过,比JPEG等压缩过的要大一些

6.可逆压缩和非可逆压缩的不同点是什么?

压缩后的数据能复原的是可逆压缩,无法复原的是非可逆压缩 JPEG就是非可逆压缩

本章重点

文件太大可以考虑使用压缩

6.1 文件以字节为单位保存

文件是将数据存储在磁盘等存储媒介中的一种形式。程序文件中存储数据的单位是字节。文件的大小之所以用××KB、××MB等来表示,就是因为文件是以字节(B = Byte)为单位来存储的。

文件就是字节数据的集合。用1字节(=8位)表示的字节数据有256种,用二进制数来表示的话,其范围就是00000000~11111111。如果文件中存储的数据是文字,那么该文件就是文本文件。如果是图形,那么该文件就是图像文件。在任何情况下,文件中的字节数据都是连续存储的

在这里插入图片描述

6.2 RLE算法的机制

半角字母中,1个字符是作为1个字节的数据被保存在文件中的。

全角—指一个字符占用两个标准字符位置。汉字字符和规定了全角的英文字符及国标GB2312-80中的图形符号和特殊字符都是全角字符。一般的系统命令是不用全角字符的,只是在作文字处理时才会使用全角字符。
半角—指一字符占用一个标准的字符位置。通常的英文字母、数字键、符号键都是半角的,半角的显示内码都是一个字节。在系统内部,以上三种字符是作为基本代码处理的,所以用户输入命令和参数时一般都使用半角。

把文件内容用“数据×重复次数”的形式来表示的压缩方法称为RLE ( Run Length Encoding,行程长度编码)算法。RLE算法是一种很好的压缩方法,经常被用于压缩传真的图像等。因为图像文件本质上也是字节数据的集合体,所以可以用RLE 算法来压缩

在这里插入图片描述

压缩了12/17=70%

6.3 RLE算法的缺点

对文本文件压缩效率低,甚至可能压缩完比原文本文件还大,当然如果改成按字符串压缩的话,效率还是能提升的,比如 this is a pen

压缩比率:压缩后桶压缩前文件大小的比率

6.4 通过莫尔斯编码来看哈夫曼算法的基础

哈夫曼算法的关键在于多次出现的数据用小于8位的字节数来表示,不常用的数据可以用超过8位的字节数来表示
在这里插入图片描述
在这里插入图片描述

6.5用二叉树实现哈夫曼编码

在这里插入图片描述

6.6 哈夫曼算法能够大幅提升压缩比率

使用哈夫曼树后,出现频率越高的数据所占用的数据位数就越少,而且数据的区分也可以很清晰地实现。

6.7可逆压缩与非可逆压缩

Bitmap是Windows自带的图像格式,没有任何压缩,因为显示器和打印机输出的bit(点)是可以直接映射的,所以叫bitmap

由于图像是不要求还原到和压缩前一样的内容,所以,常常采用不可逆压缩
而EXE以及每个字符、数值都有具体含义的文本文件则需要还原到和压缩前一样的内容,所以用可逆压缩

第7章 程序是在何种环境中运行的

问题

1.应用的运行环境,指的是什么?

操作系统和计算机本身(硬件)的种类,应用的运行环境通常是用类似于 Windows(OS)和AT兼容机(硬件)这样的OS和硬件的种类来表示的。

2 Macintosh 用的操作系统(MacOS ),在AT兼容机上能运行吗?

无法运行,不同的硬件种类需要不同的操作系统。

3 Windows上的应用,在MacOS上能运行吗?

无法运行,应用是为了在特定操作系统上运行而作成的。

4 FreeBSD提供的Ports,指的是什么?

通过使用源代码来提供应用,并根据运行环境进行整合编译,从而得以在该环境下运行的机制
FreeBSD是一种 Unix操作系统。通过在各个环境中编译Ports中公开的代码,就可以执行由此生成的本地代码了。

5.在Macintosh上可以利用的Windows环境模拟器称为什么?

Virtual PC for Mac,模拟器是指在 Macintosh 上提供虚拟的Windows环境。

6Java 虚拟机的功能是什么?

运行Java应用的字节代码
只要分别为各个环境安装专用的Java 虚拟机,同样的字节代码就能在各种环境下运行了。

本章重点

运行环境不同,程序是无法运行的,在MacOS不能之际额运行Windows应用程序

7.1运行环境=操作系统+硬件

在这里插入图片描述

同一类型的硬件可以选择安装多种操作系统
例如,同样的AT兼容机中,既可以安装 Windows,也可以安装 Linux’等操作系统。正因为如此,Office 2007的运行环境中,把硬件和操作系统的种类这两方面内容都列了出来(图 7-1)。
在这里插入图片描述

不过,Windows及 Linux操作系统也存在多种版本。根据应用的具体情况,有时只有在特定版本的操作系统上才能运行。
从程序的运行环境这一角度来考量硬件时,CPU的种类是特别重要的参数。为了保证Office 2007的正常运行,需要具备Pentium等被称为x862的CPU(微处理器)。

在这里插入图片描述

CPU 只能解释其自身固有的机器语言不同的CPU能解释的机器语言的种类也是不同的。例如,CPU有x86、MIPS、SPARC、PowerPC T等几种类型,它们各自的机器语言是完全不同的。

在这里插入图片描述

机器语言的程序称为本地代码( native code )。程序员用C语言等编写的程序,在编写阶段仅仅是文本文件。文本文件(排除文字编码的问题)在任何环境下都能显示和编辑。我们称之为源代码。通过对源代码进行编译,就可以得到本地代码。在市面上出售的用于 Windows的应用软件包CD-ROM 中,收录的就不是源代码,而是本地代码“(图7-2 )。

在这里插入图片描述

7.2 Windows克服了CPU以外的硬件差异

在这里插入图片描述
MS-DOS ( Microsoft Disk Operating System )是20世纪80年代普遍使用的计算机操作系统。

在Windows 的应用软件中,键盘输入、显示器输出等并不是直接向硬件发送指令,而是通过向Windows发送指令来间接实现的。因此,程序员就不用注意内存和IO地址的不同构成了。因为 Windows操作的是硬件而非应用软件,而且针对不同的机型,这些硬件的构成也是有差异的(图7-4 )。不过,Windows本身则需要为不同的机型分别提供专用的版本,比如用于AT兼容机的 Windows、用于PC-9081的Windows等。
在这里插入图片描述

而即便是Windows,也依然无法吸收CPU类型的差异。这是因为,市面上销售的Windows应用软件,都是用特定的CPU的本地代码来完成的。

7.3 不同操作系统的API不同

同样机型的计算机,可安装的操作系统类型也会有多种选择。例如,AT兼容机的情况下,除Windows之外.还可以采用Unix系列的Linux及 FreeBSD等多个操作系统。当然,应用软件则必须根据不同的操作系统类型来专门开发。CPU的类型不同,所对应的机器语言也不同,同样的道理,操作系统的类型不同,应用程序向操作系统传递指令的途径也是不同的

应用程序向操作系统传递指令的途径称为API ( Application Programming Interface )。Windows及 Unix系列操作系统的API,提供了任何应用程序都可以利用的函数组合。因为不同操作系统的API是有差异的,因此.将同样的应用程序移植到其他操作系统时,就必须要重写应用中利用到API的部分。像键盘输入、鼠标输入、显示器输出、文件输入输出等同外围设备进行输入输出操作的功能,都是通过API提供的。

在同类型操作系统下,不管硬件如何,API基本上没有差别。因而,针对某特定操作系统的API所编写的程序,在任何硬件上都可以运行。当然,由于CPU种类不同,机器语言也不相同,因此本地代码当然也是不同的。这种情况下,就需要利用能够生成各CPU专用的本地代码的编译器,来对源代码进行重新编译了。

所以程序(本地代码)的运行环境是由操作系统和硬件来决定的

FreeBSD 是1993年加州大学伯克利分校的Computer Systems Research Group在4.4BSD-Lite的基础上开发的Unix系列操作系统。
API也称为“系统调用”",是应用调用操作系统功能的手段

7.4 FreeBSD Port帮你轻松使用源代码

Unix系列操作系统 FreeBSD中,存在一种名为Ports的机制。该机制能够结合当前运行的硬件环境来编译应用的源代码,进而得到可以运行的本地代码系统。如果目标应用的源代码没有在硬件上的话,Ports就会自动使用FTP连接到相关站点来下载代码
在这里插入图片描述

全球很多站点都提供适用于FreeBSD的应用源代码。通过使用Ports可以利用的程序源代码,大约有16000种。
FreeBSD上应用的源代码,大部分都是用C语言来记述的。FreeBSD等Unix系列操作系统中,都带有标准的C编译器。C编译器可以结合FreeBSD的运行环境生成合适的本地代码。因而,使用FreeBSD的同时,肯定也会享受到Ports带来的益处。可以说Ports 能够克服包含CPU在内的所有硬件差异的系统。而且,Ports这个术语,表示的是porting(移植)的意思。而根据不同的运行环境来重新调整程序

7.5利用虚拟机获得其他操作系统环境

Virtual PC for MAC(虚拟机)可以使 Macintosh(MacOS)这一硬件变得同AT兼容机一样,从而能在该硬件上安装 Windows。这样一来,Windows下的所有应用就都可以正常运行了。Windows应用利用的是 Windows操作系统的API。虽然表面上是Windows将硬件处理为了AT兼容机,但由于Virtual PC for MAC的作用,实际上运行的是 Macintosh这一硬件。

在这里插入图片描述

7.6 提供相同运行环境的JAVA虚拟机

JAVA虚拟机能够提供不依赖于特定硬件和操作系统的程序运行环境

同其他编程语言相同,Java也是将Java语法记述的源代码编译后运行。不过,编译后生成的并不是特定CPU使用的本地代码,而是名为字节代码的程序字节代码的运行环境就称为Java虚拟机( JavaVM,Java Virtual Machine )。Java虚拟机是一边把Java字节代码逐一转换成本地代码一边运行的。
在这里插入图片描述
从操作系统层面看:JAVA虚拟机是一个应用
从Java应用方面看:JAVA虚拟机是运行环境

JAVA虚拟机的弊端

  • 不同的Java虚拟机之间无法进行完整互换。这是因为,想让所有字节代码在任意Java虚拟机上都能运行是比较困难的。而且,当我们使用只适用于某些特定硬件的功能时,就会出现在其他Java虚拟机上无法运行,或者功能使用受限等情况
  • 运行速度的问题。Java虚拟机每次运行时都要把字节代码变换成本机代码,这一机制是造成运行速度慢的原因。为此,目前业界也在努力改善这一问题,比如把首次变换后的本地代码保存起来,第2次以后直接利用本地代码,或是对字节代码中处理较为费时的部分进行优化(改善生成的本地代码质量)等。

7.7 BIOS和引导

程序的运行环境中,存在着名为BIOS( Basic Input/Output System)的系统。BIOS存储在ROM中,是预先内置在计算机主机内部的程序。BIOS除了键盘、磁盘、显卡等基本控制程序外,还有启动“引导程序”的功能。引导程序是存储在启动驱动器起始区域的小程序。操作系统的启动驱动器一般是硬盘,不过有时也可以是CD-ROM或软盘。
开机后,BIOS会确认硬件是否正常运行,没有问题的话就会启动引导程序。引导程序的功能是把在硬盘等记录的OS加载到内存中运行。虽然启动应用是OS的功能,但OS 并不能自己启动自己,而是通过引导程序来启动

RAM和ROM的区别
RAM(random access memory)即随机存储内存,这种存储器在断电时将丢失其存储内容,故主要用于存储短时间使用的程序。
ROM(Read-Only Memory)即只读内存,是一种只能读出事先所存数据的固态半导体存储器。

RAM:随机存取存储器(英语:Random Access Memory,缩写:RAM),也叫主存,是与CPU直接交换数据的内部存储器。
ROM:(只读内存(Read-Only Memory)简称)英文简称ROM。ROM所存数据,一般是装入整机前事先写好的,整机工作过程中只能读出,而不像随机存储器那样能快速地、方便地加以改写。
在电脑方面RAM即是亲们平时所说的运行内存,它的确是随时可读写的。那么对于电脑ROM来说,并不是指的就是硬盘,因为ROM是只可以读而不可以写的,但是这里的电脑硬盘却是可以写入并修改的
电脑ROM是用来做什么的呢?
答:存储一些系统相关信息和开机引导BIOS等等
————————————————
版权声明:本文为CSDN博主「7TribeZ」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wildand/article/details/91126833

关于RAM与ROM的区别与理解

应该是主板上的BIOS芯片
BIOS正常是无法修改的 只能读取
只有在特定的电压下才能被改变
也就是说在你升级主板BIOS时电压是很特殊的
https://www.zhihu.com/question/53514751

第8章 从源文件到可执行文件

问题

1.CPU可以解析和运行的程序形式称为什么代码?

本地代码(机器语言代码)(通过编译源代码得到本地代码。)

2.将多个目标文件结合生成EXE文件的工具称为什么?

链接器(通过编译和链接,得到EXE文件。)

3.扩展名为.obj的目标文件的内容,是源代码还是本地代码?

本地代码(通过对源文件进行编译,得到目标文件。例如,C语言中,将Samplel.c这个源文件编译后,就会得到Sample1.obj这个目标文件。目标文件的内容是本地代码。)

4.把多个目标文件收录在一起的文件称为什么?

库文件,链接器会从库文件中抽取出必要的目标文件并将其结合到EXE
文件中。此外,还存在一种程序运行时结合的DLL形式的库文件。

5.仅包含Windows 的 DLL文件中存储的函数信息的文件称为
什么?

导入库(把导入库信息结合到EXE文件中,这样程序在运行时就可以利
用DLL内的函数了。)

6.在程序运行时,用来动态申请分配的数据和对象的内存区域
形式称为什么?

堆(堆的内存空间会根据程序的命令进行申请及释放。)

本章重点

  • 源代码完成后,就可以编译生成可执行文件,负责实现该功能的是编译器。
  • 从程序编写到运行为止的流程
  • 源文件是如何通过编译转换成可执行文件的
  • 可执行文件被加载到内存后的运行机制
  • 程序运行时内存上的栈及堆

8.1 计算机只能运行本地代码

我们用编程语言编写的源代码只是文本文件,不能直接运行,这是因为CPU能直接解析并运行的不是源代码而是本地代码

本地(native )这个术语有“母语的”意思。对CPU来说,母语就是机器语言而转换成机器语言的程序就是本地代码。用任何编程语言编写的源代码,最后都要翻译成本地代码,否则CPU就不能理解。也就是说,即使是用不同编程语言编写的代码,转换成本地代码后,也都变成用同一种语言(机器语言)来表示了
在这里插入图片描述

8.2 本地代码的内容

Dump是指把文件的内容,每个字节用2位十六进制数来表示的方式。本地代码的内容就是各种数值的罗列,每个数值都表示某一个命令或数据。计算机就是把所有的信息作为数值的集合来处理的。例如,A这个字符数据就是用十六进制数41来表示的。与此相同,计算机指令也是数值的罗列。这就是本地代码。
在这里插入图片描述

8.3 编译器负责转换源代码

能够把C语言等高级编程语言编写的源代码转换成本地代码的程序称为编译器。每个编写源代码的编程语言都需要其专用的编译器。将C语言编写的源代码转换成本地代码的编译器称为C编译器

编译器首先读入代码的内容,然后再把源代码转换成本地代码。编译器中就好像有一个源代码同本地代码的对应表。但实际上,仅仅靠对应表是无法生成本地代码的。读入的源代码还要经过语法解析、句法解析、语义解析等,才能生成本地代码。

根据CPU类型的不同,本地代码的类型也不同。因而,编译器不仅和编程语言的种类有关,和CPU的类型也是相关的。例如,Pentium等x86系列CPU用的C编译器,同PowerPC这种CPU用的C编译器就不同。从另一个方面来看,这其实是非常方便的。因为这样一来,同样的源代码就可以翻译成适用于不同CPU的本地代码了
在这里插入图片描述

8.4 仅靠编译是无法得到可执行文件的

编译器转换源代码后,就会生成本地文件。不过,本地文件是无法直接运行的。为了得到可以运行的EXE文件,编译之后还需要进行链接处理

编译后生成的不是EXE文件,而是扩展名为“.obj”的目标文件“。Sample1.c编译后,就生成了Samplel.obj目标文件。虽然目标文件的内容是本地代码,但却无法直接运行。那么这是为什么呢?原因就是当前程序还处于未完成状态

1)围起来的函数Average(同(2)围起来的函数WinMain(是程序员自己作成的,处理内容记述在源代码中。Average()是用来返回两个参数数值的平均值的函数,Winmain(是程序的运行起始函数。除此之外,还有(3)指出的sprintf()函数和(4)指出的 MessageBox()函数。sprintf(是通过指定格式把数值变换成字符串的函数,MessageBox()是消息框函数,不过源代码中都没有记述这些函数的处理内容因此,这时就必须将存储着sprintf(和 MessageBox()的处理内容的目标文件同Sample1.obj结合,否则处理就不完整,EXE文件也就无法完成

把多个目标文件结合,生成1个EXE文件的处理就是链接,运行连接的程序就称为链接器( linkage editor 或连结器)。Borland C++的链接器就是ilink32.exe的命令行工具。在 Windows命令提示符下运行以下命令后,程序所需的目标文件就会被全部链接生成Samplel.exe这个EXE文件。

8.5 启动及库文件

在这里插入图片描述

Samplel.obj是 Samplel.c编译后得到的目标文件。cOw32.obj这个目标文件记述的是同所有程序起始位置相结合的处理内容,称为程序的启动。因而,即使程序不调用其他目标文件的函数,也必须要进行链接,并和启动结合起来。cOw32.obj是由 Borland C++提供的。如果C:盘中安装有Borland C++的话,文件夹C:Borland\bcc55lib中就会有cOw32.obj这个文件。

那么,大家可能会有这样一个疑问:“链接时不指定sprintf(和MessageBox()的目标文件也没问题么?”这个担心是多余的。在链接的命令行末尾,存在着扩展名是“.lib”的 import32.lib和 cw32.lib这两个文件。这是因为sprintf()的目标文件在cw32.lib 中,MessageBox()的目标文件在 import32.lib中(实际上,MessageBox()的目标文件在user32.dll这个DLL文件中。关于这一点,我们会在后面进行说明)。
像import32.lib 及cw32.lib这样的文件称为库文件库文件指的是把多个目标文件集成保存到一个文件中的形式。链接器指定库文件后,就会从中把需要的目标文件抽取出来,并同其他目标文件结合生成EXE文件。

Samplel.obj是尚未完成的本地代码,=这是因为,Sample1.obj文件中包含有“链接时请结合sprintf(及MessageBox()”这样的信息。意思是如果不存在其他函数的话,程序就无法运行。

错误消息==“无法解析的外部符号”==表示的是无法找到记述着目的变量及函数的目标文件,因而无法进行链接的意思。

sprintf(等函数,不是通过源代码形式而是通过库文件形式和编译器一起提供的。这样的函数称为标准函数。之所以使用库文件,是为了简化为链接器的参数指定多个目标文件这一过程。例如,在链接调用了数百个标准函数的程序时,就要在链接器的命令行中指定数百个目标文件,这样就太繁琐了。而利用存储着多个目标文件的库文件的话,则只需在链接器的命令行中指定几个库文件就可以了。
通过以目标文件的形式或集合多个目标文件的库文件形式来提供函数,就可以不用公开标准函数的源代码内容。由于标准函数的源代码是编译器厂商的贵重财产,因此若被其他公司任意转用的话,可能会造成一些损失。

8.6DLL文件及导入库

Windows 以函数的形式为应用提供了各种功能。这些形式的函数称为API ( Application Programming Interface,应用程序接口)。例如,Sample1.c中调用的MessageBox(),它并不是C语言的标准函数,而是Windows提供的API的一种。MessageBox()提供了显示消息框的功能。

Windows中,API的目标文件,并不是存储在通常的库文件中,而是存储在名为DLL( Dynamic Link Library )文件的特殊库文件中。就如Dynamic这一名称所表示的那样,DLL文件是程序运行时动态结合的文件。在前面的介绍中,我们提到MessageBox()的目标文件是存储在import32.lib中的。实际上,import32.lib中仅仅存储着两个信息,一是MessageBox()在 user32.dll这个DLL文件中,另一个是存储着DLL文件的文件夹信息,MessageBox()的目标文件的实体实际上并不存在。我们把类似于import32.lib这样的库文件称为导入库

与此相反,存储着目标文件的实体,并直接和EXE文件结合的库文件形式称为静态链接库。静态( static=静态的)同动态(dynamic =动态的)是相反的意思。存储着sprintf()的目标文件的cw32lib就是静态链接库。sprintf(提供了通过指定格式把数值转换成字符串的功能。

通过结合导入库文件,执行时从DLL文件中调出的 MessageBox(函数这一信息就会和EXE文件进行结合。这样,链接器链接时就不会再出现错误消息,从而就可以顺利编写EXE文件。
在这里插入图片描述

8.7 可执行文件运行时的必要条件

每次运行时,程序内的变量及函数被分配到的内存地址都是不同的。
在 EXE文件中,变量和函数的内存地址的值,是如何来表示的呢?
EXE文件中给变量及函数分配了虚拟的内存地址。在程序运行时,虚拟的内存地址会转换成实际的内存地址。链接器会在EXE文件的开头,追加转换内存地址所需的必要信息。这个信息称为再配置信息
EXE文件的再配置信息,就成为了变量和函数的相对地址。相对地址表示的是相对于基点地址的偏移量,也就是相对距离。实现相对地址,也是需要花费一番心思的。在源代码中,虽然变量及函数是在不同位置分散记述的,但在链接后的EXE文件中,变量及函数就会变成一个连续排列的组。这样一来,各变量的内存地址就可以用相对于变量组起始位置这一基点的偏移量来表示,同样,各函数的内存地址也可以用相对于函数组起始位置这一基点的偏移量来表示。而各组基点的内存地址则是在程序运行时被分配的
在这里插入图片描述

8.8 程序加载时会生成栈和堆

EXE文件的内容分为再配置信息、变量组和函数组。当程序加载到内存后,除此之外还会额外生成两个组,那就是栈和堆。栈是用来存储函数内部临时使用的变量(局部变量),以及函数调用时所用的参数的内存区域。堆是用来存储程序运行时的任意数据及对象的内存领域

EXE文件中并不存在栈及堆的组。栈和堆需要的内存空间是在EXE文件加载到内存后开始运行时得到分配的。因而,内存中的程序、就是由用于变量的内存空间用于函数的内存空间用于栈的内存空间用于堆的内存空间这4部分构成的。当然,在内存中,加载Windows等操作系统的内存空间又是另外一回事了
在这里插入图片描述

根据编程语言的不同,对堆用的内存空间进行申请分配和释放的程序的编写方法也是多种多样的。C语言中是通过malloc()函数来进行申请分配、通过free()函数来释放的。而C++中则是通过new运算符来申请分配、通过delete运算符来释放的。无论是C语言还是C++,如果没有在程序中明确释放堆的内存空间,那么即使在处理完毕后,该内存空间仍会一直残留。这个现象称为内存泄露( memory leak ),它是令C语言及C++的程序员们十分头疼的一个 bug(程序的错误)。如果内存泄露一直存在的话,就有可能会造成内存不足而导致宕机。这就好比,如果水龙头一直嘀嗒嘀嗒地漏水,那么一晚上的时间水桶就可能会装满并溢出。

8.9 有点难度的Q&A

Q:编译器和解释器有什么不同?

A:编译器是在运行前对所有源代码进行解释处理的。而解释器则是在运行时对源代码的内容一行一行地进行解释处理的。

Q:“分割编译”指的是什么?

A:将整个程序分为多个源代码来编写,然后分别进行编译,最后链接成一个EXE文件。这样每个源代码都相对变短,便于程序管理。

Q:"Build”指的是什么?

A:根据开发工具种类的不同,有的编译器可以通过选择“Build"菜单来生成EXE文件。这种情况下,Build指的是连续执行编译和链接。

Q:使用DLL文件的好处是什么?

A :DLL文件中的函数可以被多个程序共用。因此,借助该功能可以节约内存和磁盘。此外,在对函数的内容进行修正时,还不需要重新链接(静态链接)使用这个函数的程序。

Q:不链接导入库的话就无法调用DLL文件中的函数吗?

A:通过使用LoadLibraryO及GetProcAddress(这些API,即使不链接导入库,也可以在程序运行时调用DLL文件中的函数。不过使用导入库更简单一些。

Q:“叠加链接”这个术语指的是什么?
A:将不会同时执行的函数,交替加载到同一个地址中运行。通过使用“叠加链接器”这一特殊的链接器即可实现。在计算机中配置的内存容量不多的MS-DOS 时代,经常使用叠加链接。

Q:和内存管理相关的“垃圾回收机制”指的是什么呢?

A:垃圾回收机制( garbage collection)指的是对处理完毕后不再需要的堆内存空间的数据和对象“进行清理,释放它们所使用的内存空间。这里把不需要的数据比喻为了垃圾。进行该处理时,C语言用的是free()函数,C++用的是delete运算符。在C++的基础上开发出来的Java及C#这些编程语言中,程序运行环境会自动进行垃圾回收。这样就可以避免由于程序员的疏忽(忘了记述内存的释放处理)而造成内存泄露了。

这篇关于《程序是怎样跑起来的》--第5章至第8章学习笔记的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!