第08章指针.ppt

上传人:本田雅阁 文档编号:2504760 上传时间:2019-04-04 格式:PPT 页数:52 大小:1.34MB
返回 下载 相关 举报
第08章指针.ppt_第1页
第1页 / 共52页
第08章指针.ppt_第2页
第2页 / 共52页
第08章指针.ppt_第3页
第3页 / 共52页
亲,该文档总共52页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述

《第08章指针.ppt》由会员分享,可在线阅读,更多相关《第08章指针.ppt(52页珍藏版)》请在三一文库上搜索。

1、第8章:指 针,学习的意义,指针是语言中广泛使用的一种数据类型。 运用指针编程是语言最主要的风格之一。 C程序设计中使用指针可以: 使程序简洁、紧凑、高效 有效地表示复杂的数据结构 例如链表 动态分配内存 得到多于一个的函数返回值 能象汇编语言一样处理内存地址,从而编出精练而高效的程序,学习指针是学习语言中最重要的一环, 能否正确理解和使用指针是我们是否掌握语言的一个标志,可以说不懂C语言中的指针就不懂什么是C语言。,8.1 指针与指针变量的概念,1、内存地址内存中存储单元的编号,教室,教室有容量,存储单元有大小(字节单元、字单元),注意:内存单元的地址与内存单元中的数据是两个完全不同的概念。

2、,2、变量地址系统分配给变量的内存单元的起始地址,程序中: int i; float k;,内存中每个字节有一个编号-地址,i,k,编译或函数调用时为其分配内存单元,变量是对程序中数据 存储空间的抽象,注意:在TC或BC下,系统将给变量i分配2字节的单元,而VC下将是4字节的单元!,3、指针与指针变量 指针:一个变量的地址 指针变量:专门存放变量地址的变量,2000,指针变量,指针,4、&与*运算符 含义,含义: 取变量的地址 单目运算符 结合性:自右向左,含义: 取指针所指向变量的内容 单目运算符 结合性:自右向左,两者关系:互为逆运算 理解,i_pointer &i &(*i_pointe

3、r) i *i_pointer *(&i),i_pointer = &i = &(*i_pointer) i = *i_pointer = *(&i),i_pointer-指针变量,它的内容是地址量 *i_pointer-指针的目标变量,它的内容是数据 &i_pointer-指针变量占用内存的地址,直接访问:按变量名来存取变量值 间接访问:通过存放变量地址的变量去访问变量,例 i = 3; -直接访问,3,例 *i_pointer = 20; -间接访问,20,8.2 指针变量的定义和引用,1、变量值的存取方法,10,例 k = i; k = *i_pointer;,-直接访问,-间接访问,2

4、、指针变量与其所指向的变量之间的关系,i,*i_pointer,&i,i_pointer,i=3;,*i_pointer=3;,3、指针变量的定义,一般形式:,存储类型 数据类型符 *变量名;,合法标识符,表示定义指针变量 不是*运算符,指针的目标变量的数据类型,指针变量本身的存储类型,注意: int *p1, *p2; 与 int *p1, p2; 指针变量名是p1,p2 ,不是*p1,*p2 指针变量只能指向定义时所规定类型的变量 指针变量定义后,变量值不确定,应用前必须先赋值,例 int *p1, *p2; float *q; static char *name;,例 int i; in

5、t *p = ,例 void main ( ) int i; static int *p = (),不能用auto变量的地址 去初始化static型指针,4、指针变量的赋值,初始化赋值,存储类型 数据类型 *指针名 = 初始地址值;,赋给指针变量, 不是赋给目标变量,变量必须已说明过 类型应一致,例 int i; int *p = ,用已初始化指针变量作初值,例 int a; int *p; p = ,赋值语句赋值,例,int a = 20 ;,int *p, *q;,p = ,q = p;,20,2000,2000,例 int *p = ,指针变量赋值的几种错误方法:,变量a的定义在后,对a

6、的引用超出了a的作用域,例 int a; int *pi = ,pc不能指向非字符型变量,例 int a; int *p; *p = ,赋值语句中,被赋值的指针变量p的前面不能再加“*”说明符,例 int *p; p = 2000;,不允许直接把一个数赋值给指针变量,例 int a; static int *p = ,不能用auto变量的地址去初始化static型指针,注意:一个指针变量只能指向同类型的变量如果给指针赋值时,=号右边的指针类型与左边的指针类型不同,则需要进行类型强制转换。 int a; int *pi; char *pc; pi = /pc也指向了a,即pi和pc的值都是a的地

7、址,5、零指针与空类型指针 零指针:(空指针) 定义: 指针变量值为零 表示: int * p = 0;,p指向地址为0的单元, 系统保证该单元不作它用 表示指针变量值没有意义,#define NULL 0 int *p = NULL:,p = NULL与未对p赋值不同 用途: 避免指针变量的非法引用 在程序中常作为状态比较,例 int *p; while (p != NULL) . ,void *类型指针 表示: void *p; 使用时要进行强制类型转换,表示不指定p是指向哪一种 类型数据的指针变量,例 char *p1; void *p2; p1=(char *)p2; p2=(void

8、 *)p1;,6、引用指针变量,int a; int *p = ,格式: *指针变量,int a, *p; p = ,输出结果: a = 11, *p = 11,可写成(*p)+,而不是*p+,注意:程序在利用指针间接引用内存单元时,将按照指针变量定义时所指向的数据类型来解释引用的内存单元。,【例1】不同类型的指针操作同一内存变量,#include void main ( ) unsigned short a; unsigned short *pi = ,2000,2000,pc可操作单元,F0,F0,00,输出结果: a = F000,【例2】输入两个数,并使其从大到小输出,#include

9、 void main ( ) int *p1,*p2,*p, a, b; scanf (“%d,%d“, ,运行结果: a=5, b=9 max=9, min=5,.,.,5,2006,9,2008,2006,2008,2006,重点强调: 指针变量必须先定义,后赋值,最后才能使用!没有赋值的指针变量是没有任何意义的,也绝对是不允许使用的。 指针变量只能指向定义时所规定类型的变量。 指针变量也是变量,在内存中也要占用一定的内存单元,但所有类型的指针变量都占用同样大小的内存单元,其具体大小取决于所使用的编译环境,如在BC3.1和VC6.0下为4个字节,在TC2.0下为2个字节。,1、指针变量的加

10、、减运算,8.3 指针和地址运算,指针可以参与加法和减法运算,但其加、减的含义绝对不同于一般数值的加减运算。如果指针p是这样定义的:ptype *p;,并且p当前的值是ADDR,那么:,p n 的值 = ADDR n * sizeof(ptype),int *pi; char *pc; long *pl; pi = (int *) 1000; pc = (char *) 1000; pl = (long *) 1000;,pi+; /pi的值将是1002 (假设int型占2byte) pi -= 2; / pi的值将是998 pc+; / pc的值将是1001 pc -= 2; / pc的值将

11、是999 pl+; / pl的值将是1004 pl -= 2; / pi的值将是996,注意:两个指针相加没有任何意义,但两个指针相减则有一定的意义,可表示两指针之间所相差的内存单元数或元素的个数,在后面的学习中就会体会到。,2、指针变量的关系运算,若p1和p2指向同一数组,则 p1p2 表示p1指的元素在后 p1=p2 表示p1与p2指向同一元素 若p1与p2不指向同一数组,比较无意义 p=NULL或p!=NULL,1、数组的指针,8.4 指针与数组,数组的指针其实就是数组在内存中的起始地址。而数组在内存中的起始地址就是数组变量名,也就是数组第一个元素在内存中的地址。,例:short int

12、 a10;,int a10; int k; for (k = 0; k 10; k+) ak = k; /利用数组下标,int a10; int k; for (k = 0; k 10; k+) *(a+k) = k; /利用数组的指针,2、指向数组的指针变量,8.4 指针与数组,如果将数组的起始地址赋给某个指针变量,那么该指针变量就是指向数组的指针变量。,例:short int a10, p = a;,注意:p + 1指向数组的下一个元素,而不是简单地使指针变量p的值+1。其实际变化为p+1*size(size为一个元素占用的字节数)。例如,假设指针变量p的当前值为2000,则p+1为200

13、0+1*2=2002,而不是2001。,char str10; int k; for (k = 0; k 10; k+) strk = A + k; /也可写成*(str+k) = A + k,char str10; int k; char *p; p = str; for (k = 0; k 10; k+) pk = A + k; /也可写成*(p+k) = A + k,char str10; int k; char *p; p = str; for (k = 0; k 10; k+) *p+ = A + k; /相当于 *p = A + k; p+;,下面是对数组元素赋值的几种方法,它们从

14、功能上是等价的,执行完后,p仍然指向数组str的首地址,执行完后,p指向数组元素str9的下一内存单元,注意:数组名是地址常量,切不可对其赋值,也不可做+或-运算。例如:int a10;如果在程序中出现a+或a-则是错误的。,【例】数组元素的引用方法,void main( ) int a5, *pa, i; for (i = 0; i 5; i+) ai=i+1; pa = a; for (i = 0; i 5; i+) printf (“*(pa+%d):%dn“, i, *(pa+i); for (i = 0; i 5; i+) printf (“*(a+%d):%dn“, i, *(a+

15、i); for (i = 0; i 5; i+) printf (“pa%d:%dn“, i, pai); for (i = 0; i 5; i+) printf(“a%d:%dn“, i, ai); ,例: int a = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 , *p = a, i; 数组元素地址的正确表示: (A)&(a+1) (B)a+ (C)&p (D)&pi,数组名是地址常量 p+,p- () a+,a- () a+1, *(a+2) (),例:注意指针变量的运算,void main() int a = 5, 8, 7, 6, 2, 7, 3; int y,

16、*p = ,6,输出结果: 5 6,void main ( ) int i, *p, a7; p = a; for (i = 0; i 7; i+) scanf (“%d“, p+); printf (“n“); for (i = 0; i 7; i+, p+) printf (“%d“,*p); ,例 注意指针的当前值,p=a;,指针变量可以指到数组后的内存单元,8.5 指针与字符串 1、字符串表示形式 用字符数组实现,例: void main ( ) char string = “I love China!”; printf (“%sn”, string); printf (“%sn”,

17、string + 7); ,运行结果: I love China! China!,用字符指针实现,例: void main ( ) char *string = “I love China!”; printf (“%sn”, string); string += 7; while (*string) putchar (string0); string+; ,字符指针初始化:把字符串首地址赋给string char *string; string = “I love China!”;,*string != 0,运行结果: I love China! China!,2、字符指针变量与字符数组 ch

18、ar *cp; 与 char str20; str由若干元素组成,每个元素放一个字符;而cp中存放字符串首地址 char str20; str=“I love China!”; () char *cp; cp=“I love China!”; () str是地址常量;cp是地址变量 cp接受键入字符串时,必须先开辟存储空间,例 char str10; scanf(“%s”,str); () 而 char *cp; scanf(“%s”, cp); (),改为: char *cp, str10; cp = str; scanf (“%s”,cp); (),3、字符串与数组关系 字符串用一维字符数

19、组存放 字符数组具有一维数组的所有特点 数组名是指向数组首地址的地址常量 数组元素的引用方法可用指针法和下标法 数组名作函数参数是地址传递等 区别 存储格式:字符串结束标志 赋值方式与初始化 输入输出方式:%s %c,char str=“Hello!”; () char str=“Hello!”; () char str=H,e,l,l,o,!; () char *cp=“Hello”; () int a=1,2,3,4,5; () int *p=1,2,3,4,5; (),char str10,*cp; int a10, *p; str=“Hello”; () cp=“Hello!”; ()

20、 a=1,2,3,4,5; () p=1,2,3,4,5; (),scanf(“%s”,str); printf(“%s”,str); gets(str); puts(str);,4、字符指针变量使用注意事项,当字符指针指向字符串时,除了可以被赋值之外,与包含字符串的字符数组没有什么区别。,char str10, *pstr; pstr = “12345“; /pstr指向“12345“ strcpy (str, pstr); /将pstr所指向的字符串复制到数组str中 pstr = str; printf (“The Length of str is: %dn“, strlen(pstr)

21、; /输出字符串的长度5,注意“野指针”操作:,如果一个指针没有指向一个有效内存就被引用,则被称为“野指针”操作或空指针赋值。野指针操作尽管编译时不会出错,但很容易引起程序运行时表现异常,甚至导致系统崩溃。,char *pstr; char str8; scanf (“%s“,pstr); /野指针操作,pstr没有指向有效内存 strcpy (pstr, “hello“); /野指针操作 pstr = str; /pstr指向数组str所对应内存单元的首地址 strcpy (pstr, “0123456789“); /不是野指针,但会造成数组越界,为什么“野指针”操作会给程序运行带来极大的不

22、确定性,甚至造成系统崩溃呢?,pstr 0001,指针pstr所占内存,char *pstr; pstr = a;,极其危险!,为什么“野指针”赋值会给程序运行带来极大的危险?,再次提醒: 指针变量只有与内存建立联系以后才可使用,否则将造成程序运行异常,甚至导致系统死机!,【例】 利用字符指针实现字符串的倒序排列,#include #include void main ( ) char str200, ch; char *p, *q; gets (str); /读取一个字符串 p = str; /p指向字符串的首地址 q = p + strlen(p) - 1; /q指向字符串的末地址 whi

23、le (p q) /交换p和q各自指向的字符 ch = *p; /将p所指向的字符保存在ch中 *p+ = *q; /先将q指向的字符赋给p指向的字符单元,p再增1 *q- = ch; /先将ch的值赋给q指向的字符单元,q再减1 printf (“%sn“, str); ,运行结果: I love China! !anihC evol I,p = str;,q = p + strlen(p) - 1,!,I,a,n,L,i,o,h,v, ,C,e, ,End,程序执行过程演示:,8.6 指针与动态内存分配,1、静态内存分配,当程序中定义变量或数组以后,系统就会给变量或数组按照其数据类型及大小

24、来分配相应的内存单元,这种内存分配方式称为静态内存分配 。,int k; /系统将给变量k分配2个字节(VC下分配4个字节)的内存单元 char ch10; /系统将给这个数组ch分配10个字节的内存块,首地址就是ch的值,静态内存分配一般是在已知道数据量大小的情况下使用,例如,要对10个学生的成绩按降序输出,则可定义一个数组:int score10; 用于存放10个学生的成绩,然后再进行排序。,如果事先并不知道学生的具体人数,编写程序时,人数由用户输入,然后再输入学生的成绩。那有如何如何处理呢?,int n; int scoren; scanf (“%d“, ,如何解决?,动态内存分配,2、

25、动态内存分配,所谓动态内存分配是指在程序运行过程中,根据程序的实际需要来分配一块大小合适的连续的内存单元。 程序可以动态分配一个数组,也可以动态分配其它类型的数据单元。动态分配的内存需要有一个指针变量记录内存的起始地址。 C语言中动态内存分配其实就是使用一个标准的库函数malloc,其函数的原型为:,void *malloc( unsigned int size );,说明: size这个参数的含义是分配的内存的大小(以字节为单位)。 返回值:失败,则返回值是NULL(空指针)。 成功,则返回值是一个指向空类型(void)的指针 (即所分配内存块的首地址)。,2、动态内存分配,int n, *

26、pscore; scanf (“%d“, /可对pscore所指向的单元进行其它处理,例如:根据学生人数来建立数组的问题可以用动态内存分配来解决,其方法如下:,pscore 0100,共n*sizeof(int)个 字节内存单元,关于malloc的使用有几点需强调一下: malloc前面必须要加上一个指针类型转换符,如前面的(int *)。因为malloc的返回值是空类型的指针,一般应与右边的指针变量类型一致。 malloc所带的一个参数是指需分配的内存单元字节数,尽管可以直接用数字来表示,但一般写成如下形式: 分配数量*sizeof(内存单元类型符) malloc可能返回NULL,表示分配内

27、存失败,因此一定要检查分配的内存指针是否为空,如果是空指针,则不能引用这个指针,否则会造成系统崩溃。所以在动态内存分配的语句的后面一般紧跟一条if语句以判断分配是否成功。,3、动态内存释放,计算机中最宝贵的资源就是内存。因此需要动态分配内存的程序一定要坚持“好借好还,再借不难”的原则。 释放动态内存的函数free其原型为:,void free (void *block);,例: free( pscore );,注意: 调用malloc和free函数的源程序中要包含stdlib.h或malloc.h或alloc.h(在TC、BC下)。malloc和free一般成对出现!,【例】 编写程序先输入学

28、生人数,然后输入学生成绩,最后输出学生的平均成绩、最高成绩和最低成绩。,#include #include #include void main ( ) int num, i; int maxscore, minscore, sumscore; int *pscore; float averscore; printf (“input the number of student: “); scanf (“%d“, ,printf (“input the scores of students now:n“); for (i = 0; i maxscore) maxscore = pscorei;

29、if (pscorei minscore) minscore = pscorei; sumscore = sumscore + pscorei; averscore = (float)sumscore / num;,printf (“-n“); printf (“the average score of the students is %.1fn“, averscore); printf (“the highest score of the students is %dn“, maxscore); printf (“the lowest score of the students is %dn

30、“, minscore); free (pscore); /释放动态分配的内存 ,运行结果: input the number of student:4 input the scores of students now: 45 76 88 94 - the average score of the students is 75.8 the highest score of the students is 94 the lowest score of the students is 45,8.7 指针作为函数的参数,参数传递方式:传值调用和传址调用 传值调用:将参数值传递给形参。实参和形参占用各

31、自的内存单元,互不干扰,函数中对形参值得改变不会改变实参的值,属于单向数据传递方式。 传址调用:将实参的地址传递给形参。形参和实参占用同样的内存单元,对形参值得改变也会改变实参的值,属于双向数据传递方式。,运行结果:b = 0,运行结果:b = 5,为什么结果不一样呢?,void swap (int x, int y) int temp; temp = x; x = y; y = temp; void main ( ) int a, b; scanf(“%d,%d“, ,【例】将数从大到小输出,5,9,5,5,9,COPY,void swap (int x, int y) int temp;

32、temp = x; x = y; y = temp; void main ( ) int a, b; scanf(“%d,%d“, ,【例】将数从大到小输出,5,9,运行结果:5, 9,值传递,void swap(int *p1, int *p2) int p; p = *p1; *p1 = *p2; *p2 = p; void main ( ) int a, b; int *p_1, *p_2; scanf (“%d,%d“, ,5,9,2000,2002,5,9,COPY,5,【例】将数从大到小输出,void swap(int *p1, int *p2) int p; p = *p1; *

33、p1 = *p2; *p2 = p; void main ( ) int a, b; int *p_1, *p_2; scanf (“%d,%d“, ,5,9,2000,2002,5,9,【例】将数从大到小输出,运行结果:9, 5,地址传递,void swap (int *p1, int *p2) int *p; p = p1; p1 = p2; p2 = p; void main ( ) int a, b; int *p_1, *p_2; scanf (“%d,%d“, ,运行结果:5,9,5,9,2000,2002,COPY,2000,地址传递,2000,2002,【例】将数从大到小输出,

34、例. A班有m (m float fun(int *p,int t); / float fun(int p,int t);,void main( ) int a20,b20,m,n,i; float avg1,avg2,sub; scanf(“%d“, ,float fun(int *p, int t) float ave,sum=0; int i; for (i=0;it;i+) sum+=pi; ave=sum/t; return ave; ,8.11 带参数的main函数,命令行 在操作系统状态下,为执行某个程序而键入的一行字符,命令行一般形式,命令名 参数1 参数2 参数3 参数n,参

35、数之间以一个或多个空格隔开,例如:C:copy.exe source.cpp c:bakprg.cpp,有3个字符串参数的命令行,带参数的main函数的形式,void main(int argc, char *argv ) ,命令行中参数个数,形参名任意,元素指向命令行参数 中各字符串首地址,argc和argv与命令行参数之间的对应关系,命令行参数的传递,第一个参数: main所在的可执行文件名,【例1】输出命令行参数,/test.cpp void main(int argc, char *argv) while (argc 1) +argv; printf(“%sn“,*argv); -ar

36、gc; ,void main(int argc, char *argv) while (argc- 0) printf (“%sn“, *argv+); ,1. 编译、链接test.cpp,生成可执行文件test.exe 2. 在DOS状态下运行(test.exe所在路径下),例如: C:TC test.exe hello world!,运行结果:hello world!,运行结果:test hello world!,本章小结,1. 指针是语言中一个重要的组成部分,使用指针编程有以下优点: 提高程序的编译效率和执行速度。 通过指针可使用主调函数和被调函数之间共享变量或数据结构,便于实现双向数据

37、通讯。 可以实现动态的存储分配。 便于表示各种数据结构,编写高质量的程序。,2. 指针的运算 取地址运算符&:求变量的地址 取内容运算符*:表示指针所指的变量 赋值运算,把变量地址赋予指针变量 同类型指针变量相互赋值 把数组,字符串的首地址赋予指针变量 把函数入口地址赋予指针变量,本章小结,加减运算 对指向数组,字符串的指针变量可以进行加减运算,如p + n,p n,p+,p-等。对指向同一数组的两个指针变量可以相减。对指向其它类型的指针变量作加减运算是无意义的。,关系运算 指向同一数组的两个指针变量之间可以进行大于、小于、等于比较运算。指针可与0比较,p = 0表示p为空指针。,本章小结,3. 与指针有关的各种说明和意义见下表,

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

当前位置:首页 > 其他


经营许可证编号:宁ICP备18001539号-1