收藏 分享(赏)

第8讲 指针.doc

上传人:jinchen 文档编号:9068826 上传时间:2019-07-22 格式:DOC 页数:23 大小:171KB
下载 相关 举报
第8讲 指针.doc_第1页
第1页 / 共23页
第8讲 指针.doc_第2页
第2页 / 共23页
第8讲 指针.doc_第3页
第3页 / 共23页
第8讲 指针.doc_第4页
第4页 / 共23页
第8讲 指针.doc_第5页
第5页 / 共23页
点击查看更多>>
资源描述

1、第 1 页第八讲 指针教学目标1深刻理解并掌握指针的概念。2掌握指针变量的定义和应用,指针变量作为函数参数的用法。3掌握指向数组的指针与指向数组的指针变量的概念、定义和应用。4掌握指向字符串的指针的定义和应用。5理解指向函数的指针的定义和用法。6了解返回指针值的函数的定义和应用,了解它与指向函数的指针变量的区别。7了解指针数组的定义和应用,掌握指针的指针的定义和应用,了解它指针数组与指向数组的指针变量的区别。8.1 地址和指针的概念一、变量与地址1、内存地址计算机内存的组织方式是把所有单元顺序排列,每个单元(字节)有一个顺序编号, 称单元的地址或称为指针。2、变量地址系统分配给变量所需的内存单

2、元的起始地址。二、指针与指针变量1、指针:一个变量的地址。2、指针变量:专门存放变量地址的变量。三、变量值的存取1、直接存取(直接访问)一般情况下,在程序中对变量进行存取操作,实际上就是对某个地址的存储单元进行操作,只不过变量和具体地址的联系由编译系统完成,用户无须知道变量在内存中的具体地址,这种直接按变量地址存取变量值的方式称 “直接存取” 。例如:scanf(“%d“,2)说明 指针变量的值:为某个内存单元的地址值(地址编号)。值的类型均为整型。 通过指针访问的对象有类型:C 语言规定,任何一个指针变量, 只能指向具有某种特定类型的变量。定义中的类型名限定了指针变量可以关联的对象的范围。

3、指针变量也是变量:像普通变量一样可以赋值、取值等。但要满足类型约束。例如:int *p1; 定义指向整型变量的指针变量 p1, 也叫整型指针。float *p2;定义指向浮点型变量的指针变量 p2 ,也叫浮点指针。char *s; 定义指向字符的指针变量 s,也叫字符指针。int *p,n,a10 ;指针变量可和其他变量或者数组一起定义2、空指针1)定义:空指针是一个特殊的指针,它的值是 02)说明 0 的符号常量:为 NULL(在 stdio.h 中定义),系统保证这个值不会是任何变量的地址 空指针对任何指针类型赋值都是合法的:一个指针变量具有空指针值, 表示当前它没有指向任何有意义的值。

4、用 p = NULL ( 比 p = 0 ) 的形式为好。 p = NULL 与未对 p 赋值不同。3)用途: 避免指针变量的非法引用 在程序中常作为状态比较3、void 指针1)定义:(void*) 类型的指针叫通用指针, 它可以指向任何变量, C 语第 3 页言允许直接把任何变量的地址作为指针赋给通用指针。2)说明 通用指针不能做间接运算:因为编译系统无法确定它所指的变量的类型。 使用通用指针需要进行类型强制转换。如: (void *) gp 所指空间的数据是整型数据, p 是整型指针, 用下式转换:p = (int *) gp; 指针转换实际上没有带来指针值的任何变化, 一个指针类型代表

5、一种观点。如通过整型指针的间接使用, 永远把被指的东西看成整型。 通用指针对被指的内容不提供任何有意义的类型信息, 所以不能通过这种指针间接使用被指的内容。指针转换是一种观点转换,转换成哪种类型的指针,就用哪种类型去解释被指的变量。 通用指针的惟一用途就是提供一个指针值。可以看成内存中一个字节一个字节的地址, 没有具体类型。二、指针的操作1、指针赋值一个指针变量可以通过不同的方法获得一个确定的地址值, 从而指向一个具体的对象: 取地址运算 (一元运算符例如:若有定义 int x, *p; 把 p 则 scanf(“%d“,p= qp=分析下面几行程序执行的过程和结果。*p = 100; /*

6、把整数 100 赋给 k 变量 */*p =*p+1;/* 取 p 所指单元的值, 加 1 后,赋给 p 所指的存储单元, k 变为 101 */*p += 1; /* 等价于 k+= 1; k 变为 102 */+ *p; /* 等价于 +k, k 变为 103 */(*p)+; /* 等价于(k)+, k 变为 104 */3、移动指针 将移动指针就是对指针变量加上或减去一个整数, 或通过赋值运算使指针变量指向相邻的存储单元。因此, 只有当指针指向一串连续的存储单元时, 指针移动才有意义。 对指针进行 +、- 运算中, 数字 “1” 不再代表十进制数 “1”, 而是 1 个存储单元长度,

7、如果整型变量存储单元长度是 2 个字节, 整型指针移动 1 个存储单元就是移动 2 个字节, 双精度型指针加 1 就是移动 8 个字节, 依此类推。 程序中移动指针时, 不论指针的基类型是什么, 只需简单地加、减一个数而不必去管它的具体长度, 系统将会根据指针的基类型自动确定位移的字节数。 最常用的移动操作就是加一 (+) 和减一 (-) 操作。它们分别代表指针向地址值增大的方向移动一个存储单元和指针向地址值减少的方向移动一个存储单元。 两个指向同一串连续单元的指针可以进行相减的运算, 结果是两个指针之间元素的个数, 可以通过赋值使两个指针指向同一个单元。【例 8.2】设 p, q 是指向一片

8、连续整数空间的整型指针, p 指向第一个整数, q 指向最后一个整数, a 是整型变量, 顺序执行下面的操作, 分析操作的意义和结果。+p ;/* 指针 p 后移 1 个存储单元,指向第二个整数 */q-;/* 指针 q 前移 1 个存储单元, 指向倒数第二个整数 */a=*p+;/* 取出 P 所指单元内容赋给 a, 指针 P 后移 1 个存储单元, 指向第三个整数 */a=q-p; /* 计算 q 和 P 之间整型数个数将它赋给 a */p=q- ; /* 将 p 指向 q 所指单元,指针 q 前移 1 个存储单元, 指向倒数第三个整数 */*p=10/2; /* 计算 10/2 的值,

9、赋给 p 所指单元 */4、指针比较 两个指针指向同一串连续的存储单元时, 可以在关系表达式中对其进行比较, 判断指针的位置关系。如果两个指针变量的值相等, 表示它们指向同一个存储单元。另外, 还可进行是否是空 (NULL) 指第 5 页针的判断。 指针基类型对指针的相关操作有一定的约束和限制, 主要有以下几方面: 基类型使指针只能指向基类型定义的一类变量。(如:整型的指针只能指向整型变量) 限制指针移动的跨度。 (如:整型的指针每次移动都是 2 个字节的倍数)三、指针变量的初始化1、一般形式 存储类型 数据类型 *指针名 = 初始地址值2、说明1)指针变量在定义时可以用任何合法的指针(地址)

10、 值进行初始化。如下面的定义中包含了两个指针初始化部分:int a, *p = int a; 是错误的。 可以用已初始化指针变量作初值。例:int i;int *p=int *q=p; 不能用 auto 变量的地址去初始化 static 型指针。例:int i;static int *p= 是错误的。【例 8.3】用指针指向两个变量,通过指针运算,选出较小的。注意指针没有初始化的情况下,如何开始使用程序:#include main( ) int a, b, min , *pa, *pb, *pmin ;pa= pb= pmin= scanf( “%d%d“, pa, pb);printf( “

11、a =%d b = %d n“, a, b);if (*pa *pb) *pmin = *pb;else *pmin = *pa;printf (“min = %dn“, min); 运行时, 如果输入 :第 6 页23 67程序的运行结果:a=23 b=67min = 238.3 函数与指针由于 C 语言函数参数的传递机制是值传递,函数中形参的改变并不能改变对应实参的值,把数据从被调函数返回到调用函数的惟一途径是通过return 语句返回函数值,限定了函数只能返回一个数据。在函数中使用指针,可以突破这些限制,函数定义中的各部分:参数、函数名、返回值及函数体内,都可以使用指针,本节讨论这方面的

12、情况。一、指针作为函数参数1、说明 若函数的形参为指针类型,调用该函数时,对应实参必须是基类型相同的地址值或已指向某个存储单元的指针变量。 虽然实参和形参之间还是值传递方式, 但由于传递的是地址值, 所以形参和实参指到了同一个存储单元。 函数中, 通过形参操作的存储单元与实参所指的是同一单元, 因此实参的值发生了改变。利用此形式, 可以把两个或两个以上的数据从被调用函数中返回到调用函数。 注意:被调用函数不能改变实参指针变量的值,但可改变实参指针变量所指的变量的值。【例 8.4】交换两个变量值的函数程序:#include void swap(int *, int *);main( ) int

13、a=10, b=20;printf(“Before changed: a=%d b=%dn“,a,b);swap(printf(“After changed: a=%d b=%dn“,a,b);void swap (int *p, int *q) int t; /* t 是整型变量, 而非指针 */t =*p; *p=*q; *q=t;程序的运行结果:Before changed: a=10 b=20After changed: a=20 b=10二、返回指针的函数1、返回指针函数的定义一个函数可以返回一个 int 型、float 型、char 型的数据, 也可以第 7 页返回一个指针类型的数

14、据。返回指针函数的定义方法如下:类型名 *函数名( 形参表) /* 头部 */ 说明部分 /* 函数体 */语句部分2、函数体内的 return 语句的返回值必须是地址返回值可在任何有意义的引用处使用。【例 8.5】返回两个整数形参中较大数的地址#include int *fun(int *,int *);main() int *pmax, a, b;printf(“Enter two number:“);scanf(“%d%d“, pmax=fun(printf(“a=%d, j=%d *pmax=%dn“,a, b,*pmax);int *fun(int *p, int *q) /* 返回

15、值较大的单元的地址*/ if (*p*q) return p;else return q;程序运行结果:Enter two number: 45 25 a = 45 b = 23 *pmax = 45三、函数指针1、什么是函数指针一个函数在编译时,被分配了一个入口地址,这个地址就称为该函数的指针。实际上函数名就是该函数的入口地址,就象数组名是数组所占存储空间的首地址一样。可以用一个指针变量指向一个函数,然后通过该指针变量调用此函数。(间接调用)。2、函数指针变量的定义形式类型名 (*指针变量名)(参数类型表);3、实例int (* p)(); 表示 p 是一个指向函数入口的指针变量,即用于专门

16、存放函数的入口地址的变量, p 在程序中可以先后指向不同的函数。4、使用函数指针采用的步骤 定义函数指针变量 定义格式为: int (*p)() ;注意与 int *p() 返回指针的函数的区别 为函数指针变量赋值赋值 赋值格式为: p=函数名;赋值时只需给出函数名,不必给出参数。第 8 页 通过函数指针调用函数调用格式为: c=(*p)(实参);其含义是调用由 p 指向的函数,返回值赋给 c5、注意 调用时, 由于是通过 p 间接调用函数, 所以没有参数类型说明的定义, 编译系统无法进行参数类型的检查, 可能出现极难发现的语义错误。 形如 int (*p)() 的说明可以这样分析:括号的优先

17、级最高, 括号内是具有 * 标志的指针,紧跟其后的() 表示它指向函数,是函数指针。 形如 int *p() 的说明可以这样分析:p 中的两侧分别为 * 和(), () 的优先级高于 *, p 先与 () 结合, 显然是函数, 前面有一个 *, 表示函数是指针型函数, 指针的基类型是 int。【例 8.6】通过函数指针调用系统库函数 printf, 打印一行信息。#include main() int (*p)(); /* 定义 */p=printf; /* 赋值 */(*p)(“*n“); /* 调用 */6、函数指针可以作为函数的参数它的用途是把指针作为参数 (某一个函数地址) 传递到其他

18、函数, 即函数地址的传递。由于作为实参的函数指针每次所指向的函数不同, 每次调用执行的功能可能完全不同。【例 8.7】设计一个处理字符串的函数, 在调用时每次实现不同的功能, 第一次调用求出串长, 第二次调用将字符串中的字母字符全部转换成大写。分析:我们使用 C 语言系统提供的库函数 strlen 完成第一次调用, 第二次使用自定义的 toup 函数。它们的返回值类型都是 int。程序#include #include int toup (char *s);main() char str=“Testing string“;int len;int (*p)();p=strlen; len=pro

19、cess(str, p);printf(“len=%dn“,len)p=toup;process(str, p);printf(“str=%sn“, str);第 9 页int process(char str, int(*p)() ) (*p) str; int toup(char *s) while (*s!=0) if (*s=a)for ( i = 0; i main() int i, a = 1, 2, 3, 4, 5 ;int *pfor ( p=a; pmain() int i, a = 1, 2, 3, 4, 5 , 6, 7, 8, 9, 0;int *p=a printf

20、(“%x“, p ); printf (“%x“, p+9);printf (“%d“, *p+9);printf (“%d“, *(p+9);printf (“%d“, *+p+9);程序运行结果:ffc6 ffd8 10 0 113、通过带下标的指针变量引用一维数组元素(1) 运算符: 不仅用作表示数组元素的记号, 而且是一种运第 11 页算符, 表示要进行变址运算, 在一个基地址上加上相对位移形成一个新地址。如: C 编译器将 ai 处理成 *(a+i), 按数组首地址加上相对位移量得到元素的地址。(2)5 种表示数组元素的方法 当 p 指向 s 数组的首地址时 ( p = s ), 数

21、组元素 si 有 5 种表示的方法: si; *(s+i); *(p+i); pi; 若 p =main( ) int a =1, 2, 3, 4, 5, 6, 7, 8, 9, 0;print(a, 5); /* 打印 a 的前 5 个元素 */ int print(int *x, int n) int *p;for (p=x; pvoid print ( int *, int );main() int a34=1,2,3,4,5,6,7,8,9,0,1,2 ;int *p = print (p, 3, 4)第 13 页void print(int *p, int m, int n) int

22、 *q = p; for ( ; q#define M 3#define N 4int max(int(*p)n,int int *,int *)main() int aMN =1,2,3,4,5,6,7,8,9,0,1,2;int row, col ,mxmx=max(a,M, printf ( “max=%d “, mx ) printf ( “row=%d “, row )printf ( “col=%d n“, col );int max (p, m, prow, pcol)int (*p)N,int m, *prow *pcol) int mx, i, j;mx= *(p0)for

23、( i = 0; i mx) mx = *(pi+j);*prow=i; *pcol =j;return (mx);运行结果:max=9 row=2 col=0三、使用指针处理字符串1、字符指针(1)定义: char *变量名;(2)用途: 经常用来指向字符数组的元素, 访问字符数组里的字符内容。最常见的情况是用字符指针指向一个字符串或字符数组。(3)初始化第 15 页 初始化方式使字符指针指向字符串如:char *str = “Programming“; 用赋值运算使指针指向一个字符串如:char *str; str = “string one“; 与第一种初始化方法完全等价。 用字符数组指

24、向字符串和用字符指针指向的字符串的区别如:char *str=“OK“; 与 char a=“OK“;含义有三点: 定义一个字符指针 str; 建立一个字符串常量; 确定指针初值,使它指向定义的字符串常量。【例 8.14 】分析下面一段程序的输出。本题是一组关于 + 和 * 运算符的练习, 目的是熟悉字符指针对字符串的控制操作。#include main() char s = “ABCDEF“;char *str = s; printf (“%c“, *str); printf (“%c“, *str+);printf (“%c“, *+str);printf (“%c“,(*str)+);

25、printf (“%cn“, +*str); printf (“%sn“, s);2、使用字符指针处理字符串和字符数组 (1)字符指针用于输入输出 字符串输出时, 输出项可以是字符串或字符数组, 也可以是指向字符串的字符指针 字符串输入时, 输入项可以是字符数组, 也可以是字符指针。字符指针必须已经指向确切的、足够大的存储空间, 以便输入字符能放在它所指的具体单元中。 (2)常规字符串处理的指针实现【例 8.15 】编写函数,计算字符指针所指字符串的长度。分析:逐个统计串中的字符个数, 直到遇到串结束符 0 为止。#include int length (char *);main() char

26、 *str1=“good Morning!“; char str2 =“good Afternon!“; char *str3=“ “;printf ( “%s len: %dn“, str1, length(str1); printf ( “%s len: %dn“, str2, length(str2);第 16 页printf ( “ str3 len: %dn“, length(str3);int lenth (char *str) int n=0;while ( *str+!=0) n+;return (n)运行结果:Good Morning! len: 13Good Afterno

27、on! len: 15str3 len: 0【例 8.16 】编写函数,实现字符串反转的操作分析:本例实现的方法是利用两个字符指针 s 和 p, 分别指向应该进行交换的两个字符, s 从字符串的首字符向末字符移动, p 从字符串的末字符向首字符移动, 直到两个指针相遇, 如果原字符串长度是奇数, 结束时 s =p, 如果原字符串长度是偶数, 结束时 s P。#include void revstr (char *);main() char str = “good Morning!“ ; /* 用数组表示 */printf ( “str = %s n“, str ) ; revstr ( str

28、 ) ;printf ( “str = %s n“, str ) ;void revstr (char *) char *p = s , c ;while ( *p) p +; /* 到结束符停止 */p - ; /* 退回最后一个有效字符 */while ( s void * scat (char * , char * );main()第 17 页 char str1 20 = “12345“ ; /* 空间要足够大 */char *str, *s2 = “abcdefg“ ;printf ( “1: %s %s n“, s1, s2 ) ; str = scat ( s1, s2 ) ;p

29、rintf ( “2: %s n“, str ) ;char *scat (char * dest, char *src ) char *p = dest ;while ( *p) p +; /* 移到第一串的结束符处 */while ( *p+ = *src+ ); /* 接在结束符处 */return (dest) ;运行结果:1: 12345 abcdefg 2: 12345abcdefg函数中的两个 while 循环巧妙地使用字符串的结束符作为结束控制。编译时有一个警告信息,是因为第二个 while 的条件中使用了赋值号 “=” 而不是关系比较符 “ ” ,确认我们确实是想用赋值号,可

30、以不理会这个警告。【例 8.18 】编写函数,实现字符串比较的操作分析:函数为 scomp ( char *s1, char *s2), 将 s1 和 s2 进行比较, 若 s1 s2, 函数返回正数;若 sl = s2, 函数返回 0;若 sl void * scomp (char * , char * );main( ) char s1 20 = “compare string“, s220 ; int result ; gets(s2) ; result = scomp (s1, s2); if ( result 0 ) printf ( “%s%s n“, s1,s2 ) ;else

31、if (result=0) printf ( “%s%s n“, s1, s2 );else printf ( “%s #includemain()void sort(); void print();char *name=“Follow me“,“BASIC“,“Great Wall“,“FORTRAN“,“Computer design“;int n=5;sort (name,n);print(name,n);void sort(char *name,int n)char *temp;int i,j,k;第 19 页for(i=0;i0) k=j;if(k!=i)temp=namei;namei=namek;namek=temp;void print(char *name,int n)int i;for(i=0;ip2三、通用类型指针ANSI 新标准增加了一种通用类型指针,定义的一般形式为:void * 变量名具体使用时要做类型转换,如:void *p; (int *)p;

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 企业管理 > 管理学资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报