




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、第七章指 针,本章目录,7.1 指针的概念,7.2指针变量和函数,7.3指针变量和数组,7.4 指针数组,指针是C语言的一种数据类型,在C语言中处于重要的地位。正确、灵活地应用指针可以有效地表示复杂的数据结构、动态地分配内存、方便地使用字符串、高效地使用数组。熟练地应用指针可以使C程序简洁、紧凑、应用效果好。,7.1 指针的概念,指针的概念包括指针和指针变量的关系、指针和函数、指针和数组的使用等。 7.1.1 指针和指针变量的关系 一个变量的地址称为该变量的指针。 1地址指内存中存储单元的编号: 1)计算机硬件系统的内存储器中,拥有大量的存储单元(容量为字节)。为了方便管理,必须为每一个存储单
2、元编号,这个编号就是存储单元的“地址”。每个存储单元都有一个唯一的地址。 2)在地址所标识的存储单元中存放数据。 注意:内存单元的地址与内存单元中的数据是两个完全不同的概念。内存单元的编号(地址),与变量名对应。内存单元的内容:即变量的值。,7.1 指针的概念,到目前我们学过的基本数据类型int型、float型、double型、char型等,内存分配的存储单元分别是2个字节、4个字节、8个字节、1个字节等。前面我们已经介绍了变量的概念,每个变量都有自己的存储地址。定义变量的同时,把存储单元的起始地址作为变量的地址(指针)。 指针变量指专门存放变量地址的变量。为了更好的理解指针和指针变量,下面举
3、例说明: int a; 编译系统会为它分配2个字节存储单元即1000H和1001H,1000H就是变量a的地址。如果有一个变量存放变量a的地址,那这个变量就是指针变量pa。如图7-1,7.1 指针的概念,我们可以直接给变量a赋初值2,即通过变量a的地址访问a,这种方式称“直接访问”。当然还可以通过指针变量pa访问变量a,这种访问方式称“间接访问”。,1000H,1001H,变量a,指针变量pa,图7-1,7.1 指针的概念,7.1.2指针变量定义 定义指针变量的一般形式为 基类型 *指针变量名 指针变量与普通变量类似,在程序中若要使用指针变量,必须先进行定义后使用。 例如: int *p1,i
4、; (定义p1为指向整型变量的指针变量) char *p2,c; (定义p2为指向字符型变量的指针变 量) float *p3,f; (定义p3为指向实型变量的指针变量) int、char、float分别称为指针变量p1、p2、p3的“基类型”,,7.1 指针的概念,“基类型”只是指指针变量所指变量的类型,不是指针变量的类型。指针变量只是存放指向变量的地址,由于内存地址都是整型常量,所以指针变量在内存中只占2个字节。 由上可知,定义指针变量的同时也可以定义其他类型的变量,它们在语法上的差别仅在于前者在变量名多了一个“*”号。要弄清楚指针变量的含义和作用,还必须弄清楚以下几个概念。 1指针变量名
5、:指针变量名不包括“*”号,因为“*”号不能用于标识符。因此,在*p1中,“*”和p1不是一个整体,不要把*p2、*p3等看作指针变量名,指针变量名是p2、p3等。 2指针变量的值:其初值不定,当初始化或赋值后,其值表示另一个变量的地址。,7.1 指针的概念,7.1.3和指针有关运算符 指针运算包括两个运算符 1取地址运算符( 将变量a的地址赋给指针变量pa,也就是使pa指向了变量a。,7.1 指针的概念,2 指针运算符(*) 也叫间接访问运算符,单目运算符,当它和指针变量参与运算时,表示指针变量所指的内容,即实现“取内容”操作。 int a=5,b, *pa; pa= 注意:*在不同的地方作
6、用有时是不一样的。若出现在变量定义的部分,*说明pa是指针变量;出现在其他操作部分中*pa表示pa所指向的变量a。 3. 取地址运算符( 使指针变量q中也存放了变量a的地址,也就是说指针变量p和q都指向了整型变量a。使用时应注意:赋值号两边指针变量的基类型必须相同。,7.1 指针的概念,3给指针变量赋“空”值 p=NULL; NULL是在stdio.h头文件中定义的预定义符,因此在使用NULL 时,应该在程序的前面出现预定义行:# include stdio.h。NULL的代码值为0, 应当注意: 1.指针变量只能存放地址,不能把一个整型量赋给指针变量。如 p=5; 是错的。应该通过取地址运算
7、符“ /*符号*用于定义指向整型变量的指针变量pa*/ pa= ,7.1 指针的概念,程序的运行结果为:,注意:a和pa的地址不一定是上面的结果,依据当时运行结果而定。 由例子,可归纳出符号“*”在C语言中有以下4种用法: 1用作一个普通ASCII字符,如上述程序中第6行语句在双引号 中的*号作为普通字符。 2在算术表达式中用作双目乘法运算符,如程序第5行中的 a*7。 3用于定义指针变量,如程序第2行中的*pa,定义pa为一指针 变量。这里的“*”不是运算符,表示变量pa为指针变量。,7.1 指针的概念,4在地址表达式中用作“间接访问”运算符,此处的“*”号是一个单目运算符,用于“左存右取”
8、由地址表达式所指向的变量的内容。如程序第4、5行中的*pa和第6行中双引号外边的*pa中的“*”号。由程序注释可知,第5行中的*pa的作用和第4、6两行中的*pa的作用是不相同的。即,当*pa出现在赋值运算符“=”的左边时,代表将右边的值存入由指针变量pa所指向的地址单元;而在其他位置作“间接访问”运算符时,代表取出由指针变量pa所指向的地址单元的内容。 符号“*”的4种不同作用,容易混淆的是后两种作用,大家可从它们所处的不同位置加以区分。 这里请读者牢记,“*”后必须跟一个地址表达式,指针变量pa代表一个地址,所以*pa是有意义;而普通变量a不是地址,因此,*a是无意义的。不要期望得到*a的
9、输出,这只会引起编译出错。,7.1 指针的概念,思考: 1若已执行了“pa=p= 如图7-5所示,5,变量a,p,P+1,图7-5,p,P+1,P+3,a0,a1,a0,a0,a8,图7-6,7.1 指针的概念,2指针运算和数组 指针运算在数组中应用得最多。 例如p指向int型数组,且p= 则p+1 指向a2 p+3指向a4 见图7-6 3指针变量相减 两个指针变量相减的含义是求得两个地址在内存中的相隔距离(存储单元个数加1)。能做相减运算的充要条件为:两个指针不但要指向同一数据类型的目标,而且要求所指对象是唯一的,一般应用于数组。两个指针变量相加则毫无实际意义。,7.2指针变量和函数,指针变
10、量和函数的关系包括指针变量和函数参数、指针变量和返回指针值的函数。 7.2.1指针变量和函数参数 函数的参数不仅可以是整型、实型、字符型等数据,还可以是指针类型。它的作用是将指针变量的值(该指针变量所指变量的地址)传送到另一个函数中。 学过函数的读者一定知道,C语言函数参数都是单项值传递,只能是实参传递给形参而不能反过来,如果想改变实参变量中的值,最好的方法用指针变量作为函数的参数。 例7.3 学校临时决定让正在上课的301教室和307教室的学生互换教室。用指针变量作为函数的参数。 #include void huhuan(int *p1,int *p2) ,7.2指针变量和函数,int t;
11、 t=*p1; *p1=*p2; *p2=t; main( ) int js301=56,js307=60; printf(jishi301=%d,jiaoshi307=%dn,js301,js307); huhuan( 运行结果:,7.2指针变量和函数,说明 如果将huhuan函数写成以下形式就会出问题: void huhuan(int *p1, int *p2) int *t; *t=*p1; /*此语句有问题*/ *p1=*p2; *p2=*t; 请读者自己上机运行,看一下结果。 注意:当一个指针变量没有具体指向时,不能给该指针变量所指单元赋值。同时,调用函数时,不能企图通过改变形参指针
12、变量的值而使实参指针变量的值发生变化。,7.2指针变量和函数,7.2.2指针变量和返回指针值的函数 函数有类型,也就是函数的返回值类型。函数可以返回整型、实型、字符型等数据,也可以返回指针类型的的数据。此时函数的定义的一般形式如下: 类型标识符 *函数名(参数列表) 例如: int *zhizhen(int x,int y) zhizhen是函数名,函数调用它返回一个指向整型的指针。其中 最前面的int表示返回的指针指向的是整形数据,*表示它后面的 函数zhizhen()是指针类型函数,x,y是函数zhizhen的形式参 数。 例7.4 将两个教室的学生合并到一个教室,函数带回教室的地址。,7
13、.2指针变量和函数,#include int *hebing(int *a,int *b) *a=*a+*b; return a; main( ) int js1,js2; scanf(%d,%d, 运行结果如下:,7.3指针变量和数组,C语言中指针和数组的关系很密切,任何能用数组完成的操作都可以用指针来实现,正确使用指针变量来引用和处理数组元素,能够使程序简单明了,效率更高。 7.3.1 一维数组和指针变量 1数组与指针变量的关系 C语言规定,任何一个数组的数组名表示该数组的首地址,即地址常量或叫指针常量。所以数组中的元素的地址可以通过数组名加上一个整数来表示。 例如定义 int a5,*p
14、;*a *(a+1) *(a+2) a0 a1 a2 图7-7p p+1 p+2 a a+1 a+2 编译系统为数组a分配连续的存储单元,如图7-7,7.3指针变量和数组,p,P+1,P+2,a,a+1,a+2,*a,*a+1,*a+2,a0,a1,a3,a,a+1,a+2表示相应元素的地址 *a,*(a+1),*(a+2) 表示相应数组元素即 a0,a1, a2 现执行语句:p=a;这样把数组a的首地址赋给了 指针变量p,或者说p指向了数组a,数组a中的 各元素地址可以用指针变量p加上一个整数来表 示,即 p+0,p+1,p+2,相应的数组元素为*(p+0),*(p+1),*(p+2)。当然
15、也可以像数组元素形式表示p0,p1,p2,7.3指针变量和数组,2指针变量引用数组元素 综上所述,数组元素的表示方法有: 下标法,如ai; 指针法,如 *(a+i),*(p+i) 其中a是数组名,p是指向数组的指针变量。 例7.5 某班级5名学生成绩输出。 #include main() int i,a5=90,97,85,70,64,*p; /*定义一位数组a,并赋初值*/ p=a; /*指针变量p指向数组a printf(p=%x,a=%xn,p,a); /*输出数组a的首地址*/ for(i=0;i5;i+) printf(a%d= %d ,i,ai); *下标法输出*/ printf(
16、n); for(i=0;i5;i+) printf(p%d= %d ,i,pi); printf(n);,7.3指针变量和数组,for(i=0;i5;i+) printf(*(a+%d)= %d ,i,*(a+i); *指针法输出*/ printf(“n”); for(i=0;i5;i+) printf(*(p+%d)= %d ,i,*(p+i); printf(n); for(i=0;i5;i+) printf(“p%d= %d ”,i,*p+); /*逐个移动指针变量p输出 数组元素*/ printf(n); 运行结果:,7.3指针变量和数组,注意:数组a与指针变量p的值以当时运行结果为准
17、。 从上面的例子可以看出: 1)指针变量p指向数组a时,ai、*(a+i)、*(p+i)输出结果相同,在程序中可以互换使用。需要说明的是,C编译系统对ai、*(a+i)、*(p+i)的处理方法是相同的,即按照相同的地址计算出数组元素的地址,因此三种方法的执行效率是相同的。 2)指针变量p可以进行p+运算,而对于数组名a不能用a+运算,因为数组名代表的是数组的首地址,是地址常量。 3)注意指针的初始化和指向,逐个移动指针变量p输出数组元素时,输出a4时,p的当前值指向的是数组a下面的单元。 上面的程序改为: #include main() int i,a5,*p; /*定义一位数组a */ p=
18、a;,7.3指针变量和数组,for(i=0;i=4;i+) scanf(%d ,p+); for(i=0;i5;i+) printf(%d ,*p+); printf(n); 运行结果:,7.3指针变量和数组,程序中p的指针在输入数据之后,没有从新指向数组a,正确程序如下,请读者自己上机运行。 #include main() int i,a5,*p; /*定义一位数组a */ p=a; for(i=0;i4;i+) scanf(%d ,p+); p=a; for(i=0;i5;i+) printf(%d ,*p+); printf(n); ,7.3指针变量和数组,3数组名作为函数参数 在C程序
19、中,可以用数组名作函数的实参和形参。数组名作为函数参数时将数组的起始地址传递过去,形参一定是相应指针类型。 例7.6将数组a中n个整数按相反顺序存放。 #include void inv(int x , int n) int i, j, t, m; m=(n-1)/2; for (i=0; i=m; i+) j=n-1-i; t=xi; xi=xj;,7.3指针变量和数组,xj=t; main( ) int i, a10=5,3,8,7,2,6,0,9,1,4; printf(The original array:n); for (i=0; i10; i+) printf(%d , ai);
20、printf(n); inv(a, 10); printf(The array has been inverted:n); for (i=0; i10; i+) printf(%d , ai); printf(n); ,7.3指针变量和数组,程序运行结果如下:,说明 1)在本程序的main函数中定义了一个一维数组a并赋以初值,在调用函数时使用了语句 inv (a, 10); 表示将数组名(即数组的首地址)和数组元素的个数作为实参传递给形参变量x和n。 在被调函数inv中定义x为形参数组名,它得到的是实参数组的首地址,本例中的inv函数的首部也可以写成 void inv(int *x, int
21、n) 其作用是相同的。 2)在用数组名作为函数的实参时,形参可以是数组名,也可以是指针变量;在用指向数组的指针作为函数的实参时, 形参可以是数组名,也可以是指针变量。:,7.3指针变量和数组,7.3.2 二维数组和指针变量 前面已经讲过了二维数组的定义,现在讲解指针可以指向二维数组。 1指向数组元素的指针变量 定义一个二维数组: int a23=1,2,3,4,5,6; 二维数组元素在内存中是按行存放的,对于上面的定义我们可以认为a是数组名,数组包括2行即2个元素a0,a1。每个元素又是一维数组,包含3个元素。,7.3指针变量和数组,例7.7 用指针变量输出2个班级的学生成绩 #include
22、 main() float a23=97,89,56,99,77,88,*p; for(p=a0;pa0+6;p+) if(p-a0)%3=0) printf(n); printf(%6.0f,*p); 运行结果:,7.3指针变量和数组,上面的例子改为 #include main() float a23=97,89,56,99,77,88,*p; for(p= ,7.3指针变量和数组,2指向包含m个元素的一维数组的指针变量 定义: 类型标识符 (*指针变量名)m; 例如 int (*p)3; 其中p是指针变量,它指向包含3个整数元素的一维数组。 注意:*p两边的括号不能省,否则表示指针数组。
23、例7.6 输出某个班级某名学生的成绩。 #include main() float a23=97,89,56,99,77,88,(*p)3; int i,j; p=a; /*数组的首地址赋给p*/ scanf(%d,%d,/*访问第I 行第j列元素*/ ,7.3指针变量和数组,运行结果:,注意:p是一个指向一维数组的指针变量,i的变化是以 一维数组的长度为单位,而p+i指的第i行。,7.3指针变量和数组,7.3.3字符串和指针变量 前面的章节讲述的字符串,本小节介绍指针和字符串的关系。 1指向字符串的指针变量 使指针指向一个字符串的方法主要有以下几种: 1)通过赋初值使指针指向一个字符串 ch
24、ar *ps=Hello!; 2)通过赋值运算使字符指针指向字符串 char *ps; ps=Hello!; 也可以通过指针变量之间的赋值使指针指向字符串。例如: char *ps1, *ps2=Hello!; ps1=ps2; 使ps1也指向了ps2所指向的字符串。还可以将字符数组名赋给字符指针变量而使其指向字符串。例如: char *ps1, s = Hello!; ps1=s;,7.3指针变量和数组,例7.8 用指针法将字符串a复制为字符串b。 # include stdio.h main( ) char a20, b20, *p1, *p2; int i; gets(a); /*输入一
25、串字符*/ for(p1=a,p2=b; *p1!= 0 ; p1+, p2+) *p2=*p1; *p2= 0 ; puts(b); 运行结果:,7.3指针变量和数组,2指向字符串的指针作函数参数 将一个字符串从一个函数传递到另一个函数,可以采用指针传递的方法。 例7.9用函数调用实现字符串的连接。 #include void str_cat (char *p1, char *p2) while (*p1!= 0) p1+; /*找目标字符串的结束标志0*/ while(*p2!= 0) *p1=*p2; /*将字符串2连接到字符数组中*/ p1+; p2+; *p1=0; /*在连接后的字
26、符串后面添补0*/ ,7.3指针变量和数组,main( ) char a80, b80; gets(a); gets(b); str_cat(a, b); puts(a); 运行结果:,程序中的str_cat函数还可写成如下形式: viod str(chat *p1, char *p2) while(*p1) p1+; while(*p2) *p1+=*p2+; *p1=0;,7.4 指针数组,指针数组的每个元素都是一个指针数据。指针数组比较适合用于指向多个字符串,使字符串处理更加方便、灵活。 7.4.1指针数组定义和引用 1. 指针数组定义 数据类型标识符 *数组名元素个数; char *p
27、3; 其中p是数组名,包含3个元素,每个数组元素都是字符型指针变量。在定义数组时进行初始化: char *p3=”Beijing ”,”Tianjin”,”Shanghai”;,7.4 指针数组,如图所示,其中p0指向字符串”Beijing”,p1指向字符串”Tianjin”,p2指向字符串”Shanghai”。经过上面的定义后,我们就可以像普通指针那样来使用指针数组了。 2. 指针数组引用 在C语言中,指针数组常用来处理二维或多维数组。 例7.10 输出我国四个直辖市名称 #include main( ) char city410=Beijing,tianjin,shanghai, chon
28、gqing; int i; for(i=0;i4;i+) printf(%sn,cityi); 运行结果:,7.4 指针数组,在定义二位数组时,二维个数必须是大于或等于字符串的字符个数,但由于字符串长度不一致,使很多存储单元未被利用,存储单元的利用率太低。而用数组指针来处理会改善这种情况,指针数组的各个元素所指向的字符串长度没有限制。 上面的程序可以写成 #include main() char *p4=Beijing,tianjin,shanghai, chongqing; int i; for(i=0;i4;i+) printf(%sn,pi); ,7.4 指针数组,7.4.2 用作mai
29、n函数的形参 指针数组的一个重要的应用是作为main函数的形参,在前面所接触到的程序中,main函数是不带参数的,总是写成 main( ) 括号中时空的。实际上main函数可以有参数的,这个参数可以认为是 main函数的形式参数。语言规定main函数的参数只能有两个,习惯上这两个参数写为argc和argv。因此,main函数的函数头可写为: main (argc,argv)。语言还规定argc(第一个形参)必须是整型变量,argv( 第二个形参)必须是指向字符串的指针数组。加上形参说明后,main函数的函数头应写为: main (int argc,char *argv) 由于main函数不能被
30、其它函数调用, 因此不可能在程序内部取得实际值。那么在何处把实参值赋予main函数的形参呢? 实际上main函数的参数值是从操作系统命令行上获得的。当我们要运行一个可执行文件时,在DOS提示符下键入文件名,再输入实际参数即可把这些实参传送到main的形参中去。,7.4 指针数组,DOS提示符下命令行的一般形式为: C:可执行文件名 参数 参数; 但是应该特别注意的是,main 的两个形参和命令行中的参数在位置上不是一一对应的。因为main的形参只有二个,而命令行中的参数个数原则上未加限制。argc参数表示了命令行中参数的个数(注意:文件名本身也算一个参数),argc的值是在输入命令行时由系统按
31、实际参数的个数自动赋予的。例如有命令行为: C:chengxu beijing tianjing shanghai chongqing 由于文件名chengxu本身也算一个参数,所以共有5个参数,因此argc取得的值为5。argv参数是字符串指针数组,其各元素值为命令行中各字符串(参数均按字符串处理)的首地址。 指针数组的长度即为参数个数。,7.5 程序举例,例7.11 求任意两个整数的最大公约数和最小公倍数。 #include int zuida(int a,int b) int i,t; if(ab) t=a; a=b; b=t; for(i=a;i=2;i-) if(a%i=0,7.5
32、程序举例, int zuixiao(int a,int b) int i,t; if(ab) t=a; a=b; b=t; for(i=a;i=a*b;i+) if(i%a=0 ,7.5 程序举例,main() int a,b,c,d; scanf(%d,%d, 运行结果:,7.5 程序举例,本程序运行两次,分别求7,12和12,9的最大公约数和最小公倍数。 例7.12抓交通肇事犯 一辆卡车违反交通规则,撞人后逃跑。现场有三人目击事件,但都没有记住车号,只记下车号的一些特征。甲说:牌照的前两位数字是相同的;乙说:牌照的后两位数字是相同的,但与前两位不同; 丙是数学家,他说: 4位车号刚好是一个整数的平方。请根据以上线索求出车号。 *问题分析与算法设计 按照题目的要求造出一个前两位数相同、后两位数相同且相互间又不同的整数,然后判断该整数是否是另一个整数的平方。 #include
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 上海京东代播合同标准文本
- 与拆迁公司拆迁合同样本
- 购房合同范本标准版
- 古诗《宿建德江》教案新部编本
- 危险化学品采购运输装卸储存使用及报废安全管理制度
- 五年级信息技术上册教学计划
- 文明在你我心中主题班会教案
- 燃气工程竣工验收报告
- 科达纪委谈话系统行业解决方案
- 仓房买卖合同样本
- DB15T 489-2019 石油化学工业建设工程技术资料管理规范
- 数学课堂教学技能讲座课件
- 异物管控记录表
- 公车私用管理制度
- 设备主人制管理办法
- 市政基础设施工程旁站监理记录表
- 幼儿园绘本:《小蛇散步》 课件
- 《艺术学概论考研》课件艺术本体论-形式论
- 遵义会议ppt课件
- 北京大学数字图像处理(冈萨雷斯)通用课件
- 2022年班主任育人故事一等奖两篇范文
评论
0/150
提交评论