《C语言程序设计——指针(完整版)课件.ppt》由会员分享,可在线阅读,更多相关《C语言程序设计——指针(完整版)课件.ppt(80页珍藏版)》请在三一办公上搜索。
1、第九章,指针,9.1地址和指针的概念,为了说清楚什么是指针,必须弄清楚数据在内存中是如何存储的,又是如何读取的。,计算机中,所有的数据都是存放在存储器的内存单元中的。一般把存储器中一个字节称为一个内存单元,不同数据类型所占用的内存单元数不等,如整型变量占2个字节,字符变量占1个字节等。为了正确地访问这些内存单元,必须为每个内存单元编上号,根据一个内存单元的编号即可准确地找到该内存单元。内存单元的编号,称为“地址”。,内存单元的地址和内存单元的内容是两个不同的概念:假设程序中定义了整型变量i,并赋值为3。编译时系统分配2000和2001两个字节给变量i,此时两个内存单元的编号为单元的地址,其中存
2、放的数据即是该单元的内容。,两种访问方式:,直接访问方式:在程序中一般是通过变量名来对内存单元进行存取操作的,其实程序在编译后,已经将变量名转换成变量的地址,对变量值的存取都是通过地址进行的。,间接访问方式:将变量i的地址存放在另一个变量中,C语言规定,可以在程序中定义一种特殊的变量,用来存放地址。假设定义一个变量p,用来存放整型变量的地址,它被分配为3010、3011两个字节。,可以通过下面语句将i的地址(2000)存放到p:p=对变量i是访问,可直接访问,已知变量i的地址,根据此地址直接对变量i的存储单元进行存取访问;也可间接访问,先找到存放变量i的地址的变量p,从其中得到变量i的地址,然
3、后找到变量i的存储单元,对它进行存取范围。,一个变量的地址称为该变量的“指针”。如,地址2000是变量i的指针。C语言中,用一个变量存放指针(地址),此变量称为指针变量,上述的变量p是指针变量,指针变量的值是某个内存单元 的地址(指针)。严格地说,一个指针是一个地址,是一个常量;而一个指针变量却可以被赋予不同的指针值,是变量。但通常把指针变量简称指针。,指针变量的值不仅可以是变量的地址,也可以是其他数据结构的地址。例如:在一个指针变量中可存放一个数组或一个函数的首地址。,9.2 变量的指针和指向变量的指针变量,区别:变量的指针和指针变量1.变量的指针就是变量的地址。2.存放变量地址的变量是指针
4、变量,它用来指向另一个变量。,为了表示指针变量和它所指向的变量之间的联系,在程序中用“*”符号表示“指向”。若已定义i_pointer为指针变量,则*i_pointer是i_pointer所指向的变量,因此*i_pointer也代表一个变量,它和变量i是同一回事。,下面两个语句作用相同:(1)i=3;(2)*i_pointer=3;第二个语句含义是将3赋给指针变量i_pointer所指向的变量i.,一、指针变量的定义,指针变量在使用前必须先定义,一般形式为:类型说明符*指针变量名;其中,*表示这是一个指针变量,类型说明符 表示该指针变量所指变量的数据类型。例如:int*p1;(其中p1(而不是
5、*p1)是一个指向整型变量的指针变量,它的值是某个整型变量的地址。)float*p2;(其中p2是指向浮点型变量的指针变量),说明:,(1)指针变量名的构成原则是标识符,前面必须有“*”,表示该变量的类型是指针型变量。(2)在一个定义语句中,可以同时定义普通变量、数组、指针变量。(3)类型说明符说明的数据类型不是指针变量中存放的数据的数据类型,而是它将要指向的变量或数组的数据类型。因此,一个指针变量只能用来指向同种数据类型的其他变量或数组,不能时而指向一个浮点型变量,时而指向一个整型变量。,二、指针变量的引用,指针变量中只能存放变量的地址,绝不能存放任何其他数据。例如:int*p;p=2000
6、;/*2000是一个整数而不是一个地址*/,C语言提供两种有关的运算符:(1)&:取地址运算符(用来表示变量的地址)(2)*:取内容运算符(用来取其指向的内容,或称“间接 访问”运算符,或称指针运算符)两种运算符都是单目运算符,其结合性都为自右向左,优先级别相同。例如:&a为变量a的地址,*p为指针变量p所指向的内存单元的内容(即p所指向的变量的值)。,说明:,(1)设有指向整型变量的指针变量p,若要把整型变量a的地址赋予p,有以下方式:指针变量初始化的方法:int a;int*p=(2)指针运算符*和指针变量说明中的指针说明符*不是一回事。后者表示其后的变量是指针类型,前者则是一个运算符用以
7、表示指针变量所指的变量。,说明:,(3)如果已执行了语句p=,则:&*p:先进行*p的运算,即是变量a,再执行&运算,即变量a的地址。因此&*p与&a相同。*&a:先进行&a的运算,即得a的地址,再执行*运 算,即&a所指向的变量,即变量a。因此*&a与a相同。,#include void main()int a,b;int*p1,*p2;a=100;b=10;p1=,例:通过指针变量访问整型变量。,运行结果:100,10100,10,#include void main()int a=5,b,c,*p;p=,例:通过指针变量进行运算。,运行结果:5,10,说明:,(1)指针变量的运算种类也是
8、有限的,它只能进行赋值运算和部分算术运算及关系运算。(2)指针变量还可以赋空值,其赋空值后,则可以使用,只是它不指向具体的变量而已。例如:#define NULL 0 int*p=NULL;,三、指针变量作函数参数,#includeswap(int x,int y)int t;t=x;x=y;y=t;void main()int a=3,b=5;if(ab)swap(a,b);printf(“a=%d,b=%d n”,a,b);,C语言是通过传值将参数传递给函数的,对被调函数来说,没有直接的方法来改变主调函数内的变量的值。例:对两个整数按大小顺序输出。,运行结果:a=3,b=5,#includ
9、eswap(int*x,int*y)int t;t=*x;*x=*y;*y=t;void main()int a=3,b=5;if(ab)swap(,使主调函数传递指向要被改变量的指针,即可使被调函数能改变主调函数的参数值。例:对两个整数按大小顺序输出。,运行结果:a=5,b=3,数组与指针,(苏小红版第11章的11.1和11.2),一个数组是由连续的一块内存单元组成的,数组名即是这块连续内存单元的首地址。一个变量有地址,一个数组包含若干元素,每个数组元素都在内存中占用存储单元,它们都有相应的地址。指针变量既然可以指向变量,当然也可以指向数组元素(把某一元素的地址放到一个指针变量中)。所谓数组
10、元素的指针就是数组元素的地址。,指向数组的指针变量称为数组指针变量。其一般形式为:类型说明符*指针变量名;其中,类型说明符表示所指数组的类型。从中可以看出指向数组的指针变量和指向普通变量的指针变量的说明是相同的。,一、指向数组元素的指针,说明:,设有实型数组a,指向a的指针变量为pa,则有以下关系:pa,a,&a0均指向同一内存单元,它们是数组a的首地址,即是a0的首地址;pa+1,a+1,&a1均指向元素a1的地址;pa+i,a+i,&ai均指向元素ai的地址。*(pa+i),*(a+i)是pa+i 或a+i 所指向的数组元素,即ai注意:pa是变量,a与&ai都是常量(地址)。,引入指针变
11、量后,可以用两种方法来访问数组元素:1.下标法:采用ai形式访问数组元素:2.指针法:采用*(pa+i)或*(a+i)形式,用间接访问的 方法来访问数组元素,即通过指向数组元 素的指针找到所需的元素。,例:(1)int a;(2)int a;初始化赋值 int*p=,按C语言的规定:如果指针p已指向数组中的一个元素,则p+1指向同一数组中的下一个元素,而不是将p的值(地址值)简单地加1。例如:数组元素是float型,每个元素占4个字节,则p+1意味着使p的值(地址值)加4个字节,以使它指向下一个元素,此时,p+1所代表的地址实际上是p+1 d,d是一个数组元素所占的字节数。,二、通过指针引用数
12、组元素,(1)下标法#include void main()int a10,i;for(i=0;i10;i+)scanf(“%d”,例:输出数组中的全部元素。(设一个a数组,整型,有10个元素,要输出各元素的值有3种方法),(2)通过数组名计算数组元素 的地址,找出元素的值 for(i=0;i10;i+)printf(“%d”,*(a+i);(3)用指针变量指向数组元素 for(p=a;p(a+10);p+)printf(“%d”,*p);,#include void main()int a10,i,*p;p=a;for(i=0;i10;i+)scanf(“%d”,p+);printf(“n”
13、);for(i=0;i10;i+,p+)printf(“%d”,*p);printf(“n”);,例:(改错题)通过指针变量输出a数组中的10个元素。,运行结果:1 2 3 4 5 6 7 8 9 0 22153 234 0 0 30036 25202 11631 8529 8237 28483,1.算术运算 只能进行加减运算,数组指针变量加或减一个整数n的意义是把指针指向的当前位置(指某数组元素)向前或向后移动n个位置。例如:int a5,*pa;pa=a;/*pa指向a0,pa的值为/*pa指向a2,pa的值为&a2*/,数组指针变量的运算:,2.两个指针变量之间的运算 只有指向同一数组的
14、两个指针变量之间才能进行运算,否则运算毫无意义。(1)两指针变量相减两指针变量相减所得之差是两个指针所指数组元素之间相差的元素个数。两指针变量不能进行加法运算。(2)两指针变量进行关系运算指向同一数组的两个指针变量进行关系运算是表示它们所指数组元素之间的关系。例如:pa=qa表示pa和qa指向同一数组元素;qapa表示qa处于高地址位置;pa处于低地址位置;,#include void main()int a5,i,*pa,*qa;pa=qa=a;for(i=0;i5;i+)*qa=i;qa+;/*将指针qa指向a数组中的下一元素*/for(i=0;paqa;i+)printf(“a%d=%-
15、2d”,i,*pa);pa+;,例:通过指针变量的运算输出a数组中的5个元素。,运行结果:a0=0 a1=1 a2=2 a3=3 a4=4,2.指针变量的自增自减运算如果先使p指向数组a的首元素(即p=a),请分析:(1)p+:使p指向数组a的下一元素,即a1。(2)*p+:由于元素符+和*同优先级,结合方向为自 右向左,因此它等价于*(p+)。(3)*(p+)与*(+p)作用不同。(4)(*p)+:表示p所指向的元素值加1,它等价于(a0)+。若a0=3,则(*p)+的值为4。注意:是元素值加1,而不是指针值加1。,前面已介绍过可以用数组名作函数的参数,例如:void f(int arr,i
16、nt n)void main()int array10;f(array,10);f(int arr,int n)但在编译时是将arr按指针变量处理的,相当于将函数f的首部写成f(int*arr,int n)以上两种写法是等价的。,三、用数组名和数组指针变量作函数参数,以变量名和数组名、数组指针变量作为函数参数的比较:,归纳起来,如果有一个实参数组,想在函数中改变此数组中的元素的值,实参与形参的对应关系有以下种情况:,(1)形参和实参都用数组名,如:void main()void(int,int)int;(,);,(2)实参用数组名,形参用指针变量。如:void()void(int*,int)i
17、nt;(,);,(3)实参形参都用指针变量。例如:void main()void(int*,int)int,*p=a;(p,);,(4)实参为指针变量,形参为数组名。如:void main()void(int x,int),*p=a;(p,);,#include void main()void inv(int*x,int n);int i,a10=3,7,9,11,0,6,7,5,4,2;printf(“The original array:n”);for(i=0;i10;i+)printf(“%d”,ai);printf(“n”);inv(a,10);printf(“The array ha
18、s been inverted:n”);for(i=0;i10;i+)printf(“%d”,ai);printf(“n”);,例:将a数组中n个整数按相反顺序存放。,void inv(int*x,int n)int*p,temp,*i,*j,m=(n-1)/2;i=x;j=x+n-1;p=x+m;for(;i=p;i+,j-)temp=*i;*i=*j;*j=temp;return;,运行结果:The original array:3,7,9,11,0,6,7,5,4,2The array has been inverted:2,4,5,7,6,0,11,9,7,3,四、多维数组与指针,用指
19、针变量可以指向一维数组中的元素,也可以指向多维数组中的元素。下面以二维数组为例。,1.多维数组元素的地址的表示方法,定义int a34=1,2,3,4,5,6,7,8,9,10,11,12;C语言允许把一个二维数组分解为多个一维数组来处理。a是一个数组名,a数组包含3个元素:a0,a1,a2,而每一个元素又是一个一维数组,包含4个元素(即4个列元素)。如行元素a0所代表的一维数组包含4个元素:a00,a01,a02,a03。故可以认为二维数组是“数组的数组”,即二维数组a是由3个一维数组所组成的。,从二维数组的角度看,a代表二维数组首元素的地址。现在的首元素不是一个简单的整型元素,而是由4个整
20、型列元素所组成的一维数组,因此a代表的是首行(0行)的首地址;a+1代表1行的首地址;a+2代表2行的首地址。如果二维数组首行的首地址是2000,则a+1的首地址为a+4*2=2008。,a0,a1,a2是一维数组名,而C语言规定了数组名代表数组首元素地址,因此a0代表一维数组a0中第0列元素的地址,即二维数组0行0列元素的地址,即&a00;a1的值是&a10;a2的值是&a20。a0+0,a0+1,a0+2,a0+3分别是&a00,&a01,&a02,&a03。,由于a0与*(a+0)等价,由于ai与*(a+i)等价,因此 a0+1、*(a+0)+1都是&a01,a1+2、*(a+1)+2都
21、是&a12。如何用地址法表示&a01的值?由于a0+1、*(a+0)+1都是a01的地址,则*(a0+1)、*(*(a+0)+1)、*(*a+1)都是a01的值,2.指向多维数组的指针变量,(1)指向数组元素的指针变量,#include void main()int a34=1,3,5,7,9,11,13,15,17,19,21,23;int*p;for(p=a0;pa0+12;p+)if(p-a0)%4=0)printf(“n”);printf(“%4d”,*p);printf(“n”);,分析:指针变量p是用“int*p;”定义的,它是指向整型数据的,p+1所指向的元素是p所指向的元素的下
22、一元素。,(2)指向由m个元素组成的一维数组的指针变量,此时p不是指向整型变量,而是指向一个包含m个元素的一维数组。如果p先指向a0,则p+1是指向a1,p的增值以一维数组的长度为单位。,二维数组指针变量说明的一般形式为:类型说明符(*指针变量名)长度;其中,“类型说明符”是所指数组的数据类型;“*”表示其后的变量是指针类型;“长度”表示二维数组分解为多个一维数组时,一维数组的长度,即是二维数组的列数。注意:(*指针变量名)两边的括号不能省,如缺少括号则表示指针数组。,#includevoid main()int a34=1,3,5,7,9,11,13,15,17,19,21,23;int(*
23、p)4,i,j;p=a;scanf(“i=%d,j=%d”,例:输出二维数组任一行任一列元素的值。,运行结果:i=1,j=2a1,2=13,字符串与指针,(苏小红版第10章的10.3和10.7),一、字符串的表示形式,对指向字符变量的指针变量应赋予该字符变量的地址例如:char ch,*pch=其中pch是一个指向字符变量ch的指针变量。,在C语言中,可以用两种方法访问一个字符串:1.用字符数组存放一个字符串;2.用字符指针指向一个字符串。(可以不定义字符数组,而只定义一个字符指针,用字符指针指向字符串中的字符),例:#include void main()char*string=“I lov
24、e China!”;printf(“%s n”,string);,注意:1.其中string是一个指向字符串的指针变量,把字符串的首地址赋予string。2.定义string部分:char*string=“I love China!”;等价于:char*string;string=“I love China!”;但对字符数组使用方式char*str=“I love China!”;不能写为:char*str80;str=“I love China!”;,二、字符指针作函数参数,将一个字符串从一个函数传递到另一个函数,可以用地址传递的办法,即用字符数组名作参数,也可以用指向字符的指针变量作参数。
25、在被调函数中可以改变字符串的内容,在主调函数中可以得到改变了的字符串。,(1)实参用指针变量,形参用数组名#includevoid main()void copy_string(char str1,char str2);char s1=“I am a teacher.”;char s2=“You are a student.”;char*a=s1,*b=s2;printf(“string a=%snstring b=%sn”,a,b);printf(“copy string a to string b:n”);copy_string(a,b);printf(“string a=%snstrin
26、g b=%sn”,a,b);,例:用函数调用实现字符串的复制。,void copy_string(char str1,char str2)int i=0;while(str1!=0)str2i=str1i;i+;str2i=0;,例:用函数调用实现字符串的复制。,运行结果:string a=I am a teacher.string b=You are a student.copy string a to string b:string a=I am a teacher.string b=I am a teacher.,(2)实参、形参都用字符指针变量#includevoid main()vo
27、id copy_string(char*str1,char*str2);char s1=“I am a teacher.”;char s2=“You are a student.”;char*a=s1,*b=s2;printf(“string a=%snstring b=%sn”,a,b);printf(“copy string a to string b:n”);copy_string(a,b);printf(“string a=%snstring b=%sn”,a,b);,void copy_string(char*str1,char*str2)int i=0;for(;*str1!=0;
28、str1+,str2+)*str2=*str1;*str2=0;,运行结果:string a=I am a teacher.string b=You are a student.copy string a to string b:string a=I am a teacher.string b=I am a teacher.,指向函数的指针,通过用函数指针变量调用函数(苏小红版第9章的9.6),可以用指针变量指向整型变量、字符串、数组,也可以指向一个函数。C语言中规定,一个函数占用一段连续的内存单元,函数名即是函数所占内存单元的首地址(或称函数的入口地址,函数的指针)。可以把函数的首地址(即入
29、口地址)赋予一个指针变量,使该指针变量指向该函数,然后通过该指针变量可以找到并调用此函数。,把指向函数的指针变量称为“函数指针变量”。函数指针变量定义的一般形式为:类型说明符(*指针变量名)(函数参数表列);其中,“类型说明符”表示被指函数的返回值的类型。例:int(*p)(int,int);其中p是一个指向函数的指针变量,该函数有两个整型参数,函数值为整型。,#includeint max(int a,int b)return(ab)?a:b);void main()int(*pmax)(int,int),x,y,z;pmax=max;printf(“Input two numbers:n”
30、);scanf(“%d%d”,例:求a和b中的最大者。(通过指针变量调用函数),说明:,(1)定义“int(*pmax)(int,int);”中的*pmax两侧的括号不可省略,表示pmax先与*结合,是指针变量,然后再与后面的()结合,表示此指针变量指向函数,这个函数值(即函数返回值)是整型的。若写成“int*pmax(int,int);”,则由()优先级高于*,它就成了声明一个pmax函数了,此函数的返回值是指向整型变量的指针。,说明:,(2)函数指针变量不能进行算术运算,如p+、p-、p+n等运算是无意义的。因为函数指针变量p只能指向函数的入口处,而不能指向函数中的某一条指令处,因此不能用
31、(*p+1)来表示函数的下一条指令。,指针型函数,(返回指针值的函数)(苏小红版第9章的9.6),一个函数可以返回一个整型值、字符值、实型值等,也可以返回指针型是数据,即地址。这种返回指针值的函数,称为指针型函数。一般定义形式为:类型说明符*函数名(形参表列);其中,“*”在函数名之前,表示此函数是指针型函数;“类型说明符”表示被指函数的返回值的类型。,#includefloat*search(float(*pointer)4,int n)float*pt;pt=*(pointer+n);return(pt);,例:有若干个学生的成绩(每个学生有4门课程),要求在用户输入学生序号以后,能输出该
32、学生的全部成绩。用指针函数来实现。,void main()float score 4=60,70,80,90,56,89,67,88,34,78,90,66;float*p;int i,m;printf(“enter the number of student:”);scanf(“%d”,运行结果:enter the number of student:1 The scores of No.1 are:56.00 89.00 67.00 88.00,指针数组和,(苏小红版第11章的11.3),指向指针的指针变量,一、指针数组,一个数组,若其元素均为指针类型数据,称为指针数组。即指针数组中的每一
33、个元素都相当于一个指针变量。指针数组说明的一般形式为:类型说明符*数组名数组长度;其中,“类型说明符”为指针值所指向的变量的类型。例:int*p4;由于 比*优先级高,因此p先与4结合,说明p是一个数组,有4个元素。*表示其后的数组为指针类型的,每个数组元素(即指针变量)都可指向整型变量。,说明:,(1)通常可用一个指针数组来指向一个二维数组,指针数组中的每个元素被赋予二维数组每一行的首地址,因此也可理解为指向一个一维数组。(2)指针数组也常用来表示一组字符串,此时指针数组的每个元素被赋予一个字符串的首地址。,注意:,指针数组和二维数组指针变量的区别:两者虽然都可用来表示二维数组,但是其表示方
34、法和意义是不同的。二维数组指针变量是单个变量,而指针数组表示的是多个指针变量。例:int(*p)4;表示p是一个指向二维数组的指针变量,该二维数组的列数为4或分解为一维数组的长度为4。int*p4;表示p是一个指针数组,有4个元素p0,p1,p2,p3都为指针变量。,#includechar*day_name(char*name,int n)char*p1,*p2;p1=*name;p2=*(name+n);return(n7)?p1:p2);,例:用指针数组作函数参数,编写输入一个17之间的整数,输出对应的星期名。,void main()char*name=Illegal day,Monda
35、y,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday;char*ps;int i;printf(Input Day No:);scanf(%d,分析:主函数中,定义了一个指针数组name,并对name作了初始化赋值,其每个指针变量元素都指向一个字符串。在day_name函数中,指针变量p1被赋予name0的值,p2被赋予namen的值。,二、指向指针的指针变量,如果一个指针变量存放的又是另一个指针变量的地址,则称这个指针变量为指向指针的指针变量。其一般形式为:类型说明符*指针变量名;,例:#include void main()int x,*p
36、,*pp;x=10;p=,分析:p是一个指针变量,指向整型变量x;pp也是一个指针变量,它是指向指针变量p。通过pp指针变量访问x的写法是*pp。,说明:,通过指针变量访问变量称为间接访问,简称间访。这种指针变量直接指向变量,所以称为单级间访。通过指向指针的指针变量来访问变量则构成二级间访。从理论上说,间访方法可以延伸到更多的级数;但在实际上一般程序很少超过二级,因为级数太多,不易理解,易出错。,例:#include void main()int a5=1,3,5,7,9;int*num5=,运行结果:1 3 5 7 9,例:#include void main()char*name=“Fol
37、loew me”,“BASIC”,“Great Wall”,“Fortran”,“Computer design”;char*p;int i;for(i=0;i5;i+)p=name+i;printf(“%s”,*p);,运行结果:Folloew meBASICGreat WallFortranComputer design,三、指针数组作main函数的形参,指针数组的一个重要应用是作为main函数的形参。一般,main函数写成以下形式:void main()括号中是空的。实际上,main函数可以有参数,例如:void main(int argc,int*argv)其中argc和argv是ma
38、in函数的形参。,main函数是由操作系统调用。当处于操作命令状态(DOS命令状态)下,输入main所在的文件名(即可执行文件名,后缀为.exe),操作系统就可以调用main函数。,main函数的形参的值从何处取得,实际上实参是和命令一起给出的。命令行一般形式为:命令名 参数1 参数2.参数n 其中,命令名和各参数之间用空格分隔;命令名是main所在的执行文件名。,例如:file1 China Beijing(文件名应包括盘符、路径以及文件的扩展名,现简化)main函数中形参argc是指命令行中参数的个数,注意文件名也作为一个参数,即argc的值为3;形参argv是一个指向字符串的指针数组,元
39、素argv0指向字符串“file1”,argv1指向字符串“China”,argv2指向字符串“Beijing”。,若有一个名为file1的文件,包含以下的main函数:void main(int argc,char*ragv)while(argc1)+argv;printf(%sn,*argv);-argc;在DOS命令状态下输入的命令行是:file1 China Beijing则执行以上命令行将输出以下信息:China Beijing,或while(argc-1)printf(%sn,*+argv);,有关指针的数据类型的小结:,1.int i;定义整型变量i2.int*p;p为指向整型数据的指针变量3.int an;定义整型数组a,它有n个元素4.int*pn;定义指针数组p,它由n个指向整型 数据的指针元素组成5.int(*p)n;p为指向含n个元素的一维数组的 指针变量,有关指针的数据类型的小结:,6.int f();f为返回整型函数值的函数7.int*p();p为返回一个指针的函数,该指针指 向整型数据8.int(*p)();p为指向函数的指针,该函数返回 一个整型值9.int*p;p是一个指针变量,它指向一个指向 整型数据的指针变量,