C程序设计语言(第二版)_第1页
C程序设计语言(第二版)_第2页
C程序设计语言(第二版)_第3页
C程序设计语言(第二版)_第4页
C程序设计语言(第二版)_第5页
已阅读5页,还剩6页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第一章 基本概念第二章 类型、运算符与表达式一个对象的类型决定着该对象可取值的集合以及可以对该对象施行的运算。2.2 数据类型与大小1. 在C语言中只有如下几个基本数据类型:char 单字节,可以存放字符集中一个字符。int 整数,一般反映了宿主机上整数的自然大小。Float 单精度浮点数。Double 双精度浮点数。此外,还有一些可用于限定这些基本类型的限定符。2.3 常量1诸如1234一类的整数常量是int常量。Long常量要以字母l或L结尾。无符号常量以字母u或U结尾,后缀ul或UL用于表示unsigned long常量。(这里的常量其实就是指直接指定的一般数字或是字符字符串什么的)浮点

2、常量必须包含一个小数点或指数(如1e-1)或两者都包含,在没有后缀时类型为double。后缀f与F用于指定float常量,而后缀l或L则用于指定字符常量是一个整数,写成用单引号括住单个字符的形式,如x。字符常量的值是该字符在机器字符集中的数值。常量表达式时其中只涉及到常量的表达式。这种表达式可以在编译时计算而不必推迟到运算时,因而可以用在常量可以出现的任何位置,例如由define定义的宏。字符串常量也叫字符串字面值,是用双引号括住的由0个或多个字符组成的字符序列。从技术绝度看,字符串常量就是字符数组。在内部表示字符串时要用一个空字符0来结尾,故用于存储字符串的物理存储单元数比括在双引号中的字符

3、数多一个。这种表示发意味着,C语言对字符串的长度没有限制,但是程序必须扫描完整个字符串才能决定这个字符串的长度。枚举常量。枚举是常量整数值的列表。不同的枚举中的名字必须各不相同,同一枚举中各个名字的值不要求不同。枚举是使常量值与名字相关联的又一种方便的方法,其相对于#define语句的优势是常量值可以由自己控制。2.4 说明其实就是声明。如果所涉及的变量不是自动变量(就是局部变量),那么只初始化一次,而且从概念上讲应该在程序开始执行之前进行,此时要求初始化符必须为常量表达式。显示初始化的自动变量每当进入其所在的函数或分程序时就进行一次初始化,其初始化符可以是任何表达式。外部变量与静态变量的缺省

4、值为0。未经显式初始化的自动变量的值为未定义值(即垃圾)。2.5 算术运算符2.6 关系运算符与逻辑运算符2.7 类型转换1. 当一个运算符的几个运算分量的类型不同时,要根据一些规则把它们转换成某个共同的类型。一般而言,只能把“比较窄的”运算分量自动转换成“比较宽的”运算分量,这样才能不丢失信息。2. char类型就是小整数类型,在算术表达式中可以自由地使用char类型的变量或常量。这就使得在某些字符转换中有了很大的灵活性。但是在将字符转换成整数时有一点微妙。C语言没有指定char类型变量时无符号还是有符号量。当把一个char类型的值转换成int类型的值时,其结果是不是负整数?结果视机器的不同

5、而有所变化,反映了不同机器结构之间的区别。在某些机器上,如果字符的最左一位为1,那么就被转换成负整数。在另一些机器上,采取的是提升的方法,通过在最左边加上0把字符提升为整数,这样的转换结果总是正的。C语言的定义保证了机器的标准可打印字符集中的字符不会是负的,故在表达式中这些字符总是正的。但是,字符变量存储的位模式在某些机器上可能是负的,而在另一些机器上却是正的。为了保证程序的可移植性,如果要在char变量中存储非字符数据,那么最好指定signed或unsigned限定符。3. 当表达式中包含unsigned类型的运算分量时,转换规则要复杂一些。主要问题是,在有符号与无符号值之间的比较运算取决于

6、机器,因为它们取决于各个整数类型的大小。例如,假定int对象占16位,long对象占32位,那么,-1L<1U,这是因为int类型的-1U被提升为signed long类型;但是-1L>1UL,这是因为-1L被提升为unsinged long类型,因此它是一个比较大的正数。4. 在进行赋值时要进行类型转换,=右边的值要转换成左边变量的类型,后者即赋值表达式结果的类型。不管是否要进行符号扩展,字符值都要转换成整数值。当把较长的整型数转换成较短的整型数或字符时,要把超出的高位部分截掉。5. 由于函数调用的变元是表达式,当把变元传递给函数时也可能引起类型转换。在没有函数原型的情况下,ch

7、ar与short类型转换为int类型,float转换为double型,这就是即使在函数使用char与float类型的变元表达式调用时仍把参数说明成int与float的原因。2.8 加一与减一运算符2.9 按位运算符2.10 赋值运算符与赋值表达式2.11 条件表达式2.12 运算符优先级与表达式求值次序同一行的各个运算符具有相同的优先级,纵向看越往下优先级越低。运算符结合律() ->从左至右! + - + - * & (类型)sizeof从右至左* / %从左至右+ -从左至右<< >> 从左至右< <= > >=从左至右= !=从

8、左至右&从左至右从左至右|从左至右&&从左至右|从左至右?:从右至左= += -= *= /= %= &= = |= <<= >>=从右至左.从左至右第三章 控制流第四章 函数与程序结构4.1 函数的基本知识1. 程序是变量定义和函数定义的结合。函数之间的通信可以通过变元、函数返回值以及外部变量进行。函数可以以任意次序出现在原文件中。源程序可以分成多个文件,只要不把一个函数分在几个文件中就行。4.3 外部变量1C程序由一组外部对象(外部变量或函数)组成。外部变量在函数外面定义,故可以在很多函数中使用。由于C语言不允许在一个函数中定义其他函

9、数,因此函数本身是外部的。在缺省情况下,外部变量与函数具有如下性质:所有通过名字对外部变量与函数的引用都是引用的同一对象。4.4 作用域规则4.5 头文件4.6 静态变量1. static说明适用于外部变量与函数,用于把这些对象的作用域限定为被编译源文件的剩余部分。2. 外部static说明最常用于说明变量,当然它也可以用于说明函数。通常情况下,函数名字是全局的,在整个程序的各个部分都可见。然而,如果把一个函数说明称静态的,那么该函数名字就不能用在除该函数说明所在的文件之外的其他文件中。Static说明也可用于说明内部变量。内部静态变量就像自动变量一样局部于某一特定函数,只能在该函数中使用,但

10、与自动变量不同的是,不管其所在函数是否被调用,它都一直是存在的,而不像自动变量那样,随着所在函数的调用与退出而存在与消失。4.7 寄存器变量Register说明用于提醒编译程序所说明的变量在程序中使用频率较高。其思想是,将寄存器变量放在机器的寄存器中,这样可以是程序更小、执行速度更快。但编译器可以忽略此选项。寄存器说明只适用于自动变量以及函数的形式参数。所有寄存器变量的地址都是不能访问的。4.9 初始化1 在没有显示初始化的情况下,外部变量与静态变量都被初始化为0,而自动变量与寄存器变量的初值则没有定义(即,其初值是“垃圾”)。4.10 递归4.11 C预处理程序第五章 指针与数组5.1 指针

11、与地址1.取地址运算符&只能应用于内存中的对象(即变量与数组元素),它不能对表达式、常量或寄存器变量进行操作。5.3 指针与数组1.数组下标所能完成的任何运算都可以用指针来实现。一般而言,指针运算比数组下标运算的速度快。在对数组进行下标运算,即求ai的值时,C语言实际上是先将其转化成 *(a+i)的形式然后再进行求值,因而在程序中这两种形式等价。2.必须注意到,数组名字和指针之间仍然存在着一点区别。指针是变量,因而在C语言中,语句pa=a和pa+都是合法的。但是数组名字不是变量,因而诸如a=pa(这个是指对整个数组从一个到另一个的整体赋值)和a+(这是指对数组名执行自加运算,其实可以运

12、用a+i的形式,不知为啥a+不行)这样的语句是非法的。3.当把一个数组名字传递给一个函数时,实际上传递的是该数组第一个元素的位置。也可以通过传递指向子数组的指针的方法把数组的一部分作为参数传递给函数。例如f(&a2)。5.4 地址算术运算1.有效的指针运算包括:相同类型指针之间的赋值运算;指针值加或减一个整数值的运算;指向相同数组中的元素的指针之间的减或比较运算;将指针赋0或指针与0之间的比较运算。所有其他形式的指针运算均非法,诸如下列形式的运算就是非法的指针运算:指针间的加法、乘法、除法或屏蔽运算;指针值加单双精度浮点数的运算;除两者之一是void*类型指针外,不经强制类型转换就将指

13、向一种类型对象的指针赋给指向另一种类型对象的指针的运算。5.5 字符指针与函数char amessage = “now is the time”; /*定义一个数组*/char *pmessage = “now is the time”; /*定义一个指针*/上述说明中,amessage是一个不可改变的常量,它总指向同一片存储区。另一方面,pmessage是一个指针,其初值指向一个字符串常量,之后它可以被修改指向其他地址,但是如果试图修改字符串的内容,结果将不确定。5.6 指针数组与指向指针的指针1. 例如定义char *line20,那么请注意,这时首先根据运算符优先级规则,line是一个数

14、组,又由于有*的修饰,所以他是一个指针数组,即数组里面存储的全部是指针。5.7 多维数组1.数组在内存中按行存储。如果要将二维数组作为变元传递给函数,那么函数的参数说明中应该指明相应数组的列数,数组的行数不必指定。5.9 指针与多维数组注意int a1020;int *b10;这两个的区别,对于b来说,每一维的长度可以不一致。5.10 命令行变元5.11 指向函数的指针1.在C语言中,函数本身不是变量,但可以定义指向函数的指针,这种指针可以被赋值、存放于数组中、传递给函数及作为函数返回值等等。2.定义指向函数的指针:return_type (*fun_name) (参数列表);调用则为(*fu

15、n_name) (实参);5.12 复杂说明1. 见定义:char *argv; argv:指向字符指针的指针int (*daytab) 13; daytab:指向由13个整形类型元素组成的一维数组的指针(这就是数组指针,指向数组的指针。就是一指针)。(这里这么用,int nArray3 = 1, 2, 3;int (*pArray)3 = &nArray;)int *daytab13; daytab:由13个指向整数类型对象的指针组成的一维数组(存储的是13个指针)。void *comp(); comp:返回值为指向通用类型的指针的函数。void (*comp)(); comp:指向

16、返回值为通用类型的函数的指针。char (*(*X()(); X:返回值为指向一维数组的指针的函数,该一维数组由指向返回字符类型的函数指针组成。char (*(*X3)() 5; X:由3个指向函数的指针组成的一维数组,该函数返回指向由5个字符组成的一维数组的指针。对于这种复杂的声明,可以采用基于说明符的方式进行解读:说明符:可选的*序列 直接说明符直接说明符: 名字 (说明符)直接说明符()直接说明符可选的大小简而言之,说明符即前面也许带有符号*的直接说明符。直接说明符可以是名字、由一对圆括号括住的说明符、后面跟有一对圆括号的直接说明符或后面跟有由方括号括住可选大小的直接说明符。例如:(*p

17、fa)(),pfa首先是一个直接说明符,于是pfa也是一个直接说明符。接着*pfa被识别出是一个说明符,因而(*pfa)是一个直接说明符。于是,按照此规则,对于char (*(*X3)() 5作分析:X是一个直接说明符,那么*X3是一个说明符,这就定义了一个指针数组,类型待定。接着(*X3)是一个直接说明符,那么(*X3)()也是,它表示这是一个函数指针数组,接着*(*X3)()表示这个函数返回的是一个指针,(*(*X3)()表示这是一个直接说明符,char (*(*X3)() 5到这里表示这个函数返回由5个字符组成的一维数组的指针。2. 对于这种复杂的定义,有著名的右左法则:首先从最里面的圆

18、括号看起,然后往右看,在往左看。每当遇到圆括号时,就应该掉转阅读方向。一旦解析完圆括号里面所有的东西,就跳出圆括号。重复这个过程直到整个声明解析完毕。这里,应该对这个法则作一个小小的修正,应该从未定义的标识符开始阅读,即不是C语言的关键字开始。仍旧对上例说明:首先X是个未定义的标识符,往右看是个,说明这是个数组,再往右,遇到括号则往左,有*说明这是个指针数组。在往左,遇到括号,此时括号里面的东西解析完毕。接着括号外面往由,直接遇到括号,说明这是个函数,即指针数组里的指针式函数指针。跳转往左,遇到*说明函数返回值为指针,跳转往右,括号则整个跳出,再往右,说明这是一个指向数组的指针。第六章 结构6

19、.2 结构与函数1. 对结构的合法操作只有拷贝、作为一个单元对其赋值、通过&取其地址及访问结构成员这几种。6.5 自引用结构即在结构内部定义指向自己的指针。6.9 位字段1当存储空间很宝贵时,有必要将几个对象打包到一个单一机器中去。一个常用的方法是使用类似编译程序中符号表的单个位标志集合。外部使用的数据格式(如硬件接口设备)也常需要能从字的部分位中读取数据。2. 有关字段对齐的规则请查资料。C专家编程第四章 数组和指针并不相同4.2 我的代码为什么无法运行1.对于以下定义:文件1:int mango100;文件2:extern int *mango;这种情况是不行的。虽说对数组的引用总

20、是可以写成对指针的引用,而且确实存在一种指针和数组的定义完全相同的上下文环境。但是并不是总是这样。4.3 什么是声明,什么是定义1.C语言中的对象必须有且只有一个定义,但他可以有多个extern声明。定义是一种特殊的声明,它创建了一个对象;声明简单的说明了在其他地方创建的对象的名字,它允许你使用这个名字。extern对象声明告诉编译器对象的类型和名字,对象的内存分配则在别处进行。2.C语言引入了“可修改的左值”这个术语。它表示左值允许出现在赋值语句的左边。这个奇怪的术语是为与数组名区分,数组名也用于确定对象在内存中的位置,也是左值,但它不能作为赋值的对象。因此,数组名是个左值但不是可修改的左值

21、。标准规定赋值符必须用可修改的左值作为他左侧的操作数。通俗的说,只能给可以修改的东西赋值。3.出现在赋值符左边的符号有时被称为左值,出现在赋值符右边的符号有时则被称为右值。编译器为每个变量分配一个地址(左值)。这个地址在编译时可知,而且该变量在运行时一直保存于这个地址。相反,存储于变量中的值(它的右值)(对于这个地方解释,例如x=y,这时y便称为“它的右值”)只有在运行时才可知。如果需要用到变量中存储的值,编译器就发出指令从指定地址读入变量值并将它存于寄存器中。4.这里强调一点,对于所有的变量,在编译时都会分配一个地址,而且在编译时可知。那么我们引用这个变量,就是相当于直接对应了这个地址。对于

22、一般的变量,我们引用定义的名字就引用了编译时分配的对应地址里的内容;对于指针变量,由于变量对应的是一个表示地址的地址,所以我们要解引用。请特别注意理解这里!5.extern char a与extern char a100等价的原因:这两个声明都提示a是一个数组,也就是一个内存地址,数组内的字符可以从这个地址找到。编译器并不需要知道数组总共有多长,因为它只产生偏离起始地址的偏移地址。6.当你“定义为指针,但以数组方式引用”时会发生什么:其实,无论对于你定义为指针却以数组的方式引用,还是定义为数字却以指针的方式引用,都会出现错误(作为函数参数可以)。因为当另外去声明这个变量的时候,接下来就会以你声

23、明的这种方式去引用这个变量。但是二者引用的原理却不一样:定义为指针声明为数组,引用的时候会直接根据这个地址去算偏移量,而不是先解引用,所以会出错。定义为数组声明为指针则会先去解引用,这样对一个不是地址的变量去解引用必然会出错。4.5 数组和指针的其他区别1.指针和数组都可以在它们的定义中用字符串常量进行初始化。尽管看上去一样,底层的机理却不相同。定义指针时,编译器并不为指针所指向的对象分配空间,它只是分配指针本身的空间,除非在定义时同时赋给指针一个字符串常量进行初始化。2.在ANSI C 中,初始化指针时所创建的字符串常量被定义为只读。与指针相反,由字符串常量初始化的数组是可以修改的。第五章

24、对链接的思考5.1 函数库、链接和载入1. 链接器基础知识:编译器创建一个输出文件,这个文件包含了可重定位的对象。这些对象就是与源程序对应的数据和机器指令。绝大多数编译器并不是一个单一的庞大程序。他们通常由多大六七个稍小的程序所组成,这些程序由一个叫做“编译器驱动器”的控制程序来调用。这些可以方便地从编译器中分离出来的单独程序包括:预处理器、语法和语义检查器、代码生成器、汇编程序、优化器、链接器,还包括一个调用所有这些程序并向各个程序传递正确选项的驱动器程序。2. Gcc的编译流程:(1)预处理阶段:将必要的文件包含进来 gcc -E hello.c -o hello.i (2)编译阶段:这个

25、对应的是语法和语义检查器、代码生成器。在这个阶段中,Gcc首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,Gcc把代码翻译成汇编语言 gcc -s hello.i -o hello.s(3)汇编阶段:汇编阶段是把编译阶段生成的”.s”文件转成目标文件,读者在此可使用选项”-c”就可看到汇编代码已转化为”.o”的二进制目标代码了 gcc -c hello.s -o hello.o(4)链接阶段:3. 目标文件(这里只编译进行到汇编(把汇编语言代码翻译成目标机器指令的过程)之后的文件,等链接完后就成了可执行文件)不能直接执行,它首先需要载入到链接器中。链接器确认

26、main函数为初始进入点,把符号引用绑定到内存地址,把所有的目标文件集中在一起,在加上库文件,从而产生可执行文件。4. 如果函数库(这个函数库是指已经编译好的二进制文件)的一份拷贝是可执行文件的物理组成部分,那么我们称之为静态链接;如果可执行文件只是包含了文件名,让载入器在运行时能够寻找程序所需要的函数库,那么我们称之为动态链接。收集模块准备执行的三个阶段的规范名称是链接-编辑、载入和运行时链接。静态链接的模块被链接编辑并载入以便运行。动态链接的模块被链接编辑后载入,并在运行时进行链接以便运行。程序执行时,在main()函数被调用前,运行时载入器把共享的数据对象载入到进程的地址空间。外部函数被

27、真正调用之前,运行时载入器并不解析它们。所以即使链接了函数库,如果并没有实际调用,也不会带来额外开销。5.2 动态链接的优点1. 可执行文件的体积非常小。虽然运行速度稍微慢一些,但动态链接能够更加有效的利用磁盘空间,而且链接-编辑阶段的时间也会缩短(因为链接器的有些共组被推迟到载入时)。2. 尽管单个可执行文件的启动速度稍受影响,但动态链接可以从两个方面提高性能:(1)动态链接可执行文件比功能相同的静态链接可执行文件的体积小。它能够节省磁盘空间和虚拟内存,因为函数库只有在需要时才被映射到进程中。以前,避免把函数库的拷贝绑定到每个可执行文件的唯一方法就是把服务至于内核中而不是函数库中,这就带来了

28、可怕的“内核膨胀”问题。(2)所有动态链接到某个特定函数库的可执行文件在运行时共享改函数库的一个单独拷贝。操作系统内核保证映射到内存中的函数库可以被所有使用他们的进程共享。这就提供了更好的I/O和交换空间利用率,节省了物理内存,从而提高了系统的整体性能。如果可执行文件时静态链接的,每个文件都拥有一份函数库的拷贝,显然极其浪费。3. 编译时地址问题:在编译阶段,所有非局部变量地址已经确定。这些这里有与位置无关的代码和与位置有关的代码。与位置无关的代码表示这种代码保证对于任何全局数据的访问都是通过间接地方法完成的。而与位置无关的代码,其代码会被对应到固定的地址。对于这个问题,有待接下来的研究,还有

29、进程间的共享,以及与位置无关以及与位置有关的代码,这个位置指的是直接对应内存还是相对于程序本身,都有待研究。第六章 运动的诗章:运行时数据结构编程语言理论的经典对立之一就是代码和数据的区别。有些把二者视为一体。C语言通常维持二者的区别。代码和数据的区别也可以认为是编译时和运行时的分界线。编译器的绝大部分工作都跟翻译代码有关,必要的数据存储管理的绝大部分都在运行时进行。6.2 段1. 在UNIX中,段表示一个二进制文件相关的内容块。在Inter x86的内存模型中,段表示一种设计的结果。在这种设计中(基于兼容性的原因),地址空间并非一个整体,而是分成一些64K大小的区域,称之为段。2. 对于un

30、ix中的可执行文件,是以段形式组织的。一般有文本段,数据段和bss段。3. 段可以方便地映射到链接器在运行时可以直接载入的对象中。载入器只是去文件中每个段的映像,并直接将他们放入内存中。从本质上说,段在正在执行的程序中是一块内存区域,每个区域都有特定的目的。4. 每个段的作用:(1)文本段包含程序的指令。链接器把指令直接从文件拷贝到内存中,以后便再也不用管它。(2)数据段包含经过初始化的全局和静态变量以及它们的值。(3)bss段的大小从可执行文件中得到,然后链接器得到这个大小的内存块,紧跟在数据段之后。当这个内存区进入程序的地址空间后全部清零。包括数据段和bss段的整个区段此时通常称为数据区。

31、这是因为在操作系统的内存管理术语中,段就是一片连续的虚拟地址,所以相邻的段被结合。6.4 C语言运行时系统在a.out里干了什么运行时数据结构有好几种:堆栈、活动记录、数据、堆等。1. 堆栈段主要有三个用途:(1) 堆栈为函数内部声明的局部变量提供存储空间。(2) 进行函数调用时,堆栈存储与此有关的一些维护性信息。(3) 堆栈也可以被用作暂时存储区。有时候程序需要一些临时存储,比如计算一个很长的算术表达式时,它可以把部分计算结果压到堆栈中,当需要时再把它从堆栈中取出。除了递归调用之外,堆栈并非必须。因为在编译时可以知道局部变量、参数和返回地址所需空间的固定大小,并可以将它们分配于BSS段。6.

32、5 当函数被调用时发生了什么;过程活动记录1. C语言自动提供的服务之一就是跟踪调用链哪些函数调用了哪些函数,当下一个“return”语句执行后,控制将返回何处等。解决这个问题的经典机制是堆栈中的过程活动记录。当每个函数被调用时,都会产生一个过程活动记录(或类似的结构)。过程活动记录是一种数据结构,用于支持过程调用,并记录调用结束以后返回调用点所需要的全部信息。(如下图):局部变量(local varibales)参数(arguments)静态链接(stack link)指向先前结构的指针返回地址(return address)2. 绝大多数的现代算法语言允许函数和数据一样在函数内部定义。C语

33、言不允许以这种语法进行函数的嵌套。C语言中的所有函数在词法层次中都是位于最顶层。在允许嵌套过程的语言中,活动记录一般要包含一个指向它的外层函数的活动记录的指针。这个指针被称为静态链接,它允许内层过程访问外层过程的活动记录,因此也可以访问外层过程的局部数据。记住在同一时刻一个外层过程可能有好几个处于活动状态的调用。内层过程活动记录的静态链接将指向合适的活动记录,允许访问局部数据的正确实例。这种类型的访问(一个指向词法上外层范围的数据项的引用)被称作上层引用。静态链接(指向从词法上讲属于外层过程的活动记录,由编译时决定)之所以如此命名是因为它与动态链接相对照,后者是一个活动记录指针链(在运行时指向

34、最靠近自己的前一个过程调用的活动记录)。6.6 auto和static关键字1. 对于在函数内部定义的自动变量,函数调用结束就被回收。如果要返回指针,此时指针不是有malloc或静态变量,那么当函数调用结束时,返回了改变量地址的副本,但是该地址(此时为副本代表的地址)所指向的内存块实际上已经被回收。所以当你再去调用这个返回的指针时,其指向的内容其实是未知的。这个指针也叫悬垂指针。2. 存储类型说明符auto关键字在实际中从来用不着。它通常由编译器设计者使用,用于标记符号表的条目它表示“在进入该块后,自动分配存储”。3. 过程活动记录并不一定要存在于堆栈中。事实上,尽可能地把过程活动记录的内容放

35、到寄存器中会使函数调用速度更快。SPARC架构引入了一个概念,称为“寄存器窗口”,CPU拥有一组寄存器,它们只用于保存过程活动记录中的参数。每当函数调用时,空的活动记录依然到堆栈中。当函数调用链非常深而寄存器窗口不够用时,寄存器的内容就会被保存到堆栈中保留的活动记录空间中,以便重新利用这些寄存器。第七章 对内存的思考7.2 Inter 80x86内存模型以及它的工作原理1. 在Inter 80x86内存模型中,段是内存模型设计的结果,在80x86的内存模型中,个各处理器的地址空间并不一致(因为要保持兼容性),但他们都被分割成以64KB为单位的区域,每个这样的区域便称为段。2. 作为80x86内

36、存模型最基本的形式,8086中的段是一块64KB的内存区域,由一个段寄存器所指向。内存地址的形成过程是:取得段寄存器的值,左移4位,然后加上16位的偏移地址(表示段内的地址),就是最终的地址。3. 8086有20位地址总线,总共是1MB的内存。但是只有640KB可供应用程序使用。因为其他的要预留给系统使用。7.3 虚拟内存1. 虚拟内存:为了去除安装在机器上的物理内存数量的限制,提出虚拟内存这个概念。基本思路是用廉价但缓慢的磁盘来扩充快速却昂贵的内存。在任一给定时刻,程序实际需要使用的虚拟内存区段的内容就被载入物理内存中。当物理内存中的数据有一段时间未被使用,他们就可能被转移到硬盘中,节省下来

37、的物理内存空间用于载入需要使用的其它数据。2. 以SunOS为例说明:SunOS中的进程执行于32位地址空间。操作系统负责具体细节,使每个进程都以为自己拥有整个地址空间的独家访问权。这个幻觉是通过“虚拟内存”实现的。所有进程共享机器的物理内存,当内存用完时就用磁盘保存数据。在进程运行时,数据在磁盘和内存之间来回移动。内存管理硬件负责把虚拟地址翻译为物理地址,并让一个进程始终运行于系统的真正内存中。应用程序员只看到虚拟地址,并不知道自己的进程在磁盘和内存之间来回切换。虚拟内存通过“页”的形式组织。页就是操作系统在磁盘和内存之间移来移去或进行保护的单位,一般为几K字节。从潜在的可能性上说,与进程有关的所有内

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论