ImageVerifierCode 换一换
格式:DOCX , 页数:32 ,大小:90.04KB ,
资源ID:6532284      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/6532284.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(指针是c语言的重点.docx)为本站会员(b****6)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

指针是c语言的重点.docx

1、指针是c语言的重点第七章 指针指针是c语言的重点,也是难点,没有掌握指针也就没有掌握c语言的精华。正确而熟练地掌握了指针的概念和指针的使用方法就能设计出复杂的数据结构和高效的程序。本章主要讲述指针变量、指针与数组、指针与字符串、指针与函数等内容。71 指针的概念 计算机的内存是以字节为单位的一片连续的存储空间,每一个字节都有一个编号,这个编号就称为内存地址。内存的存储空间是连续的,内存的地址号也是连续的。一个变量实质上代表了“内存中的某个存储单元”。若我们在程序中定义了一个变量,C编译系统就会根据定义的变量的类型为其分配一定字节数的内存空间(如:整型占2字节,实型占4字节,双精度占8字节,字符

2、型占1字节)。每个变量所占的存储单元都有确定的地址。具体的地址是在编译时分配的。若有定义int a,b;float x;则系统为变量a,b,x分配内存空间情况如图7_1所示 abx10111012101410151204120512061207图7_1例如:int a=3; printf(“%d”,a);要访问内存中的变量a,在程序中是通过变量名a来引用变量的值。在编译时将变量名a对应一个地址,在内存中不再出现变量名而只是地址。程序中引用变量a,系统找到其对应的地址,然后从该地址中取出其中的值。程序中我们对变量进行存取操作实际上是对某个地址的存储单元进行操作,这种直接按变量的地址存取变量值的方

3、式称为“直接访问”。在C语言中还可以定义一种特殊的变量,该变量是用来存放内存地址的,称为指针变量。如图7_2所示,假设变量p它有自己的地址(2002),若变量a的内存地址(1002)存放在变量p中,这时要访问变量a所代表的存储单元,可以先找到变量p的地址(2002),从中取出a的地址(1002),然后再去访问以1002为首地址的存储单元。这种通过变量 p间接得到变量a的地址,然后取出变量a的值的方式称为“间接访问”。打个比方来解释说明“直接访问”和“间接访问”这两种方式,假设我们用房间代表存储单元,房间的钥匙代表存储单元的地址编号,房间里的物品代表存储内容。前一种可以理解为用A房间的钥匙直接打

4、开A房间,取出所需的物品;后一种方法可以理解为A房间的钥匙放在B房间里,要想打开A房间,首先必须用B房间的钥匙打开B房间取出A房间的钥匙,然后用A房间的钥匙打开A房间,这样才能取出所需的物品。所谓“指针”就是地址,指针是地址的形象化名称,“指针”是人们形象地表示访问变量的指引关系,实际上它只是存放了一个变量的地址而已。在C语言中,指针被广泛使用,它和数组、字符串、函数间数据的传递等有着密不可分的联系。在某些场合,指针是使运算得以进行的唯一途径,同时指针的运用可以使得程序代码更简洁、效率更高。但是,若对指针的概念不清,以至滥用,将大大降低程序的可读性,使用不当,将使指针指向意料不到的地方,致使程

5、序失控,严重的将导致系统崩溃。因此,正确掌握指针的概念、正确使用指针是十分重要的。72 指针变量的定义和引用7.2.1 指针变量的定义定义指针变量的一般形式: 类型名 * 指针变量名1,* 指针变量名2,;例如:int *p ;p是用户定义的标识符,变量前的星号(*)是一个说明符,用来说明该变量是指针变量,其中变量前面的星号不可省略。上式表示定义了一个指针变量p,它指向一个整型变量。也就是说,p当中存放一个整型变量的地址且只能存放整型变量的地址,此时称int是指针变量p的“基类型”。应特别说明,定义一个指针变量必须用符号“*”,它表明其后的变量是指针变量,但不要认为“*p”是指针变量,指针变量

6、是p而不是*p。要想使一个指针变量指向一个整型变量,必须将整型变量的地址赋给该指针变量。例如:int *p , i=3;p=&i;上面首先定义了一个指针变量p和一个整型变量i,i的初值为3,此时p与i之间无任何联系,当执行了赋值语句“p=&i;”,则p中存放了变量i的地址,此时p就指向i。指针变量也可以指向实型、字符型以及其它类型的变量,例如:float * p, f=3.14; char * q , ch=A;p=&f; q=&ch;7.2.2 指针变量的引用 必须记住,指针变量只能存放地址(指针),而不要将任何非地址类型的数据赋给一个指针变量。如:p=1000;(p为指针变量,1000为整

7、数)是非法的。再举个例子:int a, *p;p=&a; *p=5;需要注意一点的是,此处的*p与定义指针变量时用的*p含义是不同的。定义“ int *p”中的“*”不是运算符,它只是表示其后的变量是一个指针类型的变量,在程序的执行语句中引用的“*p”,其中的“*”是一个指针运算符,*p表示“p指向的变量”。所以当执行了语句“*p=5;”,相当于把5赋给了p所指向的变量a,即把5赋给了a变量。若有以下语句:int *p; *p=5;虽然有时以上语句可以执行,但这种方法是危险的。因为编译时虽然分配给指针变量p一个单元,p的地址是已指定了,但p的值未指定,在p单元中是一个不可预料的值。很可能会指向

8、已存放指令或数据的内存段,造成严重后果。在C语言中有两个有关指针的运算符:1) & 运算符:取地址运算符,&x 的值为x的地址。2) * 运算符:指针运算符或指向运算符,也称间接运算符,*p代表p所指向的变量。“&”和“*”两个运算符的优先级别相同,按从右到左的方向结合。如果已执行了“p=&a;”语句,若有 &*p ,则&*p与&a相同。*&a与a等价。(*p)+相当于a+,如果没有括号,就成为*(p+),这时p不再指向a了。例7.1main()int *p1, *p2, a, b; scanf(“%d, %d” ,&a, &b );p1=&a; p2=&b;printf(“%d, %dn”

9、,a, b);printf(“%d, %dn”,*p1, *p2);p2=p1;printf(“%d, %dn”,*p1, *p2);printf(“%d, %dn” ,a, b);运行结果:10,2010,2010,2010,1010,20程序中定义了两个指针变量和两个整型变量。在输入整型变量a和b的值之后,使指针变量p1指向a,p2指向b, 然后输出a和b的值,(如图7_3(a)所示)。再输出*p1和*p2的值,也就是a和b的值。接着把p1的值(即a的地址)赋给p2,这样p1=p2=&a,即p1和p2都指向a,(如图7_3(b)所示)。此时*p1和*p2都代表a,*p2不再代表b了。例7.

10、2 交换两个指针变量main()int *p1,*p2,*p; int i1=10,i2=20;p1=&i1;p2=&i2;printf(“%d, %dn”, i1, i2);printf(“%d , %dn”,*p1,*p2);p=p1;p1=p2;p2=p;printf(“%d, %dn”, i1, i2);printf(“%d , %dn”,*p1,*p2);运行结果:10,2010,2010,2020,10第一个printf函数语句执行时,*p1就是i1,*p2就是i2。将p1与p2互换,则p1的值为&i2,p2的值为&i1,即p1指向i2,p2指向i1。*p1是i2而*p2是i1了。

11、i1和i2的值始终没有改变。程序中的指针变量p的作用是交换p1和p2的值所需的临时变量。(如图7_4所示) 例7.3 交换两个指针变量所指向的变量的值。main()int *p1,*p2,i1,i2,i;i1=10;i2=20;p1=&i1;p2=&i2;i=*p1;*p1=*p2;*p2=i;printf(“i1=%d,i2=%dn”,i1,i2);运行结果:i1=20,i2=10*p1和*p2的值互换,也就是i1和i2的值互换,而p1和p2始终指向i1和i2。(如图7_5所示) 7.3 指针变量作为函数参数函数的参数不仅可以是整型、实型、字符型等数据,还可以是指针类型。下面通过例子来说明。

12、例7.4main()void mov(int * ,int *); int x, y; mov(&x, &y); printf(“%d, %dn”, x, y);void mov(int * px, int * py)*px=10; *py=20;运行情况:10,20在main函数中没有对x和y赋值。把x与y的地址作为实参传给mov函数。mov函数的形参是指针变量,可以存放地址,因而能够接受从main函数传来的实参&x和&y。在执行mov函数时,将10和20分别送给px和py所指向的变量,即x和y。这样使得x和y得到了值。于是在main函数中就可以输出x和y的值。例7.5 对输入的两个整数按大

13、小顺序输出。main()int a, b, *p1, *p2; void swap( int * ,int *); scanf(“%d, %d”,&a, &b ); p1=&a; p2=&b; if (ab) swap(p1,p2); printf(“%d, %dn”,a,b);void swap(int * pointer1, int *pointer2)int p;p=*pointer1; *pointer1=*pointer2; *pointer2=p;运行情况:10,2020,10对程序的说明:swap是用户定义的函数,它的作用是交换两个变量的值。swap函数的两个形参pointer1

14、和pointer2是指针变量。程序开始执行时,先输入a和b的值,然后使指针变量p1和p2分别指向变量a和b。当调用swap函数时,实参p1、p2的值分别传递给形参pointer1和pointer2,即使形参pointer1和pointer2分别指向变量a和b。通过执行swap函数,使pointer1和pointer2所指向的变量交换,即a和b交换。通过上面的介绍,可以知道,使用指针变量作为函数参数可以在调用一个函数时得到多个由被调函数改变了的值。如果想通过函数调用改变n个变量的值,可采用如下方法:在主函数中设n个变量,用n个指针变量指向它们,然后将指针变量作为实参将n个变量的地址传给所调用的函

15、数的形参;通过形参指针变量改变该n个变量的值,则主调函数就可以得到改变了值的变量。不能企图通过改变指针形参的值而使得指针实参的值也改变。如对例7.5的程序的子函数swap改写如下void swap (int *p1, int *p2)int *p; p=p1; p1=p2; p2=p;则主函数调用之后,主函数中的变量a和b并没有发生改变。 7.4 指向一维数组的指针变量7.4.1一维数组指针的概念变量有地址,数组包含若干元素,每个数组元素都在内存中占用存储单元,且是连续的存储单元,也有相应的地址。指针变量可以指向变量,自然也可以指向数组和数组元素。数组元素的指针就是数组元素的地址。如:int

16、a10, *p; 其中定义a为包含10个整型数据的数组,p为指向整型变量的指针变量。下面对该指针赋值:p=&a0;把a0的地址赋给指针变量p。在C语言函数体中或函数体外部定义的数组名可以认为是一个存放地址值的指针变量名,其中的地址值是数组第一个元素的地址,也就是数组所占一串连续存储单元的起始地址;定义数组时的类型即是此指针变量的基类型;重要的是这个指针变量中的地址值不可改变,也就是说不可以给数组名重新赋值,因此可以认为数组名是一个地址常量。若在函数中有以下定义:int a10, *p, x;下面两个语句等价:p=&a0;p=a; /* 这里“p=a;”的作用是把a数组的首地址赋给指针变量p。*

17、/如果有语句a=&x;或a+; 都是非法的 ,不能给a重新赋地址值,一旦定义,a永远指向a数组的首地址。引用一个数组元素可以有两种方法。一种是下标法,即指出数组名和下标值,系统就会找到该元素,如a3就是用下标法表示的数组元素;另一种方法是地址法,即通过给出地址访问某一元素。如通过a+3地址可以找到a3元素,*(a+3)就是a3。即(1)下标法,如ai形式;(2)指针法,如*(a+i)或*(p+i).其中a是数组名,p是指向数组的指针变量,其初值p=a。例7.6 分别用下标法、地址法访问数组元素。main()int a5, i, *p; for (i=0; i5; i+)scanf (“%d”,

18、 &ai); for (i=0; i5; i+) printf (“%d”, ai); printf(“n”); for (i=0; i5; i+)printf (“%d”, *(a+i); printf(“n”); for (p=a; pa+5; p+)printf (“%d”, *p); printf(“n”);运行结果:2 4 5 7 9 2 4 5 7 92 4 5 7 9 2 4 5 7 9 可以看到用三种方法都能得到a数组各个元素的值。但第3种方法比前两种快,用指针变量指向元素,不必每次都重新计算地址,这种有规律地改变地址值(p+)能大大提高执行效率。现在着重分析第三种方法:p的初

19、值等于a,此时p指向a数组中的第一个元素a0,*p就是a0。在输出*p的值后,p+使p指向下一个元素,此时p指向a1,在输出a1的值之后,p+又使p指向下一个元素,直到p=a+5为止,此时已输出5个元素。注意,p是变量,p的值不断在变化。7.4.2数组元素地址作实参当调用函数时,数组元素可以作为实参传给形参,和普通变量一样,对应的形参必须是类型相同的变量。数组元素的值可以传送给该变量,在函数中只能对该变量进行操作,而不能直接引用对应的数组元素。当数组元素的地址作为实参时,因为是地址值,所以对应的形参也应当是基类型相同的指针变量。例7.7编写函数,对具有10个元素的字符类型数组,从下标为4的元素

20、开始,全部设置*,保持前四个元素中的内容不变。#define M 10#define B 4void setstar (char *, int );void arrout (char *, int );main()char cM=A,B,C,D,E,F,G,H,I,J; setstar( &c4, M-B); arrout (c, M);void setstar (char *p, int n )int i; for (i=0;in;i+) *(p+i)=*;void arrout (char *p, int n )int i; for (i=0;i=0) *(p+i)=x;i+;scanf

21、(“%d”,&x); return i;void arrout (int *p, int n ) int i; for (i=0; in; i+)printf(i+1)%5=0)? “%dn”, *(p+i); printf(“n”);在arrin和arrout两个函数中,都用名为p的指针变量作为形参,与主函数中的实参数组s相对应。当调用这两个函数时,指针变量p指向s数组的首地址。在函数中,表达式*(p+1)就代表了主函数中的数组元素s1,表达式*(p+i)就代表了主函数中的数组元素si,当i的值由0变化到9时,*(p+i)就表示引用了数组元素s0到s9。当然在arrin和arrout两个函数

22、中,形参可以用数组名,如两个函数定义可以改为:void arrin(int p )和void arrout(int p , int n)。实参也可以使用指针形式,但该指针变量必须事先指向已定义的数组。如上面程序的main函数可以改写成指针变量作实参的形式:main() int sM, k, *p; p=s;k=arrin (p);p=s; arrout (p, k );7.4.4 函数的指针形参和函数体中数组的区别:若有以下程序,程序中定义了fun函数,形参a指向w数组,函数体内定义了一个b数组,函数把b数组的起始地址值作为函数值返回,企图使指针p指向函数体内b数组的开头。#define N

23、10int *fun (int aN, int n ) int bN; return b ;main() int wN, *p ; p=fun (w, N ); 以上程序涉及几个概念:(1)函数fun中,形参a在形式上写作了aN,实际上它也可以写作a 或*a。但无论写成上述哪种形式,C编译程序都将其作为一个指针变量处理。在调用fun函数时,系统只为形参a开辟一个存储单元,并把main函数中的起始地址存入其中,使它指向w数组的首地址;因此,在fun函数中,凡是指针变量可以参与的运算,形参指针a同样可以参与,如:可以进行a+等操作,使它移动去指向w数组的其它元素,甚至可以通过赋值使它不再指向w数组

24、中的元素。(2)函数fun的函数体中定义了一个b数组,在调用fun函数时,系统为它开辟一串连续的存储单元,b是一个地址常量,不可以对它重新赋值;虽然对a和b,有相同的说明形式,但它们一个是作为形参的指针变量,一个是函数体内定义的数组,具有完全不同的含义。(3)在函数fun执行完毕,返回主函数时,系统将释放a和b所占存储单元,指针变量a和数组b将不再存在。因此,函数fun不应把b的值作为函数值返回,这样做,主函数中的指针变量p将不指向任何对象而成为“无向指针”。7.5 指向二维数组的指针变量7.5.1 二维数组地址的概念在C语言中定义的二维数组实际上是一个一维数组,这个一维数组的每个成员又是一个

25、一维数组。如有下面定义: int *p, a34;则可把a数组看成由a0、a1、a2三个元素组成,而a0、a1、a2每个元素又分别是由4个整型元素组成的一维数组。可用a00、a01等来引用a0中的每个元素,可用a10、a11等来引用a1中的每个元素,其它依此类推。我们知道,C语言中,在函数体中或在函数体外部定义的一维数组名是一个地址常量,其值为数组第一个元素的地址,此地址的基类型就是数组元素的类型。在以上二维数组中,a0、a1、a2都是一维数组名,同样也代表一个不可变的地址常量,其值依次为二维数组每行第一个元素的地址,其基类型就是数组元素的类型。因此,对于二维数组,象a0+这样的表达式是非法的

26、。若有表达式a0+1,表达式1的单位应当是2个字节。二维数组名同样也是一个存放地址常量的指针,其值为二维数组的首地址,也就是第0行的首地址。以上a数组,数组名a的值与a0的值相同,只是其基类型为具有4个整型元素的数组类型。即a+0的值与a0的值相同,a+1的值与a1的值相同,a+2的值与a2的值相同。它们分别表示a数组中第0行、第1行、第2行的首地址。二维数组名应理解为一个行指针。在表达式a+1中,数值1的单位应当是42个字节,而不是2个字节。赋值语句p=a;是不合法的,因为p和a的基类型不同。同样,对于二维数组名a,也不可以进行a+,a=a+i等运算。ai和*(a+i)等价,&ai或a+i指

27、向行,而ai或*(a+i)指向列。这里不要把&ai理解为ai的物理地址,因为并不存在ai这样的变量,它只是一种地址的计算方法,能得到第i行的首地址。&ai和ai的值是一样的,但它们的含义是不同的。如下表所示表示形式含义地址 a二维数组名,数组首地址 2000 a0, *(a+0), *a第0行第0列元素地址 2000 a+1第1行首地址 2008 a1, *(a+1)第1行第0列元素地址 2008a1+2, *(a+1)+2, &a12第1行第2列元素地址 2012*(a1+2),*(*(a+1)+2),a12第1行第2列元素的值元素值为13第i行第j列元素地址的表示方式ai+j ,*(a+i)+j , &aij , 第i行第j列元素值的表示方式有*(ai+j) , *(*(a+i)+j) , aij 。例7.8#define FORMAT “%d,%dn”main( )int a34=1,3,5,7,9,11,13,15,17,19,21,23; printf(FORMAT, a, *a); printf(

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1