第十章指针
课题:第十章指针§1-§2
教学目的:1、了解指针与地址的概念
2、掌握指针变量的定义、初始化及指针的运算
教学重点:教学难点:指针变量的定义、初始化及指针的运算指针的运算
步骤一复习引导
指针是C语言的一个重要概念,也是C语言的一个重要特色。正确而灵活地运用它,可以有效地表示复杂的数据结构;能动态分配内存;能方便地使用字符串;有效而方便地使用数组;在调用函数时能得到多于1个的值;能直接处理内存地址等,这对设计系统软件是很必要的。
指针的概念比较复杂,使用也比较灵活,因此初学者时常会出错,务请在学习本章内容时十分小心。
步骤二讲授新课
§10.1 地址和指针的概念
计算机的主存储器被分成一个个存储单元,为了区分各存储单元,要为每个存储单元编号,这个编号即地址。
例:i =3; 或scanf(“%d”, &i);
是将3送给i所在的空间。
例:将3送到变量I_pointer所“指向”的单元(即I所标志的单元)。
所谓“指向”,是通过地址来体现的,I_pointer中的值为2000,它是变量I 的地址,这样就在I_pointer和变量之间建立起一种联系,即通过I_pointer能知道I的地址,从而找到变量I 的内存单元。因而在C语言中,将地址形象化地称为“指针”。
意思是通过它能找到以它为地址的内存单元。一个变量的地址称为该变量的“指针”。
内存单元的地址和内存单元的内容是两个不同的概念。
指针:就是地址,即内存单元的编号。
指针变量:用来存放另一变量的地址(即指针)的变量。
如:地址2000是变量i的指针;i_pointer是指针变量,其值就是指针2000。
§10.2变量的指针和指向变量的指针变量
变量的指针就是变量的地址。
存放变量地址的变量是指针变量,用来指向另一个变量。
*i_pointer 表示i_pointer 所指向的变量。
一、定义一个指针变量
指针变量的定义包含三个方面的内容:
⑴指针类型说明,即定义变量为一个指针变量
⑵指针变量名
⑶变量值(指针)所指向的变量的数据类型。
格式:存储类型基类型*指针变量名;
例:int *pointer_1, *pointer_2;
float *pointer_3;
char *pointer_4;
二、指针的引用
指针变量有两个运算符:
& :取地址运算符
功能:取变量地址;单目,右结合。
* :取内容运算符(“间接访问”运算符)
功能:只能跟地址,取变量所指向单元的内容;单目,右结合。
例:&a为变量a的地址,
*p 为指针变量p所指向的存储单元。
例:int a=5, *p=&a;
printf ( “%d”, *p );
main()
{ int a,b;
int *pointer_1,*pointer_2;
a=100;b=10;
pointer_1=&a; /*把变量a的地址赋给pointer_1*/
pointer_2=&b; /*把变量b的地址赋给pointer_2*/
printf(“%d,%d\n”,a,b);
printf(“%d,%d\n”,*pointer_1, *pointer_2);
}
输出结果:100, 10
100, 10
评注:
1、在第3行虽然定义了两个指针变量,只是提供了两个指针变量,但并未指向任何一个整型变量。称为指针“悬空”。
2、最后一行的*pointer_1和pointer_2就是变量a和b。
3、程序中两处出现*pointer_1等,含义不同。程序第3行中的*pointer_1表示定义指针变量pointer_1。它前面的*只是表示该变量是指针变量。程序最后一行中的*pointer_1则代表变量,即pointer_1所指向的变量。
4、第5行中的pointer_1=&a 是将a的地址赋给指针变量pointer_1,而不是*pointer_1。
注意:不应写成:*pointer_1=&a;
5、从上例中可看出,*pointer_1等价于a,*pointer_2等价于b,故凡在程序中出现a的地方均可用*pointer_1 代替。所以,若有:int x, y, *px=&x ;
则下面的运算均是正确的:y=*px+1; /*把x 的内容加1 送变量y*/
printf(“%d\n”, px ); /*打印当前x 的内容*/
d=sqrt((double) px); /*把x的平方根送变量d*/
px=0; /*把x 置为0*/
*px+=1; /*把x 的值加1*/
(*px)++; /*使x 的值加1*/
y=(*px)++; /*即y=x, x++*/
6、假设px和py都被定义为指向int 对象的指针,而且px当前已指向了int 型变量x,则执行:
py=px; /*把指针px的内容拷贝到指针py中去*/
即py和px 这两个不同的指针指向了同一对象x
7、指针不是整数,不能将它与整数混为一谈。
例:
# include
main()
{ int x=100,*p;
p=x;
printf(“%d”, p );
}
例如:
# include
main()
{ int a, b, *d=&a;
b = d;
printf( “%d \n”, b );
……
}——编译不出错,但得不到希望的值
关于&和*运算符的说明:
假设已执行pointer_1=&a;
1、&*pointer_1含义是什么?
&*pointer_1与&a相同,即变量a的地址。
2、*&a的含义是什么?
先进行&a运算,得a的地址,再进行*运算。
*&a、*pointer_1及变量a等价。
3、(*pointer_1) + + 相当于a + +。
它与*pointer_1 + + 不同。
4、*pointer_1 + + 等价于*(pointer_1 + +),即先进行*运算,得到a的值,然后使pointer_1的值改变,这样pointer_1不再指向a了。
例10.2:输入a和b两个整数,按先大后小的顺序输出a和b。
main()
{ int *p1,*p2,*p, a, b;
scanf(“%d,%d”,&a,&b);
p1=&a; p2=&b;
if (a
{p=p1; p1=p2; p2=p;}
printf(“\n a=%d,b=%d\n”,a,b);
printf(“max=%d,min=%d\n”,*p1,*p2);
}
运行情况:
5,9
a=5,b=9
max=9,min=5
三、指针变量作为函数参数
例10.3对输入的两个整数按大小顺序输出。
先考察如下程序,看是否能得到预期的结果
swap(int p1, int p2)
{ int temp;
temp = p1;
p1 = p2;
p2 =temp;
}
main()
{ int a, b;
scanf(“%d, %d”, &a, &b);
if(a
printf(“\n%d,%d\n”,a,b);
}
不能得到预期的结果。
改为:
swap(int *p1,int *p2)
{ int temp;
temp = *p1;
*p1 = *p2;
*p2 =temp;
}
main()
{ int a,b;
int *pointer_1,*pointer_2;
scanf(“%d,%d”,&a,&b);
pointer_1=&a;
pointer_2=&b;
if(a
print f(“\n%d,%d\n”,a,b);
}
注:如果想通过函数调用得到n个改变的值,可以:
1、在主调函数中设n 个变量,用n个指针变量指向它们;
2、然后将指针变量作实参,将这n 个变量的地址传给所调用的函数的形参;
3、通过形参指针变量,改变该n个变量的值;
4、主调函数中就可以使用这些改变了值的变量。
四、指针(变量)的初始化
指针置初始化值的格式:
存储类型基类型*指针名=初始化值;
如:main()
{ static int a;
int *p=&a, *p1=p;
…… }
再如:int *p = 0;
或int *p = NULL;
五、指针的运算
1、指针的算术运算
指针仅能进行加、减算术运算
如:p+n , p-n , p++ , p-- , ++p , --p
p-= n , p+= n , p1-p2 等
其中n是整数,p、p1、p2均为指针;
施行加法运算时,指针向地址增大的方向移动;
施行减法运算时,指针向地址减小的方向移动;
移动长度取决于指针的基类型,由计算机决定;
如有:int a,b,c, *pt =&a;
则pt++ 则指针向后移动两个字节;
再如:main()
{ int *p1,a=8,b=3;
p1=&a;
printf(“%d,%d\n”, (*p1)++, *p1++);
printf(“%d,%d\n”,a, *p1);
}
运行结果:3 ,8
8 ,4
注:p1+k = p1+k*sizeof(p1的基类型);
p1- k = p1- k*sizeof(p1的基类型);
如:
strlen(char *s)
{ char *p=s;
while(*p!=…\0‟)p++;
return(p-s);
}
2、指针的关系运算
设指针p1,p2指向同一数组中的元素,则
p1 p1 p1= =p2:表示p1、p2指向数组中的同一元素; 同理可推出>、>=、<=、!=比较的意义; 不可将指针与其他类型的对象作比较; 若两指针指向不同数组中的元素,也不可比较; 允许将指针与NULL或数值0进行= =或!=的比较,以便判定一个指针是否为空指针。步骤三课堂小结 本课介绍了指针与地址的概念,指针变量的定义、初始化及指针的运算。 指针:就是地址,即内存单元的编号。 指针变量:用来存放另一变量的地址(即指针)的变量。 例如:int a=5, *p=&a; printf ( “%d”, *p ); 注意:运算符*和&的用法,指针变量的自加自减运算。 步骤四布置作业 课后作业:第十章课后练习10.110.2 课题:第十章指针§3 教学目的:掌握指针与数组的知识 教学重点:教学难点:指向数组的指针变量指向二维数组的指针 步骤一复习引导 上节课介绍了指针变量的定义及其赋值。一个变量有地址,一个数组包含若干元素,每个数组元素都在内存中占用存储单元,它们都有相应的地址。指针变量既然可以指向变量,当然也可以指向数组和数组元素(把数组起始地址或某一元素的地址放到一个指针变量中)。 步骤二讲授新课 所谓数组的指针是指数组的起始地址,数组元素的指针是数组元素的地址。 引用数组元素可以用下标法(如a[3]),也可以用指针法,即通过指向数组元素的指针找到所需的元素。使用指针法能使目标程序质量高(占内存少,运行速度快)。 一、指向一维数组的指针 定义形式: int a[10]; int *p; p=&a[0]; 或p=a; 含义:把数组的首地址赋给指针变量p。 也即:int *p=&a[0]; 或int *p=a; 二、通过指针引用数组元素 按C的规定:如果指针变量p已指向数组中的一个元素,则p+1指向同一个数组中的下一个元素(而不是简单地加1)。 如果p的初值为&a[0],则: p+i ↔ a+i ↔ &a[i],即指向a数组的第i个元素。 *(p+i) ↔ *(a+i) ↔a[i]。 指向数组的指针变量也可以带下标,如p[i]与*(p+i)等价 引用数组元素时,可以用: 1、下标法,如:a[i] 2、指针法,如:*( a+i ) 或*( p+i ) 其中,a是数组名,p是指向数组的指针 例10.5:输出数组中的全部元素 假设有一个a数组,整型,有10个元素。用三种方法输出各元素的值: 1、下标法: main() { int a[10], i ; for( i=0; i<10 ; i++) scanf(“%d”,&a[i]); printf(“\n”); for( i=0 ; i<10; i++) printf(“%d”,a[i]); } 2、利用数组名计算数组元素地址,找出元素的值。 main() { int a[10], i ; for( i=0; i<10 ; i++) scanf(“%d”,&a[i]); printf(“\n”); for( i=0 ; i<10; i++) printf(“%d”,*(a+i)); } 3、用指针变量指向数组元素。 main() { int a[10], *p, i ; for( i=0; i<10 ; i++) scanf(“%d”,&a[i]); printf(“\n”); for( p=a; p<(a+10); p++) printf(“%d”,*p ); } 评注: 1、第1和2种方法执行效率是相同的。 2、第3种方法效率高。 3、用下标法比较直观。 在使用指针变量时,有几个问题要注意: 1、指针变量可以实现使本身的值改变。如:for(p=a; p<(a+10); p++) 2、要注意指针变量的当前值。 如:要通过指针变量输出a数组的10个元素。 main() { int a[10], i , *p=a; for( i=0; i<10 ; i++) scanf(“%d”, p++); printf(“\n”); for( i=0 ; i<10; i++,p++) printf(“%d”, *p); } 这个程序输出的并不是a数组中各元素的值。因为第一个for 循环结束时,p已经指向数组的末尾。再继续下去,p指向的是a数组下面的10个元素,是不可预料的。 可加一句为:p=a; 3、用指针变量p指向数组元素,可以指到数组以后的内存单元。C编译程序不作下标越界检查。 4、注意指针变量的运算。如果p指向数组a, ⑴p++ (或p+=1 ),使p指向下一元素a[1]。 ⑵*p++等价*(p++)。作用是先得到p指向的变量的值(即*p),然后再使p+1→p。 ⑶*(p++)与*(++p)不同。前者为a[0],后者为a[1] ⑷(*p)++表示p指向的元素值加1,即(a[0])++ ⑸如果p当前指向a数组中第i个元素,则: *(p- -)相当于a[i- -],先对p进行*运算,再使p自减; *(+ + p)相当于a[+ +i],先使p自加,再作*运算。 *(- - p)相当于a[- -i],先使p自减,再作*运算。 三、数组名作函数参数 用数组名作实参,在调用函数时是把数组的首地址传送给形参。即实参数组与形参数组共占同一段内存。 例10.7 将数组a中的n个整数按相反顺序存放。 分析:将a[0]与a[n-1]对换,再将a[1]和a[n-2]对换……,直到将a[(n-1)/2]与a[n-int((n-1)/2]对换。设i和j,i的初值为0,j的初值为n-1。将a[i]和a[j]交换,然后使i的值加1,j的值减1,再将a[i]和a[j]交换,直到i=(n-1)/2为止。 void inv(int x[], int n) { int temp, i, j,m=(n-1)/2; for( i=0; i<=m; i++) { j=n-1-i ; temp=x[i]; x[i]=x[j]; x[j]=temp; } return; } main() { int i, a[10]={3,7,9,11, 0,6,7,5,4,2}; printf(“The array: \n ”); for( i=0; i<10; i++) printf(“%d”,a[i]); printf(“\n”); inv(a,10); printf(“The array : \n”); for( i=0; i<10; i++) printf(“%d”,a[i]); printf(“\n”); } 例10.8 从10个数中找出其中最大值和最小值。 int max,min; void max_min_vlue(int array[], int n) { int *p,*array_end; array_end=array+n; max=min=*array; /*等价max=min=array[0]*/ for(p=array+1;p if(*p>max) max=*p; else if (*p return; } main() { int i, number[10]; printf(“enter 10 integer number:\n”); for( i=0; i<10; i++) scanf(“%d”,&number[i]); max_min_value(number,10); printf(“\n max=%d,min=%d\n”,max.min); } 如果有一个实参数组,想在函数中改变此数组的元素的值,实参与形参的对应关系有以下4种情况: 1、形参和实参都用数组名; main() { int a[10]; f (a,10);…… } f ( int x[],int n ) { …… } a和x指的是同一个数组 2、实参用数组名,形参用指针变量; main() { int a[10]; f( a, 10 ); …… } f( int *x, int n ) { …… } 开始时,x指向a[0] 3、实参形参都用指针变量; main() { int a[10], *p; p = a; f ( p, 10 ); …… } f ( int *x, int n ) { …… } 实参p和形参x都指向数组a 4、实参为指针变量,形参为数组名。 main() { int a[10], *p; p = a; f( p, 10 ); …… } f ( int x[], int n ) { …… } p指向数组a,x和a共用同一段内存单元 例10.9 用实参指针变量将a数组中n 个整数按相反顺序存放. void inv(int *x, int n) { int *p, m, 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; } main() { int i, a[10],*p=a; printf(“The original array: \n ”); for( i=0; i<10; i++,p++) scanf(“%d”, p); printf(“\n”); p=a; inv(p,10); printf(“The array is : \n”); for( p=a; p printf(“%d”, *p); printf(“\n”); } 例10.10:用选择法对10个整数排序。 main() {int *p, i, a[10]; p=a; for( i=0; i<10; i++) scanf(“%d”,p++); p=a; sort(p,10); for( p=a, i=0; i<10; i++) { printf(“%d”,*p);p++;} } sort(int x[],int n) { int i, j, k, t; for( i=0; i { k=i; for(j=i+1; j if(x[j]>x[k]) k=j; if( k !=i) { t = x[i]; x[i] = x[k]; x[k] = t; } } 四、指向二维数组的指针和指针变量 1、二维数组的地址如: int a[3][4] A a[0] a[0] [0] a[0] [1] a[0] [2] a[0] [3] A+1 a[1] a[1] [0] a[1] [1] a[1] [2] a[1] [3] A+2 a[2] a[2] [0] a[2] [1] a[2] [2] a[2] [3] 例:int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23}; 设首地址为2000。 ●a[k]+p ( 0<=k< i , 0<=p 由此得到: *(a[k]+p) 等价于a[k][p]; ●a[0]与&a[0][0]等价; ●a+k与&a[k][0]等价,而a[0]+k与&a[0][k]等价; ●*(a+i)可理解为a[i],故有如下等价关系: a+0 等价于a[0]、a[0]+0、&a[0][0]、*(a+0) a+1 等价于a[1]、a[1]+0、&a[1][0]、*(a+1) a+(i-1) 等价于a[i-1]、a[i-1]+0、&a[i-1][0]、*(a+i-1) ●*(a+k)与*a[k]是不同的,前者相当于a[k],是一个地址值;后者相当于*(a[k]+0)或 *&a[k][0],是数组元素a[k][0]中存储的值。 ●数组元素a[k][p]就是*(a[k]+p),即*(*(a+k)+p) 步骤三课堂小结 本课介绍了指向数组的指针,主要是指向一维数组的指针。用指针变量p指向数组a,指针变量p可以++、--,表示指向数组的上一元素或下一元素。但C编译程序不作下标越界检查。使用指针既方便有灵活,但初学者容易搞错。 步骤四布置作业 课后作业:第十章课后练习10.3110.5 课题:第十章指针§3-§4 教学目的:在掌握指针与数组的基础上,掌握字符串的指针与指向字符串的指针变量 教学重点:教学难点:指向字符串的指针变量用指针处理字符串 步骤一复习引导 上节课介绍了指向一维数组的指针及二维数组的地址,指向一维数组的指针也即指向元素的指针,那么指向二维数组的指针是怎样的呢?它有两种类型。 步骤二讲授新课 2、指向二维数组的指针变量 ——(1)指向数组元素的指针变量 设指针变量p=a[0] (&a[0][0]、a、a+0、*(a+0) ) 计算a[i][j]在n*m数组中的位置: 例:…… for( i=0; i<3; i++) {for( j=0; j<4; j++ ) printf (“%4d”, *(p+i*m+j)); printf(“\n”); } …… 例:用指针变量输出数组元素的值。 main() { int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23}; int *p;