1、第八章 指针,指针是C语言中的一个重要概念。掌握指针的用法,可使程序简洁、高效、灵活,但并不难学。,为了了解什么是指针,先看一个小故事:地下工作者阿金接到上级指令,要去寻找打开密电码的密钥,这是一个整数。几经周折,才探知如下线索,密钥藏在一栋三年前就被贴上封条的小楼中。一个风雨交加的夜晚,阿金潜入了小楼,房间很多,不知该进哪一间,正在一筹莫展之际,忽然走廊上的电话铃声响起。艺高人胆大,阿金毫不迟疑,抓起听筒,只听一个陌生人说:“去打开211房间,那里有线索”。阿金疾步上楼,打开211房间,用电筒一照,只见桌上赫然6个大字:地址1000。阿金眼睛一亮,迅速找到1000房间,取出重要数据66,完成
2、了任务。,8.1 变量的地址和指针,1.内存地址内存中存储单元的编号 (1)计算机硬件系统的内存储器中,拥有大量的存储单元(容量为字节)。 为了方便管理,必须为每一个存储单元编号,这个编号就是存储单元的“地址”。每个存储单元都有一个惟一的地址。 (2)在地址所标识的存储单元中存放数据。 注意:内存单元的地址与内存单元中的数据是两个完全不同的概念。 2.变量地址系统分配给变量的内存单元的起始地址 假设有这样一个程序:,void main() int num;scanf(“%d“,”时,存取变量num值的方式可以有两种:,(1)直接访问直接利用变量的地址进行存取1)上例中scanf(“%d”,&n
3、um)的执行过程是这样的:用变量名num作为索引值,检索符号表,找到变量num的起始地址3000;然后将键盘输入的值(假设为)送到内存单元3000至3003中。此时,变量num在内存中的地址和值。2)printf(“num=%dn“,num)的执行过程,与scanf()很相似:首先找到变量num的起始地址3000,然后从3000至3003中取出其值,最后将它输出。 (2)间接访问通过另一变量访问该变量的值语言规定:在程序中可以定义一种特殊的变量(称为指针变量),用来存放其它变量的地址。,例如,假设定义了这样一个指针变量num_pointer,它被分配到4000-4003单元,其值可通过赋值语句
4、“num_pointer=num;”得到。此时,指针变量num_pointer的值就是变量num在内存中的起始地址3000。 通过指针变量num_pointer存取变量num值的过程如下: 首先找到指针变量num_pointer的地址(4000),取出其值3000(正好是变量num 的起始地址); 然后从3000-3003中取出变量num的值“3”。 (3)两种访问方式的比较 两种访问方式之间的关系,可以用某人甲(系统)要找某人乙(变量)来类比。 一种情况是,甲知道乙在何处,直接去找就是(即直接访问)。 另一种情况是,甲不知道乙在哪,但丙(指针变量)知道,此时甲可以这么做:先找丙,从丙处获得乙
5、的去向,然后再找乙(即间接访问)。,4.指针与指针变量 (1)指针即地址一个变量的地址称为该变量的指针。通过变量的指针能够找到该变量。 (2)指针变量专门用于存储其它变量地址的变量 指针变量num_pointer的值就是变量num的地址。指针与指针变量的区别,就是变量值与变量的区别。 (3)为表示指针变量和它指向的变量之间的关系,用指针运算符“*”表示。 例如,指针变量num_pointer与它所指向的变量num的关系,表示为: *num_pointer,即*num_pointer等价于变量num。 因此,下面两个语句的作用相同: num=3; /*将3直接赋给变量num*/ num_poin
6、ter= /*将3赋给指针变量num_pointer所指向的变量*/ 注意:后两句的次序不能调换!,8.2 指针变量的定义与应用,8.2.1 指针变量的定义与相关运算 例8.1 指针变量的定义与相关运算示例。 void main() int num_int=12, *p_int; /*定义一个指向int型数据的指针变量p_int */ float num_f=3.14, *p_f; /*定义一个指向float型数据的指针变量p_f */ char num_ch=p, *p_ch; /*定义一个指向char型数据的指针变量p_ch */ p_int= ,程序运行结果: num_int=12, *
7、p_int=12 num_f=3.14, *p_f=3.14 num_ch=p, *p_ch=p程序说明: (1)头三行的变量定义语句指针变量的定义与一般变量的定义相比,除变量名前多了一个星号“*” (指针变量的定义标识符)外,其余一样:数据类型 *指针变量, *指针变量2;注意:此时的指针变量p_int、p_f、p_ch,并未指向某个具体的变量(称指针是悬空的)。使用悬空指针很容易破坏系统,导致系统瘫痪。,(2)中间三行的赋值语句取地址运算() 取地址运算的格式: 变量 例如,&num_int、&num_f、&num_ch的结果,分别为变量num_int、num_f、num_ch的地址。注意
8、:指针变量只能存放指针(地址),且只能是相同类型变量的地址。 例如,指针变量p_int、p_f、p_ch,只能分别接收int型、float型、char型变量的地址,否则出错。(3)后三行的输出语句指针运算(*) 使用直接访问和间接访问两种方式,分别输出变量num_int、num_f、num_ch的值。 注意:这三行出现在指针变量前的星号“*”是指针运算符,访问指针变量所指向的变量的值,而非访问指针运算符。,例8.2 使用指针变量求解:输入2个整数,按升序(从小到大排序)输出。void main() int num1,num2;int *num1_p=,9,&num1,&num2,6,num1,
9、num2,num1_p,num2_p,2,程序运行情况: Input the first number:9 Input the second number:6 num1=9, num2=6 min=6, max=9程序说明: (1)第5行的if语句如果*num1_p*num2_p (即num1num2),则交换指针,使num1_p指向变量num2(较小值),num2_p指向变量num1(较大值)。 (2)printf(“min=%d, max=%dn”, *num1_p, *num2_p); 语句:通过指针变量,间接访问变量的值。,本案例的处理思路是:交换指针变量num1_p 和num2_p的
10、值,而不是变量num1和num2的值(变量num1和num2并未交换,仍保持原值),最后通过指针变量输出处理结果。8.2.2 指针变量作函数参数 1.指针变量,既可以作为函数的形参,也可以作函数的实参。 2.指针变量作实参时,与普通变量一样,也是“值传递”,即将指针变量的值(一个地址)传递给被调用函数的形参(必须是一个指针变量)。 注意:被调用函数不能改变实参指针变量的值,但可以改变实参指针变量所指向的变量的值。,例8.3 使用函数调用方式改写例8.2,要求实参为指针变量。 #include void swap(int *pointer1, int *pointer2) int temp; t
11、emp=*pointer1;*pointer1=*pointer2; *pointer2=temp; /*主函数main()*/ void main() int num1,num2;/*定义并初始化指针变量num1_p和 num2_p */int *num1_p=,printf(“Input the first number: ”); scanf(“%d”, num1_p);printf(“Input the second number: ”); scanf(“%d”, num2_p);printf(“num1=%d, num2=%dn”, num1, num2);if( *num1_p *n
12、um2_p ) /* 即(num1num2)*/swap(num1_p, num2_p); /*指针变量作实参*/*输出排序后的num1和num2的值*/printf(“min=%d, max=%dn”, num1, num2); 程序运行情况: Input the first number:9 Input the second number:6 num1=9, num2=6 min=6, max=9,例8.3_1 使用函数调用方式改写例8.2,要求实参为指针变量。 #include void swap(int *pointer1, int *pointer2) int *temp; temp
13、=pointer1;pointer1=pointer2; pointer2=temp; /*主函数main()*/ void main() int num1,num2;/*定义并初始化指针变量num1_p和 num2_p */int *num1_p=,printf(“Input the first number: ”); scanf(“%d”, num1_p);printf(“Input the second number: ”); scanf(“%d”, num2_p);printf(“num1=%d, num2=%dn”, num1, num2);if( *num1_p *num2_p )
14、 /* 即(num1num2)*/swap(num1_p, num2_p); /*指针变量作实参*/*输出排序后的num1和num2的值*/printf(“min=%d, max=%dn”, num1, num2); 程序运行情况: Input the first number:9 Input the second number:6 num1=9, num2=6 min=9, max=6,&num1,temp,3,例8.3_2 使用函数调用方式改写例8.2,要求实参为指针变量。 #include void swap(int *pointer1, int *pointer2) int *temp
15、; *temp=*pointer1; /temp无初始化,悬空指针*pointer1=*pointer2; *pointer2=*temp; /*主函数main()*/ void main() ,形参指针变量pointer1(指向变量num1)和pointer2(指向变量num2),在函数调用开始时才分配存储空间,函数调用结束后立即被释放。 虽然被调用函数不能改变实参指针变量的值,但可以改变它们所指向的变量的值。总结:为了利用被调用函数改变的变量值,应该使用指针(或指针变量)作函数实参。其机制为:在执行被调用函数时,使形参指针变量所指向的变量的值发生变化;函数调用结束后,通过不变的实参指针(或
16、实参指针变量)将变化的值保留下来。,例8.4 输入3个整数,按降序(从大到小的顺序)输出。要求使用变量的指针作函数调用的实参来实现。void swap(int *pointer1, int *pointer2) int temp;temp=*pointer1; *pointer1=*pointer2;*pointer2=temp;,/*主函数main()*/ void main() int num1,num2,num3;/*从键盘上输入3个整数*/printf(“Input the first number: ”); scanf(“%d”, 测试用例1:12,6,9 测试用例2:9,12,6
17、测试用例3:9,6,12 测试用例4:12,9,6(最差情况),程序运行情况: Input the first number:9 Input the second number:6 Input the third number:12 num1=9, num2=6, num3=12 排序结果: 12, 9, 6,例8.5 编写函数add(int *a,int *b),函数中把指针a,b所指的存储单元中的两个值相加,然后将和值作为函数值返回。 #include int add(int *a,int *b) int sum;sum=*a+*b;return sum; void main() int
18、x,y,z;scanf(“%d,%d”, ,例8.6 把主函数中变量i和j中存放较大数的那个地址作为函数值返回。 #include int *fun(int *a,int *b) if(*a*b) return a;return b; void main() int *p,I,j;scanf(“%d,%d”, ,例8.7 请编写函数,其功能是对传送过来的两个浮点数求出和值与差值,并通过形参送回调用函数。 #include float add(float x,float y) return x+y; float sub(float x,float y) return x-y; void adds
19、ubResult(float x,float y, float *addR, float *subR) *addR=add(x,y);*subR=sub(x,y); void main() float addResult,subResult,i,j;scanf(“%f,%f“, ,例8.8 请编写函数,对传送过来三个数选出最大值和最小值,并通过形参传回调用函数。 #include int max(int x,int y) return xy?x:y; int min(int x,int y) return xy?x:y; void minmaxResult(int x,int y, int z,int *minR, int *maxR) *minR=min(x,min(y,z);*maxR=max(x,max(y,z); void main() int minResult,maxResult,i,j,k;scanf(“%d,%d,%d“, ,