C程序设计C程序设计 (69).pdf
C C程序设计程序设计Programming in CProgramming in C指针的有效性和风险控制指针的有效性和风险控制1、指针的有效性2、指针的风险控制C C程序设计程序设计程序设计程序设计3 37.2.4 7.2.4 指针的有效性指针的有效性指针是特殊的数据,因此指针的运算和操作要注意有效性问题。程序中的一个指针必然是以下三种状态之一:指向一个已知对象;0值;未初始化的、或未赋值的、或指向未知对象。4 47.2.4 7.2.4 指针的有效性指针的有效性无论指针作了什么运算和处理,只要操作后指针指向程序中某个确切的对象,即指向一个有确定存储空间的对象(称为已知对象),则该指针是有效的;如果对该指针使用间接引用运算,总能够得到这个已知对象。指针理论上可以为任意的地址值,若一个指针不指向程序中任何已知对象,称其指向未知对象。未知对象的指针是无效的,无效的指针使用间接引用运算几乎总会导致崩溃性的异常错误。5 57.2.4 7.2.4 指针的有效性指针的有效性(1)如果指针的值为0,称为0值指针,又称空指针(null pointer),空指针是无效的。例如:多数情况下,应该在指针间接引用之前检测它是否为空指针,从而避免异常错误。intint*p p=0 0;*;*p p=2 2;/空指针间接引用将导致程序产生严重的异常错误/空指针间接引用将导致程序产生严重的异常错误6 67.2.4 7.2.4 指针的有效性指针的有效性(2)如果指针未经初始化,或者没有赋值,或者指针运算后指向未知对象,那么该指针是无效的。一个指针还没有初始化,称为“野指针”(wild pointer)。严格的说,每个指针还没有初始化之前都是“野指针”,大多数的编译器都对此产生警告。例如:intint*p p;/p是野指针/p是野指针*p p=2 2;/几乎总会导致程序产生严重的异常错误/几乎总会导致程序产生严重的异常错误7 77.2.4 7.2.4 指针的有效性指针的有效性一个指针曾经指向一个已知对象,在对象的内存空间释放后,虽然该指针仍是原来的内存地址,但指针所指已是未知对象,称为“迷途指针”(dangling pointer)。8 87.2.4 7.2.4 指针的有效性指针的有效性1 charchar*p p=NULLNULL;/p是空指针,全局变量/p是空指针,全局变量2 voidvoid funfun()()3 4 charchar c c;/局部变量/局部变量5 p p=&=&c c;6/指向局部变量c,函数调用结束后,c的空间释放,p就成了迷途指针/指向局部变量c,函数调用结束后,c的空间释放,p就成了迷途指针7 8 voidvoid callercaller()()9 10 funfun();();11*p p=2 2;/p现在是迷途指针,几乎总会导致程序产生严重的异常错误/p现在是迷途指针,几乎总会导致程序产生严重的异常错误12 9 97.2.4 7.2.4 指针的有效性指针的有效性这两种情况比起空指针更难发现,因为程序无法检测这个非0值的指针p究竟是有效的还是无效的,也无法区分这个指针所指向的对象的地址是已知对象的还是未知对象的。10107.2.4 7.2.4 指针的有效性指针的有效性示例scanf函数的实参要求输入变量的地址(即&a和&b),以便它将输入数据按地址送到变量中,但第2行实际给出的是变量a和b的值,而a和b尚未初始化,于是实参就成了未初始化的指针,是无效指针。intint a a,b b;scanfscanf(%d%d,(%d%d,a a,b b););/错误,几乎总会导致程序产生严重的异常错误/错误,几乎总会导致程序产生严重的异常错误11117.2.4 7.2.4 指针的有效性指针的有效性实际编程中,程序员要始终确保引用的指针是有效的,对尚未初始化或未赋值的指针一般先将其初始化为0值,引用指针之前检测它是否为0值。12127.2.5 7.2.5 指针运算指针运算指针运算主要是给定范围内指针的算术运算、比较运算、类型转换等,由于指针数据的特殊性,因此需要特别注意指针运算的地址意义。13137.2.5 7.2.5 指针运算指针运算1指针的算术运算指针的算术运算有指针加减整数运算,指针变量自增自减运算,两个指针相减运算。14147.2.5 7.2.5 指针运算指针运算(1)指针加减整数运算设p是一个指针(常量或变量),n是一个整型(常量或变量),则p+n的结果是一个指针,指向p所指向对象的后面的第n个对象;而p-n的结果是一个指针,指向p所指向对象的前面的第n个对象。15157.2.5 7.2.5 指针运算指针运算例如:特别地,p+0、p-0与p均指向同一个对象。intint x x,n n=3 3,*,*p p=&=&x x;p p+1 1/指向存储空间中变量x后面的第1个int型存储单元/指向存储空间中变量x后面的第1个int型存储单元p p+n n/指向存储空间中变量x后面的第n(此时为3)个int型存储单元/指向存储空间中变量x后面的第n(此时为3)个int型存储单元p p-1 1/指向存储空间中变量x前面的第1个int型存储单元/指向存储空间中变量x前面的第1个int型存储单元p p-n n/指向存储空间中变量x前面的第n(此时为3)个int型存储单元/指向存储空间中变量x前面的第n(此时为3)个int型存储单元16167.2.5 7.2.5 指针运算指针运算【例7.5】指针加减整数运算后的输出。17177.2.5 7.2.5 指针运算指针运算例7.51#include#include 2 intint mainmain()()3 4 intint x x,n n=3 3,*,*p p=&=&x x;5 printfprintf(p=%x,p+1=%x,(p=%x,p+1=%x,p p,p p+1 1););/地址输出用十六进制形式/地址输出用十六进制形式6 printfprintf(p+n=%x,p-n=%x(p+n=%x,p-n=%xnn,p p+n n,p p-n n););/地址输出用十六进制形式/地址输出用十六进制形式7 returnreturn 0 0;8 18187.2.5 7.2.5 指针运算指针运算例7.51#include#include 2 int main()int main()3 4 int x,n=3,*p=&x;int x,n=3,*p=&x;5 printf(p=%x,p+1=%x,p,p+1);/地址输出用十六进制形式printf(p=%x,p+1=%x,p,p+1);/地址输出用十六进制形式6 printf(p+n=%x,p-n=%xn,p+n,p-n);/地址输出用十六进制形式printf(p+n=%x,p-n=%xn,p+n,p-n);/地址输出用十六进制形式7 return 0;return 0;8 p=12ff7c,p+1=12ff80,p+n=12ff88,p-n=12ff70程序运行屏幕19197.2.5 7.2.5 指针运算指针运算可以看出,p+1的地址值与p的地址值相差了4,p+n的地址值与p的地址值相差了12。即p+1不是按数学意义来计算的,而是按指针的地址意义来计算的。p+1就是p所指向的int型后面的那个int型对象的地址,由于int型对象在内存中占用4个字节,因此p+1的值与p相差4。显然,p+1的值究竟是多少,与p所指向对象的类型有关。20207.2.5 7.2.5 指针运算指针运算一般地,如果指针p所指向对象的类型为TYPE,那么pn的值为:p的地址值 n*sizeof(TYPE)如图所示。21217.2.5 7.2.5 指针运算指针运算(2)指针变量自增自减运算设p是一个指针变量,其自增自减运算包括p+、+p、p-、-p形式。22227.2.5 7.2.5 指针运算指针运算intint x x,*,*p p=&=&x x;p p+/运算后表达式的值(临时指针对象)指向变量x,p指向变量x后面的第1个int型内存单元/运算后表达式的值(临时指针对象)指向变量x,p指向变量x后面的第1个int型内存单元+p p/运算后表达式的值(临时指针对象)和p均指向变量x后面的第1个int型内存单元/运算后表达式的值(临时指针对象)和p均指向变量x后面的第1个int型内存单元p p-/运算后表达式的值(临时指针对象)指向变量x,p指向变量x前面的第1个int型内存单元/运算后表达式的值(临时指针对象)指向变量x,p指向变量x前面的第1个int型内存单元-p p/运算后表达式的值(临时指针对象)和p均指向存储空间中变量x前面的第1个int型内存单元/运算后表达式的值(临时指针对象)和p均指向存储空间中变量x前面的第1个int型内存单元23237.2.5 7.2.5 指针运算指针运算【例7.6】指针变量自增自减运算后的输出。24247.2.5 7.2.5 指针运算指针运算例7.61#include#include 2 intint mainmain()()3 4 intint x x,*,*p1p1,*,*p p;5 p p=&=&x x,p1p1=p p+;+;6 printfprintf(p+:&x=%x,p=%x,p+=%x(p+:&x=%x,p=%x,p+=%xnn,&,&x x,p p,p1p1););/地址输出用十六进制形式/地址输出用十六进制形式7 p p=&=&x x,p1p1=+=+p p;8 printfprintf(+p:&x=%x,p=%x,+p=%x(+p:&x=%x,p=%x,+p=%xnn,&,&x x,p p,p1p1););/地址输出用十六进制形式/地址输出用十六进制形式9 returnreturn 0 0;10 25257.2.5 7.2.5 指针运算指针运算例7.61#include#include 2 int main()int main()3 4 int x,*p1,*p;int x,*p1,*p;5 p=&x,p1=p+;p=&x,p1=p+;6 printf(p+:&x=%x,p=%x,p+=%xn,&x,p,p1);/地址输出用十六进制形式printf(p+:&x=%x,p=%x,p+=%xn,&x,p,p1);/地址输出用十六进制形式7 p=&x,p1=+p;p=&x,p1=+p;8 printf(+p:&x=%x,p=%x,+p=%xn,&x,p,p1);/地址输出用十六进制形式printf(+p:&x=%x,p=%x,+p=%xn,&x,p,p1);/地址输出用十六进制形式9 return 0;return 0;10 p+:&x=12ff7c,p=12ff80,p+=12ff7c+p:&x=12ff7c,p=12ff80,+p=12ff80程序运行屏幕26267.2.5 7.2.5 指针运算指针运算图7.9 指针变量自增自减运算示意27277.2.5 7.2.5 指针运算指针运算另外,设有定义“int a100,*p=&a;”,需要注意以下形式的运算含义。(*p)+:等价于a+,运算执行后p值不变;*p+:按照运算符优先级,等价于*(p+),运算后表达式的值为a,p指向下一个int型内存单元;*+p:按照运算符优先级,等价于*(+p),p先指向下一个int型内存单元,表达式再引用这个内存单元的值。28287.2.5 7.2.5 指针运算指针运算(3)两个指针相减运算设p1、p2是同一个指向类型的两个指针(常量或变量),则p2-p1的结果为两个指针之间对象的个数,如果p2的地址值大于p1结果为正,否则为负。29297.2.5 7.2.5 指针运算指针运算指针算术运算后通常会引起地址的变化,实际编程中要考虑此时指针的有效性。例如:p原先指向x,是有效的;p+运算后p指向x的“下一个”,但这里“下一个”是未知对象,故自增运算后的p是无效的。intint x x,*,*p p=&=&x x;p p+;+;/迷途指针,指向未知对象/迷途指针,指向未知对象*p p=100100;/几乎总会导致程序产生严重的异常错误/几乎总会导致程序产生严重的异常错误30307.2.5 7.2.5 指针运算指针运算指针算术运算经常用于数组、字符串或内存数据块,因为这些对象拥有连续的有效地址空间,只要在其存储空间范围内,运算后的指针都是有效的。结束结束