第十章 指 针 信息管理系 章 节 总 览 指针是C语言中的一个重要的概念,也是C 语言中的一个重要特色。正确灵活的使用指针: (1) 可以有效的表示复杂的数据结构; (2) 能动态分配内存; (3) 能方便使用字符串; (4) 有效而方便的使用数组; • • • • 现实生活中的指针及其特点: 钟表上的指针 1、根据指向确定值 2、不同的指针单位不同 10.1地址和指针的概念(1) 一、地址: 1.概述:如果在程序中定义了一个变量,在编译时就给这个变量分 配内存单元,该内存单元有相应的地址;同时,该内存单元可以存 放变量的值,又有相应的内容。 2.定义:内存的每一个字节有一个编号,这就是地址。 3.根据内存单元的地址访问内存单元的内容的方式 (1)直接访问: 按变量地址存取变量值的方式称为 “直接访问”方式。 另一个变量 (2)间接访问: 变量的地址存放在另一个变量中 的方式称为“间接访问”方式。 如:i_pointer=&i;(见图10.2) 指针变量 i_pointer i 10 2000 2000 2001 地址和指针的概念 (2) 二、指针: 1.指针:一个变量的地址称为该变量的“指针”。 2.指针变量:如果有一个变量专门用来存放另一变量 的地 址(即指针),则它称为“指针变量”。 3.指针和指针变量的关系: 指针变量的值(指针变量中存 放的值)是指针 (地址)。 4.指向:所谓“指向”就是通过地址来实现 的。 i_pointer i 10 2000 2000 例:假定程序定义了以下变量,指出各个变量的指针各是多少? (假定这些变量在内存中连续存放,且x所占的单元从2000开始) int x=1,y=5; char z=‘B’; float f=9.8; long int m=87; double df=12.4; p 2000 变量x 1 2000 变量y 5 2002 变量z 66 2004 若变量p里面存放着变 变量f 9.8 量x的地址(指针)2000, 则变量p就是变量x的指 变量m 87 针变量,我们就可以说 变量df 12.4 变量p指向变量x 2005 2009 2013 10.2变量的指针和指向变量的指针变量(1) 一、指针变量的定义和赋值: 1、概述:定义的一般形式:基类型 *指针变量名 (1)指针变量不同于整型变量和其他类型变量,它是用来 专门存放地址的。必须将它定义为“指针类型”。 我们用*来标记这一变量为指针变量。 (2)指针变量的基类型用来指定指针变量所指向的变量 的类型,不可缺少。 例如: int i , j ; int *pointer_1 , *pointer_2 ; pointer_1,pointer_2用来指向整型变量,但不可以用来 指向其他类型的变量。 注意:定义时, “*”表示该变量的类型为指针类型; 而指针变量的名称为:pointer_1,pointer_2 变量的指针和指向变量的指针变量(2) 3、赋值:一个指针变量指向另一个变量: 如:pointer_1=&i; pointer_2=&j; 注意:指针变量中只能存放地址(指针),不能将任 何其他非 地址类型的数据赋给一个指针变量。 变量i , j 的地址被分别存放到指针变量pointer_1和 pointer_2中,这样,pointer_1指向了变量i , pointer_2 指向了变量j。 pointer_1=10.0; 是非法的。 示例 如果在定义变量时变量名字前面带*,则所定义 的变量为指针变量 左面的三条语句定义了四个 例:int x,y,*p1; 一般变量x,y,z,w,也定义了 四个指针变量p1,p2,p3,p4, float z, *p2; 其中p1只能用来存放整形变 char *p3,w, * p4; 量的地址,p2只能用来存放 浮点型变量的地址,p3,p4只 根据上面的定 能用来存放字符型变量的地 义,说明下面 址 代码的对错: p1=2000;p1=&y;p2=&w;p2=&z;p3=&w;p4=p3; 变量的指针和指向变量的指针变量(3) 二、指针变量的使用: 1、 两个有关的运算符: (1)& 取地址运算符 (2)* 指针运算符(或称“间接访问”运算符),非定义 时出现,用于求指针(地址)所指向的变量。 例如: int a , * p; p=&a; *p=3; & i 为变量 i 的地址 * p 为指针变量p所指向的变量a 例:分析以下程序,并回答问题 假定程序运行时,从键盘输入了25,78.12,B 程序的结 果如何?(程序P10_1) main() {int a,*pa=&a;float b, *pb;char c, *pc; pb=&b;pc=&c; scanf(“%d,%f,%c”,&a,&b,&c); printf(\n a=%d,b=%.0f,c=%c”,a,b,c); printf(\n a=%d,b=%.0f,c=%c”, *pa, *pb, *pc);} 1、可否把int 语句中a和*pa=&a的位置互换? 2、scanf(“%d,%f,%c”,&a,&b,&c);语句可否换成 scanf(“%d,%f,%c”,pa,pb,pc); 3、两个printf语句可否等价? 指针变量的引用 根据&和*的含义回答以下问题,并弄清楚原因 1、若只执行了int *pa,a;语句,你能确定指针 变量pa指向了哪个变量吗? 2、若再执行pa=&a;语句,你能确定pa的指向 吗?你能确定pa所指向单元的值吗? 3、此时的 *&a与*pa和a是否等价? 4、此时&*pa与pa和&a是否等价? 5、(*pa)++与*pa++的意义如何?是否等价? 注意: *与++的级别相同,但结合方向自右向 左,即*p++等价于*(p++), (*p)++与a++等价, *(p++)是先用p的值,然后p再加1,即p指向了a 后面的一个变量(++作用在p上)。 指 针 变 量 举 例(2) 例2:利用指针,用两种方法,输入变量a,b的值,按 先大后小的顺序输出。 方法1:让指针变量pa指向a,指针变量pb指向b,若a<b, 就交换a和b的值,见程序P10_2.c a b main() 10 5 10 5 {int a,b,t,*pa, *pb; pa=&a;pb=&b; pa pb scanf(“%d,%d”,pa,pb); &a &b if (*pa<*pb) t {t=*pa; *pa=*pb; *pb=t;} 5 printf(“\n a=%d,b=%d”,a,b); printf(“\n max=%d,min=%d”,*pa,*pb); } 方法2:让指针变量pa指向a,指针变量pb指向b,若a<b, 就交换pa和pb的值,见程序P10_3.c a b main() 5 9 {int a,b, *t,*pa, *pb; pa pb pa=&a;pb=&b; &b &a &b &a scanf(“%d,%d”,pa,pb); t &a if (*pa<*pb) {t=pa; pa=pb; pb=t;} printf(“\n a=%d,b=%d”,a,b); printf(“\n max=%d,min=%d”,*pa,*pb); } 注意:方法1和方法2中的变量t有什么区别?为什么 要如此定义?方法1和方法2中各自都交换了什么? 作业: 1、模仿例题2的两种方法,从键盘输入三个 整数,按由小到大的顺序输出。 继续 指针变量(5) 关于& 和 * 的运算(& 和 * 的优先级相同) int a, *pointer_1; (1)若有&*pointer_1 &a pointer_1= &a; 先进行*pointer_i的运算,即为变量a; 再进行&运算,即为&a;表达式的值最终为 a 的地址 (2)若有*&a *pointer_i a 先进行 & a 运算,得到 a 的地址,即pointer_1或 &a; 再进行 * 运算,得到 & a 所指向的变量 a+ + (3)(*pointer_1) + + 注意:括号是必要的。否则就成为*(pointer_i + +)此时, 先按poiter_i的原值进行 * 运算得到a的值,然后 pointer_i的值增加1 , 即pointer_指向a 以后的存储单元, pointer_i不再指向a。 指 针 变 量(6) 三、指针变量作为函数参数: (1) 函数的形参的类型不仅可以是整型、实型、字 符型等数据,还可以是指针类型。 作用:是将一个变量的地址传送到函数中。 ( 2 )函数的类型(返回值的类型)也可以是指针类型 例1、编写函数,交换两个数的值: main() {int a=4,b=15; viod swap(int x,int y); swap(a,b); printf(“a=%d,b=%d\n”,a,b); } void swap(int x,int y) {int t; t=x;x=y;y=t; printf(“x=%d,y=%d\n”,x,y); } a b 4 15 x y 154 15 4 t 4 例1、编写函数,交换两个数的值: main() {int a=4,b=15, *p,*q; void swap(int *p,int *q); p=&a;q=&b; swap(p,q); printf(“a=%d,b=%d\n”,a,b); } void swap(int *m,int *n) { intint t; *t; t=*m;*m=*n;*n=t; t=m;m=n;n=t; printf(“*x=%d,*y=%d\n”,*m,*n) ; } a b a 15 4 4 b 15 4 15 p p &a &a q t t 4 &a q &b &b 例2、利用子函数和指针参数,对输入的两个整 数按大小顺序输出。 算法如下:编写一个子函数,接受两个整形数 据的地址,并负责交换这两个地址里面的内容。 主函数输入两个整数,当两个整数不满足要求 时,调用子函数做交换。最后主函数输出结果 代码如下:*见程序P10_4.c main() {void swap(int *p1,int *p2); int a,b, *pa, *pb; pa=&a;pb=&b; scanf(“%d,%d”,&a,&b); if(a<b) swap(pa,pb); printf(“\n a=%d,b=%d”,a,b); } void swap(int *p1,int *p2) {int temp; temp=*p1; *p1=*p2; *p2=temp;} 根据程序回答: 1、可否把a<b换 成*p<*pb? 2、可否把scanf 语句中的&a,&b 换成pa,pb? 3、可否把输出 语句中的a,b换 成*pa和*pb? 4、可否不使用 pa,pb,而把 swap(pa,pb)换 成swap(&a,&b)? 5、子函数中可否只交换p1与p2的值?即子函数改为如下形式: void swap(int *p1,int *p2) {int *temp; temp=p1; p1=p2; p2=temp;} 6、可否把代 码改为左图 内容?这些 代码交换了 什么? main() {void swap(int x,int y); int a,b,; scanf(“%d,%d”,&a,&b); if(a<b) swap(a, b); printf(“\n a=%d,b=%d”,a,b); } void swap(int x,int y) {int temp; temp=x; x=y; y=temp;} 指 针 变 量 举 例(3) 例3、阅读P10_5.c程序代码,并说明程序的功能 swap(int *p1,int *p2) exchange(int *q1,int *q2,int *q3) { int temp; { if(*q1<*q2) swap(q1,q2); temp=*p1; if(*q1<*q3) swap(q1,q3); *p1=*p2; if(*q2<*q3) swap(q2,q3); } *p2=temp; } 结果: main( ) 9,0,10 {int a,b,c; a=10,b=9,c=0 int *pointer_1,*pointer_2,*pointer_3; scanf("%d,%d,%d",&a,&b,&c); pointer_1=&a; pointer_2=&b; pointer_3=&c; exchange ( pointer_1, pointer_2, pointer_3); printf("\na=%d,b=%d,c=%d\n",a,b,c); } 指针与指针变量 复习巩固 复习: 1、什么是指针? 2、什么是指针变量? 3、指向是什么意思? 4、怎样定义指针变量? 5、怎样给指针变量赋值? 如果有以下定义: int a=3, b, *p, *q, *t, m; a Char c=‘A’,*pc; 0000 0011 则说出下面表示的含义: 2000 2001 2002 p p=&a; pc=&c; p++ &a 2002 q=&b; *q=10; c *p, &a, *&a &*p 10001 (*p)++; *p++; pc++; 4501 4502 4500 t=p; pc pc++ &c 4501 m=*p;*p=*q;*q=m; t=p;p=q;q=t; 2003 4503 判断下面写法的对错: 1、int 2、int 3、int 4、int 5、int a,*p; p=&a; a,*p=&a; a, *p; *p=a; a, *p=a; a; printf(“%d”,*a); 练习: 1、下列程序的输出结果是(6) int b=2; int func(int *a) {b+=*a; return(b);} main() {int a=2,res=2; res+=func(&a); printf(“%d”,res); } 练习: 2、选出正确的程序段(C) A)int p; scanf(“%d”,p); D)int *s, k; B)int *s, k; char *p,e; *s=100; s=&k; C)int *s, k; p=&e; char *p, c; s=p; s=&k; p=&c; *s=1; *p=‘a’; 练习: 3、有如下的程序段: int *p,a=10, b=1; p=&a;a=*p+b; 执行该段程序后,a的值为(11) 4、下列程序的输出结果是(2,0) void fun(int *n) {while((*n)--) printf(“%d”,--(*n)); } main() {int a=4; fun(&a); } 练习: 5、以下程序段的输出结果是(2,1,4,3) void fun(int *x,int *y) {printf(“%d %d “,*x,*y); *x=3;*y=4;} main() {int x=1;y=2; fun(&y,&x); printf(“%d %d “,x,y;) } 练习: 6、下列程序的运行结果是(3 6) void fun(int *a,int *b) {int *k; k=a;a=b;b=k; } main() {int a=3,b=6,*x=&a,*y=&b; fun(x,y); printf(“%d %d “,a,b); } 练习: 7、设有以下程序: main() {int a,b,k=4,m=6,*p1=&k,*p2=&m; a=k; p1=&m; b=(*p1)/(*p2)+7; printf(“a=%d”,a); printf(“b=%d”,b); } 程序执行后,a的值为(4),b的值为(8)。 10.3数组的指针和指向数组的指针变量(1) 概述: 1. 数组的指针:数组的起始地址。 2. 数组元素的指针:数组元素的地址。 一、指向数组元素的指针: 1.定义:定义一个指向数组元素的指针变量的方法,与以 前介绍的指向变量的指针变量相同。 例如: int a[10]; int *p; p=&a[0]; /* 指针 p就 指向了元素a[0] */ 10.3数组的指针和指向数组的指针变量(1) int a[10]; 数组的名字a表示数组的首地址;即:a==&a[0]; 也就是说a本身就是一个地址。 a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9] a p 5 9 7 6 4 3 8 2 0 1 2000 2000 地址可以存到指针变量中。 如果有 int *p; p=a; 因为a就是&a[0],所以也可以写成 p=&a[0]; 数组的指针和指向数组的指针变量 (2) 2、C语言规定数组名代表数组的首地址,即第0个元素 的地址。 故p=&a[0]; 与p=a;等价。 注意: 数组a不代表整个数组,上述“p=a;”的作用是“把a 数组的首地址(恰好是 a[0] 的地址 )赋给指针变量p”, 而不是“把数组a各元素的值赋给p”。 3、 在定义指针变量时可以赋给初值: int *p=&a[0]; 等价于:int *p; p=&a[0]; 或 int *p=a; /*注意:不是*p=&a[0];*/ 10.3数组的指针和指向数组的指针变量(1) int a[10],*p,*q; 若有p=a;是把数组的首地址(也就是a[0]的地址给了p, 此时,*p与a[0]等价。 若有q=&a[5];是把数组a中第5个元素的地址送给q,此 时*q与a[5]等价。 *(a+4) *(p+9) * (p+8) *p *(p+1) *(p+4 a[0] a[1] a[2] a[3]) a[4] a[5] a[6] a[7] a[8] a[9] 9 7 6 4 3 8 2 0 1 a 5 p 2000200120022003 a &a[0] 2000 q &a[5] 2010 若p=a; p++; p指向谁? 若p=a;p=p+4;那么a[4]又可以怎样表示? 既然p=a,那么a[4]还可以表示成什么形式? 那么*(p+5)与*p+5有什么不同? 假定执行了int a[20], *p;回答下列问题: 1、如果再执行p=&a[19]; *p=100;则数组的哪个元素被 赋值,为什么? 2、如果想让p指向数组的第0个元素,该怎么操作?可否 用p=a;为什么? 3、假定如果在定义指针变量p时,直接把a的值送给p, 即int a[20],p=a;能否让p指向数组的开始位置,即指 向a[0]? 4、假定有p=&a[1];如果再执行p++;则p指向了谁?如果 再执行p=p+5;p又指向了谁? 5、如果有p=a;再执行*p=90;这又是在给谁赋值? 6、可否用p来操作所有数组元素? 注意:如果一个指针变量指向了数组的首地址, 则这个指针变量可看作是数组的指针变量。 数组的指针和指向数组的指针变量 (3) 二、通过指针引用数组元素: 1. 概述: (1)已有 int a[10] , *p ; p=a; *p=1; 表示:对 p 当前所指向的数组元素赋予一个值1 ; (2)按C的规定,如果指针变量p已指向数组中的一个元 素,则p+1指向同一数组中的下一个元素(而不是将p的 值简单地加1, 如对于 int 和 char ,p的值变化就不同)。 2. 引用一个数组元素的方法: (1)下标法,如a[i]形式;或者p[i] (2)指针法,如*(a+i)或者*(p+i)。 其中a是数组名,p是指向数组的指针变量,其初值p = a a[i], p[i] *(a+i),*(p+i)都是用来表示数组的第i+1个元素。 即下标为i的元素。 例:判断下面的代码可否完成数组的输入输出操作?见程序 D10_7.c main() {int a[10], *p=a,i; for (i=0;i<10;i++,p++) scanf(“%d”,p); p=a; for (i=0;i<10;i++,p++) printf(“ %d “,*p); } 回答问题: 1、可否去掉p=a;语句 为什么? 2、可否把 for(i=0;i<10;i++,p++) 循环中的,p++去掉, 而把输入语句换成 scanf(“%d”,p++);把输 出语句换成 printf(“ %d “,*p++); 3、可否把两个循环中的p换成数组名a? 数组的指针和指向数组的指针变量 (4) main() { int a[10]; int i; for(i=0;i<10;i++) scanf("%d",&a[i]); printf("\n"); for(i=0;i<10;i++) printf("%d",a[i]);} main() { int a[10]; int i; for(i=0;i<10;i++) scanf("%d",&a[i]); printf("\n"); for(i=0;i<10;i++) printf("%d",*(a+i));} 结果: 1 2 3 4 5 6 7 8 9 0(回车) 1234567890 main() { int a[10] , *p =a ; int i; for(i=0;i<10;i++) scanf("%d", p+i); printf("\n"); for(i=0;i<10;i++) printf("%d",*p++);} 3.注意: (1)要注意指针变量的当前值。 (2)指针变量可以指到数组以后的内存单元。 A数组 (3)注意指针变量的运算。 main( ) { int a[10],i,*p ; p=a; for(i=0;i<10;i++) scanf("%d",p++); printf("\n"); for(i=0;i<10;i++,p++) printf("%d",*p); } p, ( 2000) p+1,a+1, p[1] p+2,a+2, p[2] p+3,a+3, p[3] 1 2 3 4 p+i,a+i, p[i] p+7,a+7, p[7] p+8,a+8, p[8] p+9,a+9, p[9] a[0] a[1] a[2] a[3] a[ i] 8 9 0 a[7] a[8] a[9] 2000 p 通过上面的分析可见数组的指针和数组名之间 存在以下关系(假定有int a[20],*p=a;): 1、p的值可以改变,但a的值不能改变,如 p++;p=p+5;p--;--p等都是正确的,但下列语句 a++;a=a+5;a--;--a等都是错误的。即数组名永远代表 数组的首地址,不能被更改,但数组的指针可以任意 改变指向。 2、除了上面的区别,在使用上数组名和数组的指针几 乎没有区别。即当指针变量p指向数组a的首个元素时, p+i,a+i,&a[i],&p[i]都代表第i个元素的地址, *(p+i),*(a+i), p[i], a[i]都代表第i个元素。p与a在 这点上是等价的。但若p指向的不是首地址,则上面就 不等价。 例:判断下面的代码可否完成数组的输入输出操作?见程序 D10_8.c 和D10_9.c及D10_10.c main() {int a[10], *p=a,i; for (i=0;i<10;i++) scanf(“%d”,p++); for (i=10;i>=1; i--) printf(“ %d “,*(p-i)); } main() {int a[10], *p=a,i; for (i=0;i<10;i++) scanf(“%d”,&p[i]); for (p=a;p<(a+10);p++) printf(“ %d “,*p); } main(){int a[10], *p=a,i; for (i=0;i<10;i++) scanf(“%d”,p++); for (i=0;i<10;i++)printf(“ %d “,*(a+i));} 练习: 1、若有下面的定义:int a[9],*p=a;并在以后的语句中未 改变p的值,不能表示a[3]地址的是() A)p+1 B)a+1 C)p++ D)a++ 2、若有如下图所示的5个连续的int类型的存储单元并赋 值如下图,a[0]的地址小于a[4]的地址。p和s是基类型为 int的指针变量,请对以下问题填空。 a[0] a[1] a[2] a[3] a[4] 22 33 44 55 66 1、若p已指向存储单元a[1],通过指针p给s赋值,使 s指向最后一个存储单元a[4]的语句是() 2、若指针s指向存储单元a[2],p指向存储单元a[0], 表达式s-p的值是() 练习: 3、若有下面的定义:int w[5]={23,54,10,30,98},*p=w; 则不移动指针p,且通过指针p引用值为98的数组元素的 表达式是() 4、如有如下说明:int a[10]={1,2,3,4,5,6,7,8,9,10},*p=a; 则数值为9的表达式是() A)*p+9 B)*(p+8) C)*p+=9 D)p+8 5、以下程序的输出结果是() main() {int arr[ ]={30,25,20,15,10,5},*p=arr; p++; printf(“%d”,*(p+3)); } 练习: 6、以下程序的输出结果是() main() {int a [10 ]={9,8,7,6,5,4,3,2,1,0},*p=a+5; printf(“%d”,*--p); } 7、下面程序的输出结果是() main() {int a [10 ]={1,2,3,4,5,6,7,8,9,10},*p; p=a; printf(“%d”,*p+9); } 指针与指针变量 复 习 • • • • • 指针:地址 指针变量:存放指针(地址)的变量 指向:指针变量指向所存地址对应的变量。 数组的指针和指向数组的指针变量 如果有以下定义: int a[10],*p; p=a; 或者: int a[10],*p=a; 说出下列表示的含义: a,p,&a[0], &a[i],a+i,p+i,&p[i] a[i],*(a+i),*(p+i),p[i]; 用指针实现数组的操作 复 习 • 数组的输入和输出: main() {int a[10], *p=a,i; for (i=0;i<10;i++) scanf(“%d”, &p[i] &a[i]) p+i a+i ; for (i=0;I<10; i++) printf(“ %d “, *(p+i) *(a+i) p[i] ); a[i] } 指针p不移动,始终指向 数组的首地址。 main() {int a[10], *p=a,i; for (i=0;i<10;i++) scanf(“%d”,p++); for (p=a;p<(a+10);p++) printf(“ %d “,*p); } 每做一次循环,指针p都 要向后移动一个位置,循 环结束后,指针p指向数 组的最后一个元素。 数组名作为函数参数 • 数组名作为函数参数,实参向形参传递的是 数组的首地址。 • 我们知道,指针变量又可以存放地址。 所以: 1、当实参为数组名时,形参可以为数组, 也可以为指针变量 2、当形参为数组时,实参可以为数组名, 也可以为指针变量 3、实参与形参的数据类型必须一致。 数组名作为函数参数 判 断 下 列 参 数 传 递 是 否 正 确 zhs(int y[]) {…} main() {int x[20]; ….. zhs(x);} zhs(int y[]) {…} main() {int x[20]; ….. zhs(&x[5]);} zhs(int y[]) {…} main() {int x[20],*p=x; ….. zhs(p);} zhs(int y[]) {…} main() {int x[20],*p=&x[0]; ….. zhs(p);} 数组名作为函数参数 判 断 下 列 参 数 传 递 是 否 正 确 zhs(int *y) {…} main() {int x[20],*p=x; ….. zhs(p);} zhs(int *y) {…} main() {int x[20]; ….. zhs(x);} zhs(int *y) {…} main() {int x[20]; ….. zhs(&x[5]);} zhs(int *y) {…} main() {int x[20],*p=x; ….. zhs(p+5);} 数组名作为函数参数(例题) 例:用子函数和指针从10个浮点型数据中找出 最大值和最小值。(见程序P_12.c) 要求:主函数负责输入输出,子函数负责查找。 回答问题: 1、max和min两个变量为什么要放到所有函数 之外?它们是什么属性的变量? 2、如果把max和min的定义放在程序最后可否? 3、子函数的array变量可否改为int类型?为 什么? 4、程序中的实参与形参各是什么,是指针变 量还是数组名? 5、试着把参数换成各种允许的形式,验证程 序的正确性 数组名作为函数参数(练习) • 练:用子函数和指针,将数组a中的n个整数反序存放。 子函数fxcf负责对接受到的起始地址开始的n个元素 进行反序存放,主函数负责输入输出,见程序 x[0] P10_11.c,并回答以下问题: void fxcf(int x[],int n) x[1] {函数的代码完成对 x[0]到x[n-1]之间的元 素反序存放 } main() {负责输入数组 调用子函数 输出交换后的数组} x[2] x[n-3] x[n-2] x[n-1] 数组名作为函数参数 思 1、子函数中的形参数组x可否换成指针变量x? 既子函数的定义为void fxcf(int *x,int n) 考 : 2、主函数中的实参数组名a可否换成&a[0]或者 一个相应的指针变量?即调用子函数时用 fxcf(&a[0] ,20);或fxcf(p);其中p为整形指针 变量且p=a; 3、如果主函数在调用子函数时采用 fxcf(a,10); ,程序的功能如何? 4、可否把子函数中的x[i]和x[j]都改为*(x+i) 和*(x+j),而不管形参是数组还是指针? 数组名作为函数参数 课 作业:用子函数和指针完成对一个整形数 后 组的排序(用冒泡法完成),并在主函数 练 中调用它完成一组数据的排序。 习 : 对于一维字符数组(字符串) 复 习 : • 对于一个字符串,可以存放到一个字符数 组中: • 例如: main( ) { char s []="I love China!”; printf(“%s\n”,s); } /* string是数组名,代表字符数组的首地址 */ • 字符串的输入与输出 scanf(“%s”,s); gets(s); 例如有如下定义:char s[20]; printf(“%s”,s); puts(s); 指向字符串的指针 指 • 因为字符串一般存放到一维字符数组中, 故指向字符串的指针可以看成是指向一维 向 字符数组的指针 一 • 例如: 维 main( ) 字 {char s []="I love China!”; 符 char *p; p=s; 数 printf(“%s\n”, s ); 组 } 的 用指针指向一个字符串以后,可以用指针 指 对字符串进行操作。 针 指向字符串的指针 指 向 一 维 字 符 数 组 的 指 针 • 字符串赋值与输出: main( ) { char s []="I love China!”; printf(“%s\n”, s ); } main( ) { char s[20] , *p; p=s ; p="I love China!”; printf(“%s\n”, p ); } 法2与法3有什么不同? main( ) { char *p="I love China!”; printf(“%s\n”, p ); } main( ) { char s[20] , *p; p=s ; gets(s); gets(p ); puts(s); puts(p ); } 指向字符串的指针 例 题 : • 例:从键盘输入一个字符串,输出整个字 符串,并输出这个字符串中下标为3的元素。 • 例:自己编写一个函数,完成字符串的复 制(不使用库函数) 字符数组与字符指针变量的讨论 • 字符数组由若干个元素组成,每个元素中放 一个字符 • 字符指针变量中存放的是地址(字符串第一 个元素的地址),只是指向这个字符串,而 决不是将字符串放到字符指针变量中。 • 赋值方式:对字符数组只能对各个元素赋值, 下面的方法是错的: char s[20]; s=“I love China.” 对字符指针变量,char *p; p=“I love China.” 但注意,赋给p的不是字符,而是字符串第一个 元素的地址。 字符数组与字符指针变量的讨论 • • • • • • 判断下列赋值方式的对错: char s[20] =“I love China.”; char s[20] ;s[20]=“I love China.”; char *p =“I love China.”; char *p; p =“I love China.”; 指针变量的值是可以改变的,但数组的名字 的值不能改变 字符数组与字符指针变量的讨论 • 用指针变量指向一个字符串使用更加方便。 • 例:char *format; format=“\na=%d,b=%d”; printf(format, a,b);等价于 printf(“\na=%d,b=%d”,a,b); 也可以: • char format[]=“\na=%d,b=%d”; printf(format, a,b);等价于 printf(“\na=%d,b=%d”,a,b); • 但是: • char format[]; • format=“\na=%d,b=%d”; printf(format, a,b); 练习 1.以下程序的输出结果是( )。 #include <stdio.h> #include <string.h> main( ) {char *p="abcde\Ofghjik\0"; printf("%d\n",strlen(p)); } A)12 B)15 C)6 D)5 练习: 2.如下程序的输出结果是( )。 #include <stdio.h> main( ) {char s[]="ABCD", *p; for(p=s+1; p<s+4; p++) printf ("%s\n",p); } D)BC C)B A)ABCD B)A D C B BCD D CD C CD D D D 3.有以下程序: #include <stdio.h> void ss(char *s,char t) { while(*s) { if(*s==t)*s=t-′a′+′A′; s++;}} main( ) { char str1[100]="abcddfefdbd",c=′d′; ss(str1,c); printf("%s\n",str1);} 程序运行后的输出结果是( )。 A)ABCDDEFEDBD B)abcDDfefDbD C)abcAAfefAbA D)Abcddfefdbd 4.下面程序的输出结果是______。 #include <stdio.h> main( ) { static char a[]="language",b[]="program"; char *ptr1=a, *ptr2=b; int k; for (k=0;k<7;k++) if(*(ptr1+k)==*(ptr2+k)) printf("%c",*(ptr1+k)); } 5.下面程序的输出结果是______。 #include <stdio.h> main( ) { static char a[]="language"; char *ptr=a; while (*ptr) { printf("%c",*ptr-32); ptr++; } } 6.下面程序的输出结果是______。 #include <stdio.h> char *p="abcdefghijklmnopq"; main( ) { while (*p++!=′e′); printf("%c\n",*p);} 7、当运行以下程序时,从键盘输入Happy!<CR>(<CR> 表示回车),则下面程序的运行结果是______。 #include <stdio.h> #include <string.h> main( ) { char str[10],*p=str; gets(p); printf("%d\n",stre (p)); } stre (char str[]) { int num=0; while (*(str+num)!=′\0′)num++; return(num); } 8.下面程序的运行结果是______。 #include <stdio.h> #include <string.h> main( ) { char *p1,*p2,str[50]="abc"; p1="abc";p2="abc"; strcpy(str+1,strcat(p1,p2)); printf("%s\n",str);} 二维数组的地址 • 设有如下定义:int a[3][4]; • 则: a[0][0] a[0][1] a[0][2] a[0][3] a[0] a[1][0] a[1][1] a[1][2] a[1][3] a[1] a[2][0] a[2][1] a[2][2] a[2][3] a[2] 由于二维数组可以看作是由一维数组构成的,所以数 组a可以看作有3个一维数组a[0]、a[1]、a[2]构成, 而它们又分别由4个元素构成 也就是说:二维数组a由三个元素构成,这三个元素 都是一维数组。 二维数组的地址 对于普通的一维数组a来说: 元素地址: a+0 元素值:*(a+0) a a[0] a+1 *(a+1) a+2 a+3 *(a+2) *(a+3) a[1] a[2] a[3] 同样对于一维数组a[0]来说: 元素地址:a[0]+0 a[0]+1 a[0]+2 a[0]+3 元素值: *(a[0]+0) *(a[0]+1) *(a[0]+2) *(a[0]+3) a[0] a[0][0] a[0][1] a[0][2] a[0][3] 二维数组的地址 同样对于一维数组a[1]和a[2]来说: a[0]+0 a a+1 a[0]+1 a[0]+2 a[0]+3 a[0][3] a[1]+3 a[0] a[1][0] a[1][1] a[1][2] a[1][3] a[2]+0 a[2]+1 a[2]+2 a[2]+3 a[1] a[0][0] a[0][1] a[0][2] a[1]+0 a[1]+1 a[1]+2 a[2] a[2][0] a[2][1] a[2][2] a[2][3] a+2 思考: 1、二维数组a有三个元素:数组a[0]、a[1]和a[2],那么a 应该存放的是首地址:即&a[0],a++就是移动一个元素的 位置,即移动一行。我们说a是指向行的。 2、一维数组a[i]有4个元素:即a[i][0]、a[i][1]、a[i][2]和 a[i][3]。那么a[i]应该存放的是首地址:即&a[i][0], a[0]++就是移动一个元素的位置,即移动一个元素。我们 说a[i]是指向列的。 二维数组的地址 a[0]+0 a a[0]+1 a[0]+2 a[0]+3 a[0][0] a[0][1] a[0][2] a[1]+0 a[1]+1 a[1]+2 a[0][3] a[1]+3 a+1 a[1][0] a[1][1] a[1][2] a[1][3] a[2]+0 a[2]+1 a[2]+2 a[2]+3 a+2 a[2][0] a[2][1] a[2][2] a[2][3] 那么: a[0][2]可以表示为*(a[0]+2) a[2][3]可以表示为*(a[2]+2) 第i行第j列的元素a[i][j]应该如何表示? *(a[i]+j) a[0] a[1] a[2] 二维数组的地址 第i行第j列的元素a[i][j]前 面有 i*4+j个元素,故a[i][j] 的位置为: &a[0][0]+(i+*4+j) a(2000) *a[0] a[0][1] *(a[0]+1) a[0][2] a[i][j]也可以写成: *(a[0]+(i*4+j)) a[0][3] a+1 (2008) a[0] ---a[0][0] a[0][1] a[0][2] a[0][3] a a[0][0] a[1] ---a[1][0] a[1][1] a[1][2] a[1][3] a[1][0] a[1] 二维数组的地址 a[0]+0 a a[0]+1 a[0]+2 a[0]+3 a[0][0] a[0][1] a[0][2] a[1]+0 a[1]+1 a[1]+2 a[0][3] a[1]+3 a+1 a[1][0] a[1][1] a[1][2] a[1][3] a[2]+0 a[2]+1 a[2]+2 a[2]+3 a+2 a[2][0] a[2][1] a[2][2] a[2][3] a[0] a[1] a[2] 设&a[0][0]为2000,那么,a[0]里面放的是a[0][0]这个 元素的首地址:a[0]=&a[0][0]=2000;*(a[0])=a[0][0]。 a里面放的是a[0]这个元素的首地址,即a=&a[0]=2000 。 所以*a=a[0]=&a[0][0]=2000;也就是说:*可以把行地址变 为列地址,相反地,&运算可以把列地址转换为行地址。 故:元素a[i][j]的地址又可写为:*(a+i)+j 元素a[i][j] 又可写为:*(*(a+i)+j) 二维数组的地址 说出下列表示的含义:设&a[0][0]=2000 表示形式 a &a[0] a[0],*(a+0),*a a+1,&a[1] a[1],*(a+1) a[1]+2,*(a+1)+2,&a[1][2] *(a[1]+2), *(*(a+1)+2), a[1][2] 含义 值 2000 第0行首地址 第0行第0列的 2000 地址 第1行首地址 2008 第1行第0列的 2008 地址 第1行第2列的 2012 地址 第1行第2列的 元素 思考:a与*a,a[1]与&a[1]的值相同,但他们有什么不同? 指向二维数组的指针变量 • 设有如下的定义:int a[3][4],*p; • 请问可以这样赋值么? • 1、 p=&a[0][0]; • 2、 p=a[0]; • 3、 p=*a; • 4、 p=a; • 为什么? 如果令p=&a[0][0],那么元素a[i][j]应该如何表示? 指向二维数组的指针变量 若定义int a[3][4], *p; a(2000) p=&a[0][0]; 元素a[i][j]的地址用p表示 为:p+i*4+j 元素a[i][j]用p可表示为: *(p+i*4+j) *a[0] a[0][1] *(a[0]+1) 第i行第j列的元素a[i][j]前 面有 i*4+j个元素,故a[i][j] 的位置为: &a[0][0]+(i+*4+j) a[i][j]也可以写成: *(a[0]+(i*4+j)) a[0][0] a[0][2] a[0][3] a+1 (2008) a[1][0] a[1] 指向二维数组的指针变量 • • • • • • a 如何令一个指针指向一行? 定义 指向数组的指针变量: 定义格式:类型 (*指针名)[个数] 如:int (*p)[4]; 若有定义int a[3][4],(*p)[4]; 则可以p=a; 那么元素a[i][j]的地址又可以怎样表示? p 元素a[i][j]又可以怎样表示? a[0] a[0][0] a[0][1] a[0][2] a[0][3] a+1 a[1][0] a[1][1] a[1][2] a[1][3] a[1] a+2 a[2][0] a[2][1] a[2][2] a[2][3] a[2] p+1 p+2 指向二维数组的指针变量 练 习 : 1、下列程序的执行结果是() main() {int a[3][3],*p,I; p=&a[0][0]; for(I=0;I<9;I++) p[I]=I+1; printf(“%d”,a[1][2]); } 2、下列程序的输出结果是() main() {int a[3][3]={1,2,3,4,5,6,7,8,9},(*p)[3],I; p=a; printf(“%d”,*(*(p+2)+1)); } 指针数组 如果一个数组的元素都是指针变量,则这个数 组称为指针数组。 定义方式:类型 *p[数组大小]; 例如:char *p[3]; 定义了一个指针数组,包含三个指针元素。 注意与指向数组的指针变量的区别。 指针数组常用于处理多个字符串。 指针数组 • 例如:用指针数组处理多个字符串。 main() {char *name[]= {“China”,”English”,”Japan”,”Americ”}; int I; for(I=0;I<3;I<++) printf(“\n%s”,name[I]); } 多级指针 • 指向指针的指针 定义方式: 类型 **指针变量名; 例如:int **p, *q, I; I=5; q=&I; p=&q; printf(“%d”,**p); } 返回值为指针的函数 • 当一个函数的返回值为地址时,称函数的返 回值为指针,该函数成为指针型函数。 • 指针函数的定义如下: • 数据类型 *函数名(形参列表) {…… return(指针变量); } 指向函数的指针 • 函数名代表函数的首地址。 • 数据类型 (*指针变量名)( ); 1.如下程序的输出结果是( )。 #include <stdio.h> main( ) {char ch[2][5]={"6937","8254"},*p[2]; int i,j,s=0; for(i=0;i<2;i++)p[i]=ch[i]; for(i=0;i<2;i++) for(j=0;p[i][j]>′\0′;j+=2) s=10*s+p[i][j]-′0′; printf("%d\n",s); } A)69825 B)63825 C)6385 D)693825 2.在说明语句:int *f( );中,标识符f代表的是( )。 A)一个用于指向整型数据的指针变量 B)一个用于指向一维数组的行指针 C)一个用于指向函数的指针变量 D)一个返回值为指针型的函数名 3.设有以下语句,则( )不是对a数组元素的正确 引用,其中0≤i<10。 int a[10]={0,1,2,3,4,5,6,7,8,9}, *p=a; A)a[p-a] B)*(&a[i]) C)p[i] D)*(*(a+i)) 4.以下程序的输出结果为( )。 #include <stdio.h> main( ) {char *alpha[6] ={"ABCDEFGH","IJKL","MNOP","QRST","UVWX"}; Char ; int i; p=alpha; for(i=0;i<4;i ++ ) printf("%s",p[i]); printf("\n"); } A)ABCDEFGHIJKL B)ABCD C)ABCDEFGHIJKLMNOPQRST D)AEIM 5.以下程序通过函数指针p调用函数fun( ),请 在空格处写出定义变量p的语句。 void fun(int *x,int *y) {…} main( ) { int a=10,b=20; ______;/* 定义变量p */ p=fun; p(&a,&b); … }
© Copyright 2024 ExpyDoc