1、1,指针,本章主要内容:1、指针的基本概念,变量访问方式;2、指针变量定义、赋值、引用。3、使用指针访问一维数组。4、指针作为函数参数。 5、指向数组的指针作为函数参数。,2,内存的再探讨,内存(内部存储器):是由大规模集成电路芯片组成存储器,包括RAM、ROM。运行中的程序和数据都是存放在内存中的。与内存相对的是外存,外存是辅助存储器(包括软盘、硬盘、光盘),一般用于保存永久的数据。一定要记住:程序、数据是在内存(虚拟内存是利用外存扩充内存的技术)中由CPU来执行和处理的。外存上尽管可以保存程序和数据,但是当这些数据在没有调入内存之前,是不能由CPU来执行和处理的。,3,内存地址,内存地址:
2、内存是由内存单元(一般称为字节)构成的一片连续的存储空间,每个内存单元都有一个编号。内存单元的编号就是内存地址,简称地址。CPU是通过内存地址来访问内存,进行数据存取(读/写)。,4,内存示意图,内存条,5,一组概念,变量:命名的内存空间。变量在内存中占有一定空间,用于存放各种类型的数据。 变量名:变量名是给内存空间取的一个容易记忆的名字。 变量的地址:变量所使用的内存空间的首地址。 变量值:在变量的地址所对应的内存空间中存放的数值即为变量的值或变量的内容。,6,变量示意图,7,指针、变量的指针的概念,指针:就是 “内存单元的地址”。指针指向一个内存单元。 变量的指针:就是“变量的地址”。变量
3、的指针指向一个变量对应的内存单元首地址。,8,指针变量,指针变量:就是存放地址的变量。地址(指针)也是数据,可以保存在一个变量中。保存地址(指针)数据的专用变量称为指针变量。 指针变量p中的值是一个地址值,可以说指针变量p指向这个地址。如果这个地址是一个变量i的地址,则称指针变量p指向变量i。指针变量p指向的地址也可能仅仅是一个内存地址。,9,指针示意图,10,说明,1、牢记:指针-地址。指针变量-地址变量。 2、指针变量是变量,它也有地址,指针变量的地址-指针变量的指针(指针的指针)。,11,系统访问变量的两种方式 (变量的存取方式),1、直接访问:按地址存取内存的方式称为“直接访问” 。2
4、、间接访问(使用指针变量访问变量) 。,12,直接访问,1)按变量名直接访问,按变量地址直接访问。 如:a=3; *( 从系统的角度看不管是按变量名访问变量,还是按变量地址访问变量,本质上都是对地址的直接访问。用变量名对变量的访问属于直接访问,因为编译后,变量名-变量地址之间有对应关系,对变量名的访问系统自动转化为利用变量地址对变量的访问。,13,直接访问,2)按地址直接访问(访问内存存储空间)-一般不要这么使用,这里仅为了说明概念。 如:*(int *)(2008)=5; 在地址2008保存一个整数。farptr=(char far *)0xA0000000; *(farptr+offs+i
5、1*MAXB)=*farbit; 操作显示内存。,14,间接访问 (使用指针变量访问变量),将变量a的地址(指针)存放在指针变量p中,p中的内容就是变量a的地址,也就是p指向a,然后利用指针变量p进行变量a的访问。p=&a, *p=3。 从变量名获得变量地址用“&”运算符,从地址获得地址指向的数据用“*”运算符。,15,示意图,16,指针变量的定义,定义格式 :基类型 *变量名;,例如: int *pt1, *pt2; /定义两个指针变量pt1、pt2,基类型为整型,即指向的数据类型为整型。 float *f; /定义指针变量f,基类型为浮点型,即指向的数据类型为浮点型。 char *pc;
6、/定义指针变量pc,基类型为字符型,即指向的数据类型为字符型。,17,说明,1、C语言变量先定义后使用,指针变量也不例外,为了表示指针变量是存放地址的特殊变量,定义变量时在变量名前加“*”号。 2、指针变量存放地址值,32位机用4个字节表示一个地址,所以指针变量无论指向什么类型,其本身在内存中占用的空间是4个字节。sizeof(pt1)=sizeof(f)=sizeof(pc)=4。,18,说明,3、指针变量的基类型(简称:指针变量类型):指针变量所指向数据的类型。假如:整型数据占用4个字节,浮点数据占用4个字节,字符数据占用1个字节。指针变量类型使得指针变量的某些操作具有特殊的含义。比如,p
7、t1+;不是将地址值增1,而是表示将地址值+4(指向后面一个整数)。,19,指针变量的赋值方法,1、方法一:将地址直接赋值给指针变量(指针变量指向该地址代表的内存空间) 。2、方法二:将变量的地址赋值给指针变量(指针变量指向该变量) 。,20,方法一(链表中常用),将地址直接赋值给指针变量(指针变量指向该地址代表的内存空间) 。 例如: char far *farptr=(char far *)0xA0000000; 将显存首地址赋值给指针。float *f=(float *)malloc(4); malloc动态分配了4个字节的连续空间,返回空间首地址,然后将首地址赋值给浮点型指针f。这样浮
8、点型指针f指向这个连续空间的第一个字节。,21,方法二,将变量的地址赋值给指针变量(指针变量指向该变量) 。 例如:int i,*p; p=,22,指针变量的引用,&运算符(取地址运算符):表示取变量的地址。 *运算符(指针运算符、间接访问运算符):访问指针变量指向的变量的值。 -这两个运算符都是单目运算符。,23,应用举例,例: #include “stdio.h“ int main() int i=100,j=10; /定义两个整型变量int *pi,*pj; /定义两个整型指针pi= 程序运行结果: 100,10 100,10,24,使用指针访问一维数组,数组的指针:就是数组的地址。数组
9、的地址指的是数组的起始地址(首地址),也就是第一个数组元素的地址。C语言还规定数组名代表数组的首地址。,25,举例,对于整型数组int a10; 数组的指针就是数组的起始地址&a0,也可以用数组名a表示。,26,指向数组的指针变量 (指向数组元素的指针变量),指向数组的指针变量:存放数组元素地址(初始时一般为数组首地址)的变量,称为指向数组的指针变量(简称:数组的指针变量)。,27,数组指针变量的定义和初始化,28,说明,1、数组的指针变量的定义与数组元素的指针变量的定义相同。实质就是基类型指针变量的定义。 例如:int a10,*p; 定义了一个整型数组a,如果需要定义指向该数组的指针变量就
10、要定义一个整型指针变量p。,29,说明,2、数组的指针变量的初始化可以用两种方法: (1)定义时初始化,可以使用已经定义的数组的数组名来初始化数组的指针变量。 (2)通过赋值初始化,将数组的首地址赋值给数组的指针变量(数组的指针变量的赋值也与一般的指针变量的赋值相同)。,30,举例,int a10,*p; 定义了一个整型数组a,一个整型指针变量p。 p=a; 或者p= 在定义数组的指针变量p的同时初始化指向已经定义的数组a。,31,通过指针引用数组元素,1、指针p+i的含义:不是地址值p增加i个字节后的地址值,而是指p向后移动i个基类型元素后的地址值。p-i,p+,p-都有类似的含义。 2、指
11、针与数组的关系 。,32,示意图,33,通过指针引用数组元素,两种方法:通过下标(索引)来访问数组元素的,数组元素的访问还可以通过指针完成。,34,数组元素的地址表示,假如:p定义为指向数组a的指针。数组元素ai的地址可以表示为:&ai,p+i,a+i。,35,数组元素的访问,例如:数组元素ai的访问可以是:ai,*(p+i),*(a+i)。,36,数组指针变量,数组名在许多场合甚至可以交换使用,假如:p=a,那么ai甚至可以表示为pi(指针变量带下标),37,注意,1、数组名,数组指针变量使用时的区别:数组名是常量指针,它指向数组首地址,数组指针变量是变量,它的值可以改变。在不至于混淆的场合
12、,数组名,数组指针变量可以统称数组指针。 例如:假设a、b是数组名,p是同类型的数组指针变量。 a+; *(a+); a=a+i; a=b; 错误 而p+; *(p+); p=p+i; p=a;都是可以的。,38,字符串的表示形式,1、字符数组。2、字符指针。,39,字符数组,将字符串的各个字符(包括结尾标志0)依次存放到字符数组中,利用数组名或下标变量对数组进行操作。,40,字符数组应用,#include “stdio.h“ int main( ) char string=“I am a student!“; /定义字符数组printf(“%sn“,string); /整体输出数组print
13、f(“%c,%cn“,string3,*(string+3); /输出单个字符return 0; 程序运行结果: I am a student! m,m,41,字符指针,可以不定义字符数组,直接定义指向字符串的指针变量,利用指针变量对字符串进行操作。,42,字符指针的应用,#include “stdio.h“ int main() char *string=“I am a student!”; /定义字符指针printf(“%sn”,string); /整体输出字符串printf(“%c,%cn“,string3,*(string+3); /输出单个字符return 0; 程序运行结果: I
14、am a student! m,m 其中的char *string=“I am a student!”; 等价于:char temp= =“I am a student!”,*string=temp;,43,字符数组和字符指针的区别,存储方式的区别:字符数组由若干元素组成,每个元素存放一个字符。字符指针存放的是地址(字符数组的首地址),不是将整个字符串放到字符指针变量中。,44,字符数组和字符指针的区别,赋值方式的区别: 对字符数组只能对各个元素赋值,不能将一个常量字符串赋值给字符数组(字符数组定义例外)。可以将一个常量字符串赋值给字符指针,但含义仅仅是将常量串首地址赋值给字符指针。 例如:
15、不允许:char str100; str=”I am a student.”;允许:char *pstr; pstr=”I am a student.”;,45,字符数组和字符指针的区别,定义方式的区别 :定义数组后,编译系统分配具体的内存单元(一片连续内存空间),各个单元有确切的地址。定义一个指针变量,编译系统只分配一个2字节存储单元,以存放地址值。也就是说字符指针变量可以指向一个字符型数据(字符变量或字符数组),但是在对它赋以具体地址前,它的值是随机的(不知道它指向的是什么)。所以字符指针必须初始化才能使用。,46,字符数组和字符指针的区别,运算方面的区别 :指针变量的值允许改变(+,-,
16、赋值等),而字符数组的数组名是常量地址,不允许改变。,47,指针变量作为函数的参数,指针变量作为函数参数时,同样是从实参单向传递指针变量的内容给形参,只是传递的内容是一个地址值。可以通过这个地址值间接改变实参、形参所共同指向的变量。所以尽管不能改变实际参数地址本身,但是可以间接改变地址所指向的变量。,48,应用举例,例:输入a,b;交换a,b数据后输出。,#include “stdio.h“ void swap(int a,int b)int c;c=a;a=b;b=c;printf(“2.a=%d b=%d n“,a,b); int main( )int a,b;a=3;b=8;printf
17、(“1.a=%d b=%d n“,a,b);swap(a,b);printf(“3.a=%d b=%d n“,a,b);return 0;,#include “stdio.h“ void swap(int *a, int *b)int *c;c=a;a=b;b=c;printf(“2.a=%d b=%d n“,*a,*b); int main( )int a,b;a=3;b=8;printf(“1.a=%d b=%d n“,a,b);swap(,49,正确解法,#include “stdio.h“ void swap(int *a, int *b)int c;c=*a;*a=*b;*b=c;p
18、rintf(“2.a=%d b=%d n“,*a,*b); int main( )int a,b;a=3;b=8;printf(“1.a=%d b=%d n“,a,b);swap(,8,a,b,主函数,a,b,c,Swap函数,3,3,8,3,50,说明,1、将主调函数变量的地址传递给被调用函数,就是说函数应当传递的是变量的地址。这样被调用函数的形参应当使用指针变量接受主调函数的地址值。,51,说明,2、在被调用函数中通过形参指针变量间接访问,修改实参、形参地址所共同指向的变量。本例的操作是交换两个指针变量所指向的变量。,52,数组的指针作为函数参数 可以分为4种情况,1、形参、实参都是数组名
19、。 2、实参是数组名,形参是指针变量 。 3、形参、实参都是指针变量 。 4、实参是指针变量,形参是数组名 。,实参与形参均用数组,void inv(int x, int n) int t,i,j,m=(n-1)/2;for(i=0;i=m;i+) j=n-1-i;t=xi; xi=xj; xj=t; int main( ) int i,a10=3,7,9,11,0,6,7,5,4,2;inv(a,10);printf(“The array has been reverted:n“);for(i=0;i10;i+)printf(“%d,“,ai);printf(“n“);return 0; ,
20、m=4,例 将数组a中的n个整数按相反顺序存放(1),void inv(int *x, int n) int t,*p,*i,*j,m=(n-1)/2;i=x; j=x+n-1; p=x+m;for(;i=p;i+,j-) t=*i; *i=*j; *j=t; int main( ) int i,a10=3,7,9,11,0,6,7,5,4,2;inv(a,10);printf(“The array has been reverted:n“);for(i=0;i10;i+)printf(“%d,“,ai);printf(“n“);return 0; ,实参用数组,形参用指针变量,例 将数组a中
21、的n个整数按相反顺序存放(2),void inv(int *x, int n) int t,*i,*j,*p,m=(n-1)/2;i=x; j=x+n-1; p=x+m;for(;i=p;i+,j-) t=*i; *i=*j; *j=t; int main( ) int i,a10,*p=a;for(i=0;i10;i+,p+)scanf(“%d“,p);p=a; inv(p,10);printf(“The array has been reverted:n“);for(p=a;pa+10;p+)printf(“%d“,*p);return 0; ,实参与形参均用指针变量,例 将数组a中的n个
22、整数按相反顺序存放(3),void inv(int x, int n) int t,i,j,m=(n-1)/2;for(i=0;i=m;i+) j=n-1-i;t=xi; xi=xj; xj=t; int main( ) int i,a10,*p=a;for(i=0;i10;i+,p+)scanf(“%d“,p);p=a; inv(p,10);printf(“The array has been reverted:n“);for(p=arr;parr+10;p+)printf(“%d “,*p);return 0; ,实参用指针变量,形参用数组,例 将数组a中的n个整数按相反顺序存放(4),i
23、nt *p 与 int q10 数组名是指针(地址)常量 p=q; p+i 是qi的地址 数组元素的表示方法:下标法和指针法, 即若p=q, 则 pi qi *(p+i) *(q+i) 形参数组实质上是指针变量,即int q int *q 在定义指针变量(不是形参)时,不能把int *p 写成int p; 系统只给p分配能保存一个指针值的内存区(一般4字节);而给q分配4*10字节的内存区,指针变量与一维数组的关系,58,内容回顾,指针是表示内存地址的特殊数据(内存地址是无符号整数) 指针变量是存放指针-地址这种特殊数据的变量 指针变量保存了某个变量的地址,就可以通过指针变量间接访问那个变量。
24、 数组名是指针(地址常量),指针指向数组,可使用指针名替换数组名对数组操作,并可对指针进行加减整数的操作,其含义是将指针在数组中移动若干数组元素。 基类型相同的指针变量可以相互赋值。 注意区分p和*p的含义,59,返回指针值的函数,函数可以返回整型、实型、字符型等类型的数据,还可以返回地址值-即返回指针值。返回指针值的函数定义:类型名 * 函数名(参数表)例如: int *fun(int x,int y) 表示func是返回整型指针的函数,返回的指针值指向一个整型数据。该函数还包含两个整型参数x,y。,60,应用举例,例:返回两个数中大数地址的函数。 #include “stdio.h“ in
25、t *fun(int x,int y) int *z;z= ,61,指针数组,指针数组:一个数组,如果其数组元素均为指针,那么此数组为指针数组。 一维指针数组的定义:类型名 *数组名数组长度,62,举例,例如:int *p4; 定义一个4个元素的数组p,其中每个元素是一个整型指针,也即数组p是一个4元素整型指针数组。 又如:char *p4;定义一个4个元素的字符指针数组p,其中每个数组元素是一个字符指针,可以指向一个字符串。也就是说利用此字符指针数组可以指向4个字符串。,63,指针运算举例,例:编写函数length(char *s),函数返回指针s所指字符串的长度。,64,65,常用的字符串
26、处理函数,字符串标准函数的原型在头文件string.h中。1输入字符串gets()函数 (1)调用方式:gets(字符数组) (2)函数功能:从标准输入设备(stdin)键盘上,读取1个字符串(可以包含空格),并将其存储到字符数组中去。 (3)使用说明1)gets()读取的字符串,其长度没有限制,编程者要保证字符数组有足够大的空间,存放输入的字符串。2)该函数输入的字符串中允许包含空格,而scanf()函数不允许。,66,2输出字符串puts()函数 (1)调用方式:puts(字符数组) (2)函数功能:把字符数组中所存放的字符串,输出到标准输出设备中去,并用n取代字符串的结束标志0。所以用p
27、uts()函数输出字符串时,不要求另加换行符。 ( 3)使用说明 1)字符串中允许包含转义字符,输出时产生一个控制操作。 2)该函数一次只能输出一个字符串,而printf( )函数也能用来输出字符串,且一次能输出多个。,67,3字符串比较strcmp()函数 (1)调用方式:strcmp(字符串1 ,字符串2) 其中“字符串”可以是串常量,也可以是1维字符数组。 (2)函数功能:比较两个字符串的大小。 如果:字符串1=字符串2,函数返回值等于0;字符串1字符串2,函数返回值正整数。 (3)使用说明 1)如果一个字符串是另一个字符串从头开始的子串,则母串为大。 2)不能使用关系运算符“”来比较两
28、个字符串,只能用strcmp() 函数来处理。,68,案例9 gets函数和strcmp函数的应用。 /*案例代码文件名:AL6_7.C*/ /*功能:简单密码检测程序*/ #include “stdio.h“ #include “string.h“ int main( )char pass_str80; /*定义字符数组passstr*/int i=0;/*检验密码*/while(1) printf(“请输入密码n“);gets(pass_str); /*输入密码*/,69,if(strcmp(pass_str,“password“)!=0) /*口令错*/printf(“口令错误,按任意键
29、继续n“);elseprintf(“你已通过密码验证。可以继续!n“);break;/*输入正确的密码,中止循环*/getchar();i+;if(i=3) printf(“你已三次输入错误密码。程序结束!n“);return; /*输入三次错误的密码,退出程序*/ /*输入正确密码所进入的程序段*/return 0; ,70,4拷贝字符串strcpy()函数 (1)调用方式:strcpy(字符数组, 字符串) 其中“字符串”可以是串常量,也可以是字符数组。 (2)函数功能:将“字符串”完整地复制到“字符数组”中,字符数组中原有内容被覆盖。 (3)使用说明 1)字符数组必须定义得足够大,以便容
30、纳复制过来的字符串。复制时,连同结束标志0一起复制。 2)不能用赋值运算符“”将一个字符串直接赋值给一个字符数组,只能用strcpy()函数来处理。,71,连接字符串strcat()函数 (1)调用方式:strcat(字符数组, 字符串) (2)函数功能:把“字符串”连接到“字符数组”中的字符串尾端,并存储于“字符数组”中。“字符数组”中原来的结束标志,被“字符串”的第一个字符覆盖,而“字符串”在操作中未被修改。 (3)使用说明1)由于没有边界检查,编程者要注意保证“字符数组”定义得足够大,以便容纳连接后的目标字符串;否则,会因长度不够而产生问题。2)连接前两个字符串都有结束标志0,连接后“字
31、符数组”中存储的字符串的结束标志0被舍弃,只在目标串的最后保留一个0。,72,求字符串长度strlen()函数(len是length的缩写) (1)调用方式:strlen(字符串) (2)函数功能:求字符串(常量或字符数组)的实际长度(不包含结束标志)。 7将字符串中大写字母转换成小写strlwr()函数 (1)调用方式:strlwr(字符串) (2)函数功能:将字符串中的大写字母转换成小写,其它字符(包括小写字母和非字母字符)不转换。 8将字符串中小写字母转换成大写strupr()函数 (1)调用方式:strupr(字符串) (2)函数功能:将字符串中小写字母转换成大写,其它字符(包括大写字母和非字母字符)不转换。,