1、第八章,善于利用指针,主要内容,地址和指针的概念 变量的指针和指向变量的指针变量 数组与指针 字符串与指针 指向函数的指针 指针数组与指向指针的指针,问题:从键盘输入两个整数到变量a、b中,编写 子函数交换两个变量,void swap(int p,int q) int temp;temp=p;p=q;q=temp;/return ?;/只能返回一个值 #include void main( ) int a,b;scanf(“%d,%d“,解决办法: 1.定义全局变量,但浪费空间且不利于模块化结构设计; 2.使用指针,可以得到多个返回值。,用指针来实现,void swap(int *p,int
2、*q) int temp;temp= *p;*p=*q;*q=temp;/return ?; #include void main( ) int a,b;scanf(“%d,%d“,运用指针变量作为参数, 可以得到多个变化了的值; 又可以不浪费内存空间。,8.1 指针是什么,数据在内存中是如何存储的,又是如何读取的?,内存区的每一个字节有一个编号,这就是“地址” 。如果在程序中定义了一个变量,在对程序进行编译时,系统就会给这个变量分配内存单元。一个变量的地址称为该变量的指针。,.按变量地址存取变量值的方式称为“直接访问”方式,printf(“%d”,i); scanf(“%d”,例如:,2.
3、通过间接访问来存取变量值,将变量的地址存放在另一个变量中。 在语言中,指针是一种特殊的变量,它是存放地址的。 假设我们定义了一个指针变量i_pointer用来存放整型变量的地址,它被分配地址为(3010)、(3011)的两个字节。 可以通过语句:i_pointer ;将的地址(2000)存放到i_pointer中。这时, i_pointer的值就是(2000) ,即变量所占用单元的起始地址。 要存取变量的值,可以采用间接方式:先找到存放“的地址”的变量i_pointer ,从中取出的地址(2000),然后到2000 、 200字节取出的值()。,指针和指针变量的定义:,一个变量的地址称为该变量
4、的“指针”。例如,地址2000是变量的指针。 如果有一个变量专门用来存放另一变量的地址(即指针),则它称为“指针变量”。上述的i_pointer就是一个指针变量。 指针变量的值(即指针变量中存放的值)是地址(即指针)。,8.2 指针变量,C中用符号“*”来联系指针变量和它所指向的变量,如果已定义i_pointer为指针变量,则(*i_pointer)是i_pointer所指向的变量,它和i是同一回事。 例如:,8.2 怎么定义指针变量,定义指针变量的一般形式为基类型 *指针变量名; 下面都是合法的定义:float *pointer_; / pointer_是指向float型变量的指针变量cha
5、r *pointer_; /pointer_是指向字符型变量的指针变量,指针变量的赋值,可以用赋值语句使一个指针变量得到另一个变量的地址,从而使它指向一个该变量。如:pointer_;pointer_;,在定义指针变量时要注意两点:,指针变量前面的“*”,表示该变量的类型为指针型变量。 例: float *pointer_1;指针变量名是pointer_1 ,而不是* pointer_1 。 在定义指针变量时必须指定基类型。需要特别注意的是,只有整型变量的地址才能放到指向整型变量的指针变量中。下面的赋值是错误的float a; int * pointer_1; pointer_1= /* 将f
6、loat型变量的地址放到指向整型变量的指针变量中,错误 */,8.3 怎样引用指针变量,请牢记,指针变量中只能存放地址(指针),不要将一个整数(或任何其他非地址类型的数据)赋给一个指针变量。,例8. 通过指针变量访问整型变量,#include void main ( ) int ,;int*pointer_, *pointer_;pointer_;/*把变量的地址赋 给pointer_1 */ pointer_;/*把变量的地址 赋给pointer_ */printf(%,%,);printf(%,%,*pointer_, *pointer_);,对“”和“*”运算符说明:,如果已执行了语句
7、pointer_; * pointer_的含义是什么?“”和“*”两个运算符的优先级别相同,但按自右而左方向结合,因此先进行* pointer_的运算,它就是变量,再执行运算。因此,* pointer_与相同,即变量a的地址。 *的含义是什么?先进行运算,得的地址,再进行*运算。即所指向的变量,也就是变量a。*和*pointer_的作用是一样的,它们都等价于变量。即*与等价。,对“”和“*”运算符说明:,(*pointer_)相当于。注意括号是必要的,如果没有括号,就成为了*pointer_,从附录可知:+和*为同一优先级别,而结合方向为自右而左,因此它相当于*(pointer_)。由于+在p
8、ointer_1的右侧,是“后加”,因此先对pointer_的原值进行*运算,得到的值,然后使pointer_的值改变,这样pointer_不再指向了。,例8 . 2 输入和两个整数,按先大后小的顺序输出和。,#include void main() int *1,*2,*,;scanf(,);1;if();printf(=,=,);printf(max=,min=,*1,*2); ,交换前(a)和交换后(b)的情况,8.3 指针变量作为函数参数,指针类型的数据可以 作为函数的参数,将 一个变量的地址传送 到另一个函数中,例8 . 3 对输入的两个整数按大小顺序输出,void swap(int
9、 *p1,int *p2) int temp;temp= *p1;*p1=*p2;*p2=temp; #include void main( ) int a,b,*pointer_1,*pointer_2;scanf(“%d,%d“, ,指针变量作为函数参数,不可能通过执行调用函数来改变实参指针变量的值,例8 . 4 对输入的两个整数按大小顺序输出,void swap(int *p1,int *p2) int *p;p= p1;p1=p2;p2=p; #include void main( ) int a,b,*pointer_1,*pointer_2;scanf(“%d,%d“, ,例8.5
10、 输入、 3个整数,按大小顺序输出,void swap(int *p,int *q) int temp;temp= *p;*p=*q;*q=temp; void exchange(int *q1,int *q2,int *q3) if(*q1 *q2) swap(q1,q2);if(*q1 *q3) swap(q1,q3);if(*q2 *q3) swap(q2,q3); ,#include void main( ) int a,b,*pointer_1,*pointer_2;scanf(“%d,%d“, ,运用指针变量作为参数, 可以得到多个变化了的值,8.3 数组与指针,一个变量有地址,一
11、个数组包含若干元素,每个数组元素都在内存中占用存储单元,它们都有相应的地址。所谓数组元素的指针就是数组元素的地址。 指针变量既然可以指向变量,当然也可以指向数组元素(把某一元素的地址放到一个指针变量中),也就是指向数组元素的指针。 引用数组元素可以用下标法(如a3),也可以用指针法,即通过数组元素的指针找到所需的元素。使用指针法能使目标程序质量高(占内存少,运行速度快)。,8.3.1 数组元素的指针,定义一个指向数组元素的指针变量的方法,与 以前介绍的指向变量的指针变量相同。例如: ;(定义为包含个整型数据的数组) *; (定义为指向整型变量的指针变量) 应当注意,如果数组为型,则指针变量的基
12、类型亦应为型。,对该指针变量赋值:;(p=a;) 把元素的地址赋给指针变量。,也就是使指 向数组的 第号元素, 如图:,8.2 在引用数组元素时指针的运算,当指针指向数组元素时,可以对指针进行加和减的运算: () 如果指针变量P已指向数组中的一个元素,则P+1指向同一数组中的下一个元素,p-1指向同一数组中的上一个元素; () 如果p的初值为&a0,则p+i和a+i就是数组元素ai的地址;,8.2 在引用数组元素时指针的运算,当指针指向数组元素时,可以对指针进行加和减的运算: (3) *(p+i)或者*(a+i)是p+i或者a+i所指向的数组元素ai; (4) 如果指针变量p1和p2都指向同一
13、数组,如执行p2-p1,结果是p2-p1的值(两个地址之差)除以数组元素的长度,它反映的是它们的相对位置。两个地址不能相加,即p1+p2是无实际意义的。,8.3通过指针引用数组元素,引用一个数组元素,可以用: () 下标法,如形式; () 指针法,如*()或*(i)。其中是数组名,是指向数组元素的指针变量,其初值。(3)pi,它与*(i)等价。,例8.6 输出数组中的全部元素,假设有一个数组,整型,有个元素。要输出 各元素的值有三种方法:,(1)下标法 #include void main() int ;int ;for(;)scanf(,);printf();for(;)printf(,);
14、 ,用下标法比较直观,能 直接知道是第几个元素,(2) 通过数组名计算数组元素地址,找出元素的值。,#include void main() int ;int ;for(; )scanf(,);printf();for(;)printf(,*();,(3) 用指针变量指向数组元素。,#include void main() int ;int *,;for(;)scanf(,);printf();for(;();)printf( ,*);,用指针变量,不必每次 计算地址,效率高,注意:,可以通过指针变量指向不同的元素,但不可通过a+来改变a所代表的值,因为数组名a代表数组首元素的地址,它是一个指
15、针常量,它的值在程序运行期间是固定不变的。 要注意指针变量的当前值。,例8.7 通过指针变量输出数组的个元素。,有人编写出以下程序: #include void main() int*,;for(; )scanf(,); printf();for(;, )printf(,*); ,注意(续):,3. 要切实保证指针变量p指向数组中有效的元素。 4. 注意指针变量的运算。,作业:,输入10个整数,将其中最大的数与第一个数对换,把最小的数与最后一个数对换。写3个函数: (1)输入10个数;(2)对换处理;(3)输出10个数。要求用指针方法处理。提示:找出最大数和最小数并本别用指针max和min进行
16、指向,然后将max指向的元素与a0元素对调,将min指向的元素与a9对调。,作业:,2. 有10个整数,要求将前面各数顺序向后移动5个位置,使最后的5个数变成最前面的5个数。写一个函数实现以上的功能,在主函数中输入10个整数和输出调整后的10个整数。提示:用指针变量p指向数组a10,设置一个循环(循环变量i取值0-4),将p+i与p+5+i位置元素对调即可。,8.4 用数组名作函数参数,在第7章中介绍过可以用数组名作函数的参 数。当用数组名作为参数时,形参数组与实参数 组共占同一段内存单元,因此,如果形参数组中 各元素的值发生变化,实参数组的值随之变化。 通过指针,我们对此可以有更深刻的理解。
17、,例88 将数组中个整数按相反顺序存放,#include void main() void inv(int ,int );int ,;printf(The original array:);for(;)printf (,);printf();inv (,);printf(The array has been in verted:);for(;)printf (,);printf (); ,void inv(int x ,int ) /*形参x是数组名*/, int temp,();for(;);temp;temp;return;,对这个程序可以作一些改动。将函数inv中的形参改成指针变量。,v
18、oid inv(int *,int ) /*形参x为指针变量*/ int,temp,*,*,();for(;,)emp*;*;*temp;return; ,说明:,C语言调用函数时虚实结合的方法都是采用“值传递”方式,当用变量名作为函数参数时传递的是变量的值,当用数组名作为函数参数时,由于数组名代表的是数组首元素地址,因此传递的值是地址。 实际上,C编译都是将形参数组名作为指针变量来处理的。 在函数被调用时,系统会建立一个和形参对应的指针变量,用来存放从主调函数传递过来的实参数组首元素的地址。 如果有一个实参数组,想在函数中改变此数组中的元素的值,实参与形参的有种对应关系,例109 用选择法对
19、个整数按由大到小顺序排序,#include void main() void sort(int ,int );int*,10;for(;)scanf(,);sort(,);for(,;) (,*); ,void sort(int ,int ), int ,;for(;);for(;)() ;(!);,8.5 通过指针引用多维数组,多维数组元素的地址 指向多维数组元素的指针变量 用指向多维数组的指针作函数参数,1.多维数组元素的地址,二维数组可以认为是“数组的数组”,例 : 定义int a34=1,3,5,7,,; 则二维数组a是由3个一维数组所组成的。设二维数组的首 行的首地址为,则,例8.1
20、 输出二维数组有关的值,#include define FROMAT , void main() int 341,3,5,7,9,;printf(,*);printf(,0 , *();printf(,0,00);printf(,1,);printf(,10,*(+)+);printf(,*();printf(,);printf(,*(*(1);,2 . 指向多维数组元素的指针变量 (1) 指向数组元素的指针变量,例8.12 用指针变量输出二维数组元素的值,#include void main() int 341,3,5,7,9,11,13,15,17,19,21,23;int*;for(;)
21、()printf();printf(,*); ,(2) 指向由个元素组成的一维数组的指针变量,例8.13 输出二维数组任一行任一列元素的值,#include void main ( ) int 1,3,5,7,9,11,13,15,;int (*),;scanf( ,);printf(,*(*(); ,3. 用指向数组的指针作函数参数,例8.14 有一个班,个学生,各学门课,计算总平均分数以及第个学生的成绩。用函数求总平均成绩,用函数找出并输出第个学生的成绩。,用指针变量作形参来接受实参数组名传递来的地址: (1)用指向变量的指针变量; (2)用指向一维数组的指针变量;,C程序实现:,#inc
22、lude void main() void average(float *p,int n);void search(float (*p)4,int n);float score34=65,67,70,60,80,87,90,81,90,99,100,98;average(*score,12);*求12个分数的平均分*search(score,);*求序号为的学生的成绩* , float*_;float ,;_;(;_;)(*);printf(average,aver); ,void average(float *,int ),void search(float (*)4,int ) / * p
23、是指向具有4个元素的一维数组的指针 */,int ;printf(the score of No. % are:,);for(;)printf(5.2,*(*();,例8.5 在上题基础上,查找有一门以上课程不 及格的学生,打印出他们的全部课程的成绩。,#include void main() void search(float (*p)4,int n);/*函数声明*/float score34=65,57,70,60,58,87,90,81,90,99,100,98;search(score,); ,int ,;for(;)flag;for(;)if(*(*()) flag;if() pr
24、intf(“No.%d fails,his scores are:n“,j+1);for(;)printf(%.,*(*();printf(); ,void search(float (*p)4,int ),说明:,通过指针变量存取数组元素速度快,且程序简明。 用指针变量作形参,所处理的数组大小可以变化。 使用指针可以使程序质量提高,且编写程序方便灵活。,8. 字符串与指针 8.4.1字符串的表示形式,(1) 用字符数组存放一个字符串,例 8.6 定义一个字符数组,对它初始化, 然后输出该字符串,#include void main() char string !;printf(,);,str
25、ing是数组名,代表数组的首元素的地址,(2) 用字符指针指向一个字符串,例8.7 定义字符指针,#include void main() char*string” !”;printf(,); ,例8.8 将字符串复制为字符串,#include void main( ) char ”I am a boy.”,20; int ;for(;*()!;)*()*();*();printf(string a is :,);printf( :);for(;!;)printf(,);printf();,对字符串中字符的存取,可以用下标方法, 也可以用指针方法,例8.9 用指针变量来处理例88问题。,#in
26、clude void main() char =“I am a boy. “,20,*p1,*p2;int i;;for(;*!;p1,p2)*;*;printf(string is:,);printf( :);for(;!;)printf(,);printf(); ,也可以用指针变量指向字符串的字符,8. 字符指针作函数参数,例820 用函数调用实现字符串的复制,(1) 用字符数组作参数,#include void main() void copy_string(char from , char to );char a = am a teacher;char =you are a stude
27、nt;printf(“string a= string ,a,b);printf(“copy string a to string b:n “);copy_string (,);printf(“nstring a=%snstring b=%sn“,a,b); ,void copy_string(char from , char to) int ;while(!); ,()用字符指针变量作实参,#include void main() void copy_string(char *, char *);char * am a teacher .;char *you are a student ;p
28、rintf(“string a= ,a,b);printf(“copy string a to string b:n “);_(,);printf(“nstring a=%snstring b=%sn“,a,b); ,(3)用字符指针变量作形参和实参,#include void main() void copy_string(char *, char *);char * am a teacher .;char *you are a student ;printf(“string a= ,a,b);printf(“copy string a to string b:n “);_(,);print
29、f(“nstring a=%snstring b=%sn“,a,b); ,void copy_string(char *,char *) for(;*from!;from,to)*;*;,8. 对使用字符指针变量和字符数组的比较,(1) 字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址(字符串第1个字符的地址),决不是将字符串放到字符指针变量中。 (2)赋值方式。对字符数组只能对各个元素赋值,不能用以下办法对字符数组赋值。char str;str !;而对字符指针变量,可以采用下面方法赋值:char*; !;但注意赋给的不是字符,而是字符串第一个元素的地址。,(3)
30、对字符指针变量赋初值:,char * love China!;等价于char*; !;而对数组的初始化:char str love China!;不能等价于char str;str !; 数组可以在定义时整体赋初值,但不能再赋值语句中整体赋值,(4)指针变量要先赋值,再引用,如: char str;scanf(,str);是可以的。 而常有人用下面的方法,目的是想输入一个字符串,虽然一般也能运行,但这种方法是危险的 :char*;scanf(,);,应当这样: *,; ; (,);,(5) 指针变量的值是可以改变的,例8.1 改变指针变量的值,#include void () char*I l
31、ove China!;printf(,);,(6)字符指针变量指向的字符串常量中的内容不可以被取代,Char a=“House”; Char *b=“House”;A2=r; B2=r; /非法,( 7)只有指针变量指向数组时才可用下标引用数组元素 (8)可以用指针变量指向格式字符串,作业:,输入10个整数,将其中最大的数与第一个数对换,把最小的数与最后一个数对换。写3个函数: (1)输入10个数;(2)对换处理;(3)输出10个数。要求用指针方法处理。提示:找出最大数和最小数并本别用指针max和min进行指向,然后将max指向的元素与a0元素对调,将min指向的元素与a9对调。,作业:,2.
32、 有10个整数,要求将前面各数顺序向后移动5个位置,使最后的5个数变成最前面的5个数。写一个函数实现以上的功能,在主函数中输入10个整数和输出调整后的10个整数。提示:用指针变量p指向数组a10,设置一个循环(循环变量i取值0-4),将p+i与p+5+i位置元素对调即可。,作业:,3. 编写三个子函数,分别实现下面的功能:(1)子函数void input_matrix(double ANN)从键盘输入一个二维矩阵A;(2)子函数void output_matrix(double ANN)输出一个二维矩阵A到屏幕;(3)子函数 void multiply_matrix(double ANN, d
33、ouble BNN,double CNN)计算矩阵A与B的乘积C;主函数调用三个子函数进行测试。,8. 指向函数的指针,8. 什么是函数指针,如果在程序中定义了一个函数,在编译时,编译系统为 函数代码分配一段存储空间,这段存储空间的起始地址 (又称为入口地址)称为这个函数的指针。可以定义一个指向函数的指针变量,指向该函数,如, int (*p)(int,int);,可以用一个指针变量指向某个函数,然后通过该指针变 量调用此函数。,8.2 用函数指针变量调用函数,例8.2 求和中的大者。先列出按一般 方法的程序。,#include void main() int max(int,int);int
34、 ,;scanf(,);(,);printf(”, “,a,b,c);int max(int ,int ) int ;if();else ;return();,#include void main() int (int,int);int (*)(int,int);int ,;scanf(,);(*)(,);printf(,);,用一个指针变量指向一个函数, 将 main 函数改写为:,8.3 怎么样定义和使用指向函数的指针变量,通过指针变量根据不同情况先后调用不同的函数,例8.3 输入两个整数,然后让用户选择1或1,选1时调用max函数,输出二者当中的大数,选2时调用min函数,输出二者中的小
35、数。,用指针变量使程序更简洁和专业,8.4 用指向函数的指针作函数参数,指向函数的指针变量常用的用途之一是把函数的地址作 为参数传递到其他函数,以便能够在被调用的函数中使 用实参函数。,实参函数名 f1 void fun(int (*x1)(int),int (*x2)(int,int)) int ,;(*)(); *调用函数*(*)(,);*调用函数*,例8.4 输入两个整数a和b,由用户输入1,2或3。如果输入1,程序输出a,b当中的大数,如果输入2,程序输出a,b中的小数,输入3,则求a与b之和。,说明:,使用指向函数的指针变量作为函数参数,可以大大增加函数使用的灵活性。可以编写一个通用
36、的函数来实现各种专用的功能。 对作为实参的函数,应在主调函数中用函数原型作函数声明。,8. 返回指针值的函数,一个函数可以带回一个整型值、字符值、实型值等,也可以带回指针型的数据,即地址。 这种带回指针值的函数,一般定义形式为 类型名 *函数名(参数表列); 例如: *(int ,int );,例8.25 有若干个学生的成绩(每个学生有门课程),要求在用户输入学生序号以后,能输出该学生的全部成绩。用指针函数来实现。,#include void main() float score 4=60,70,80,90,56,89,67,88,34,78,90,66;float*search(float
37、(*pointer)4,int n);float*;int ,;printf(enter the number of student:);scanf(,);printf(The scores of No are:,);search(,);for(;printf(,*(); ,子函数:,float * search(float (*pinter)4,int ) float *;*();return();,运行情况如下: enter the number of student: The scores of No. are: 56.00 89.00 67.00 88.00,8.指针数组和多重指针,8
38、. 什么是指针数组一个数组,若其元素均为指针类型数据,称为指针数组,也就是说,指针数组中的每一个元素都相当于一个指针变量。一维指针数组的定义形式为类型名*数组名数组长度;例如:*;,例87 将若干字符串按字母顺序(由小到大)输出。,#include #include void main() void sort(char *name ,int n);void printf(char *name ,int n);char *name =“Follow me”,“BASIC”,“Great Wall,“FORTRAN“,“Computer design“;int ;sort(,);print(,);
39、 ,char *;int ,;for(;);for(;)if(strcmp(name,name)0)=;if(!)temp=namei; namei=namek;namek=temp;,void sort(char * ,int ),void print(char * ,int ) ;(;)printf(,);,子函数:,运行结果为: ,8.7.2 指向指针的指针,指向指针数据的指针变量,简称为指向指针的指针。 怎样定义一个指向指针数据的指针变量呢?如下: *; 的前面有两个*号。*运算符的结合性是从右到左,因此*相当于*(*),显然*是指针变量的定义形式。如果没有最前面的*,那就是定义了一个
40、指向字符数据的指针变量。现在它前面又有一个*号,表示指针变量是指向一个字符指针变量的。*就是所指向的另一个指针变量。,例8.8 使用指向指针的指针,#include void main() char *name=“Follow me“,“BASIC“,“Great Wall,“FORTRAN“,“Computer design“;char *;int ;for(;);printf(,*);,例8.29 一个指针数组的元素指向整型数据的简单例子,#include void main() int ,;int *num5=&a0,&a1,&a2,&a3,&a4;int *,;for(;) printf( ,*);,8. 指针数组作函数的形参,指针数组的一个重要应用是作为main函数的形参。在以往的程序中,函数的第一行一般写成以下形式:void main()括弧中是空的。实际上,main函数可以有参数,例如:void main(int argc,char *argv )argc和argv就是main函数的形参。 main函数是由操作系统调用的。那么,main函数的形参的值从何处得到呢?显然不可能在程序中得到。实际上实参是和命令一起给出的。也就是在一个命令行中包括命令名和需要传给main函数的参数。命令行的一般形式为命令名 参数 参数参数,