C语言程序设计教程课件 第7章 指针

上传人:w****i 文档编号:94568124 上传时间:2019-08-08 格式:PPT 页数:91 大小:497.50KB
返回 下载 相关 举报
C语言程序设计教程课件 第7章 指针_第1页
第1页 / 共91页
C语言程序设计教程课件 第7章 指针_第2页
第2页 / 共91页
C语言程序设计教程课件 第7章 指针_第3页
第3页 / 共91页
C语言程序设计教程课件 第7章 指针_第4页
第4页 / 共91页
C语言程序设计教程课件 第7章 指针_第5页
第5页 / 共91页
点击查看更多>>
资源描述

《C语言程序设计教程课件 第7章 指针》由会员分享,可在线阅读,更多相关《C语言程序设计教程课件 第7章 指针(91页珍藏版)》请在金锄头文库上搜索。

1、第1页 共91页,第7章 指针,指针和存储器地址 指针的类型和指针的初始化 指针表达式 指针的运算 用指针访问一维数组 用指针访问二维数组 指针与字符串 指针数组与指向指针的指针 用const限定符修饰指针 指针与动态内存分配,第2页 共91页,7.1 指针和存储器地址,我们可以将存储器概念化成存储单元的顺序集合。如图所示,每个存储单元用一个地址来标识,如2020,称做2020号单元。目前,大部分系统都是按字节编址的,即一个字节对应一个地址。图7-1给出的就是字节编址的存储器。通常,一个字节的长度是八个二进制位。存储单元就是用来存放数据的,从程序员的角度看,内存中的每个存储单元总是存储着某个值

2、的。,第3页 共91页,一个存储位置对应一个地址,第4页 共91页,一个字节作为数据的独立存储单位,在大多数情况下远远不能满足应用的要求,因为它所能表示的值的范围非常有限。为了存储更大的值,我们往往将两个或更多的字节在逻辑上组合成一个更大的存储单位。例如,大多数机器以所谓“字”为单位存取整数,每个字一般由2个字节或4个字节组成。 尽管一个字包含了两个字节,它有两个物理地址,但从程序员的角度看,这个字只有一个地址,它一般是首字节的地址。但也不一定,有些系统将末字节的地址作为字的地址。,第5页 共91页,每个字的长度为2字节,第6页 共91页,我们在编程中定义变量,例如,设有如下定义:int a

3、= 100, b = 199;,第7页 共91页,7.2 指针的类型和指针的初始化,再谈指针的定义和指针的初始化 指针的类型,第8页 共91页,7.2.1 再谈指针的定义和指针的初始化,和其他变量一样,指针变量在使用前必须先定义。在2.5.6就已经介绍了如何定义指针变量。读者要注意指针定义语句的微妙之处。定义语句: int *nPtr1, nSum; 定义了一个int* 类型的指针变量nPtr1,可读作“nPtr1是指向整数值的指针”,另外,还定义了一个整型变量nSum。 由于C语言在书写形式上的过度自由,很可能会写出如下形式的定义语句: int* nPtr1, nSum;,第9页 共91页,

4、请注意它与前面那个定义语句在形式上的细微差别。它没有语法错误,但本质上,它们两者都表达的是同一个意思。那么你是如何理解第二个定义语句的呢?你是否被它的外表所蒙骗,理所当然地认为它定义了两个指针变量了呢?正确的理解应该是,星号只是表达式*nPtr1的一部分,nPtr1是一个指针,nSum是一个int变量。可见,间接访问运算符“*”并不针对定义语句中的所有变量。每定义一个指针都必须在其名字前面用“*”作前缀。例如,要将上述两个变量定义为指向整数值的指针变量,正确的写法是: int *nPtr1, *nSum;,第10页 共91页,NULL是在头文件中定义的一个常量。将NULL赋给一个指针,表示那个

5、指针并不指向任何目标,我们称值为NULL的指针为“空指针”。将一个指针初始化为0等价于把该指针初始化为NULL,但是用NULL更好。数值0是唯一能够直接赋给指针变量的整数值。,第11页 共91页,由于NULL指针并未指向任何目标,因此,对NULL指针施加间接访问运算是非法的。不同编译器对这个非法操作的处理可能不尽相同。有些系统会捕获并报告这个错误,然后终止程序;有些系统允许这个运算被执行,而将由此产生的后果推给程序员。 总之,空指针在概念上不同于“悬挂”指针。空指针可以确保不指向任何目标,而悬挂指针可能指向任何地方。但是,对空指针或悬挂指针施加间接访问运算,都可能会导致致命的执行错误或意外地修

6、改重要的数据。如果修改了数据的程序能够完成运行,但提供的结果是毫无意义的。,第12页 共91页,7.2.2 指针的类型,指针类型的意义在于可以指示编译器怎样解释特定地址上内存的内容这些数据的含义或逻辑关系,以及该内存区域应该跨越多少存储单元。例如,对于32位机器,设p是一个int型指针,p指向的位置是2020,那么跨越的地址空间是20202023,表达式*p将这四个字节的内存区域的内容解释为一个32位的整数;如果p是一个double型指针,p指向的位置是2020,那么跨越的地址空间是20202027,表达式*p将这8个字节的内存区域的内容解释为一个64位的浮点数。,第13页 共91页,由此可见

7、,一个指针不能指向与其类型不同的目标。下面的例子说明了这一点。 int a, *nPtr; /* 指针nPtr的类型是int* */ float x,*fPtr = ,第14页 共91页,上面的第二个语句试图将一个浮点类型指针指向一个整数,这是非法的。因为,编译器已经将fPtr解释为浮点类型指针,因此,它将要指向的目标空间的大小和该空间只能存储什么内容都已经确定,而实际赋给它的值与预期的不符。 第三个语句是将浮点类型指针的值赋给整数类型指针,这同样会导致编译时出错。不是说nPtr在物理上不能持有与变量x相关联的内存地址,它能够,但是不允许。因为,虽然nPtr和fPtr能够持有同样的地址值,但对

8、那块内存的存储布局和内容的解释却完全不同。因此,不同类型的指针变量不能赋值。,第15页 共91页,7.3 指针表达式,我们已经知道,指针可以作为左值。但问题是,涉及到指针的表达式,哪些能作为左值,哪些不能。先分析下面这个例子: int a = 10,*p,*p1 = 0; char *p2; p = a; *p = a; p1 = p; a = *p; p2 = “a“;,第16页 共91页,指针p和p2都没有初始化。那么p和p2是不是左值呢?当然是左值。它们是左值,不是因为它们是指针,而是因为它们是变量。变量肯定是左值。这就说明指针是不是左值与它是否初始化无关。本例中,编译器判定表达式:p=

9、a是非法的,错误的原因是将一个非0的数值10赋给一个指针;编译器将对表达式:*p = a提出警告。因为p是左值,*p不一定也是合法的左值。由于p是一个悬挂指针,p的值可能是一个合法的也可能是一个非法的地址,对这个表达式的执行,可能会导致致命的执行错误或意外地修改重要的数据。由此可见,对于一个悬挂指针p,p和*p都不是一个合法的右值,即对于表达式:p1 = p和a = *p编译器都将给出警告,程序员应当对这种善意的忠告给予足够的重视。事实上,由于指针p是悬挂的,p和*p的值都是不确定的,因而也是无意义的,它们只不过是些垃圾数据。而这两个表达式却用这些垃圾数据分别更新了p1和a的值。而对于一个已经

10、指向一个确定目标的指针,例如,指针p指向变量a,即执行语句:p = 后,凡是可以出现&a的地方,都可以替换成指针p,a可以替换成*p。,第17页 共91页,注意,取地址运算符“&”不能作用于表达式或者常量。 最后一个表达式:p2 = “a“从形式上看好像有问题:将一个字符串常量赋给指针,犯的是与表达式p=a一样的错误。但实际上它是将字符串首字符的地址赋给指针p2,所以这个表达式是正确的。,第18页 共91页,7.4 指针的运算,指针的算术运算 指针的关系运算,第19页 共91页,7.4.1 指针的算术运算,指针自增、自减运算 +和-运算在指针变量中使用得相当频繁。包括, 前缀:+p,-p 后缀

11、:p+,p-,第20页 共91页,指针与整数的加减运算 指针加上(减去)一个整数的结果是另一个指针,其形式是: 指针 整数 这里的“n”指的是n个元素单位,而不是实际加减的“量”,实际加减的“量”应该是,n乘以一个元素所占空间的字节数。显然,它与指针的类型有关。例如,不妨设指针p的类型是float*,则,下面表达式: p+5 实际上是: p+5*sizeof(float),第21页 共91页,如果float类型的大小是4,则实际加到指针p上的整数值是20。 ANSI C定义这种形式的运算只能用于指向数组中某个元素的指针。如图7-6所示,当指针p已经指向数组第i个元素时,对p加1使得p指向数组中

12、下一个(即第i+1个)元素,加5使得p向下移动5个元素位置(指向ai+5)。类似地,对一个指针减去5,使得指针p上移(往回退)5个元素位置。显然,指针的这种运算使得我们快速、连续地访问数组中元素,既方便又自然。,第22页 共91页,指针与指针的相减运算 这种运算具有如下形式: 指针-指针 只有当参与运算的两指针都指向同一数组中的元素时,从一个指针减去另一个指针的行为才变的有意义。显然,这两个指针的类型应相同。 指针与指针相减所得结果为一带符号整数,但它不是一个地址值,而是表示两指针之间在内存中的距离或元素的个数。于是,指针p与r相减的结果实际按下式计算: (p-r)/数组元素长度,第23页 共

13、91页,7.4.2 指针的关系运算,所有关系运算都适用于指针。关系运算的结果是逻辑值0或非0,它反映了两指针所指存储位置之间的关系。 无论指针的类型是否相同,任何指针之间都可以进行“=”或“!=”比较,虽说这样做可能没什么意义。如果两个指针都指向同一数组中的元素,那么它们之间还可以进行、=等关系运算,以判断它们在数组中的相对位置。要注意的是,ANSI C认为,如果两个任意的指针进行上述比较,其结果是未定义的。 特别地,任何指针与常量NULL或数值0作比较都是有意义的,因为据此可判断该指针是否为空指针。,第24页 共91页,7.5 用指针访问一维数组,指针在一维数组中的应用 指针的效率,第25页

14、 共91页,7.5.1 指针在一维数组中的应用,假设我们已经定义了一个整型数组a10和整型指针p。因为数组名a是数组的首地址,,第26页 共91页,如果指针p指向了数组a,那么,指针表达式p+n与a+n都表示数组元素an的地址,即&an。对整个a数组来说,根据前面的假设,共有10个元素, 则n的取值为09,它们分别与&a0 &a9保持一致。即有:p+n = &an 其中,n是一个受限的整数。由于间接访问运算符*可作用于指针表达式,对上面这个等式的两边施加间接访问运算,则得到下面的等式: *(p+n) = *&an 因为表达式*&an的结果就是an,所以我们得到如下结论: *(p+n) = an

15、,第27页 共91页,上面这个等式说明,当指针p指向了数组a的首元素后,对该数组的任一元素ai,都可以用与它等价的指针表达式*(p+i)来引用。图7-8右边前面的两列给出了它们的等价表示形式。其中,*p就是*(p+0)。 指针表达式*(p+i)无疑是一个左值表达式,式中的“i”是相对于指针的偏移量。当该指针指向数组的起始位置时,偏移量说明了引用哪一个数组元素,它等于数组的下标。这种表示法称为“指针/偏移量表示法”(pointer/offset notation)。因为*的优先级高于+的优先级,所以表达式中的圆括号是必需的。,第28页 共91页,既然指针表达式*(p+i)中的“i”是相对于指针的

16、偏移量,那么,等式: *(p+i) = ai 就只有当指针p指向了数组a的首元素时才成立。如果指针p不是指向数组的首元素,而是指向该数组中的其他某个元素,例如指向a3,那么,表达式: *(p+2) 就不是引用a2,而是引用a5。因为表达式*(p+2)中的这个“2”是相对于p的当前位置的一个偏移量,其含义是“从p的当前位置出发,向前偏移2个元素位置”,而指针p当前处在元素a3的位置。根据指针运算法则和偏移量概念,表达式: *(p+(-2) 即表达式*(p-2)的含义是“从p的当前位置出发,往后偏移(即后退)2个元素位置”。显然,只要这个偏移量最终不会导致整个表达式跨越数组的边界,它就是有意义的。,第29页 共91页,7.5.2 指针的效率,分析我们熟知的两个循环语句: for( i = 0; i SIZE; i+ ) printf( “%d“,ai);和 for( i = 0; i SIZE; i+ ) printf( “%d“,aPtr

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 高等教育 > 大学课件

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号