第 10章 指针
指针 ----C语言的精华
10, 1 地址和指针的概念
指针就是 地址 。有变量的、数组的、函数
的、字符串的指针等。
一、内存的访问方式
1、直接访问
如, i=2,j=6,k=9,通过变量 i,j,k 来
访问存储单元。
2、间接访问
如变量 i,不知道其值及地址,而只知道
它的地址放在 3010,3011中,因此可通过
存放地址的单元找到变量 i 的值,此为间接
访问。
对间接访问,可定义一变量存放地址,如
定义 ipointer,赋值 ipointer=&i
2000
2002
2004
3010
i
j
k
i_p
3
6
9
2000
,
,
,
内存用户数据区
,
,
二、指针的概念
一个变量的地址称为该变量的,指针,。
i的地址是 2000,则地址 2000是变量 I的指针。
存放地址的变量叫“指针变量”,指针变量存放地址。
ipointer存放的是变量 I的地址。
i 变量
ipointer 指针变量 ipointer=&i
10, 2 变量的指针和指向变量的指针变量
一、指针变量的定义
一般形式,类型标识符 *变量名
如,int *p1;
char *p2;
float *p3;
说明,(1) * 表示指针型变量,意为“指向 …,.的指针,
(2) 指针变量有确定的指向的数据类型。
思考,指针变量存放的是一个地址,为什么定义时需要指定类型?
二、指针变量的引用
1、给指针变量赋值 ( 赋变量的地址)
如,p1=&i 一般不直接赋数值。
*p1的意义为 p1所指向的变量 i;
*p与 i等价,i=3,则 *p1也为 3。
注意,p1是地址变量,而 *p 1是非地址变量,与 i等价。
,*”:为指针运算符或叫指向运算符、间接运算符,为
单目运算符。
2000 3
P1 *p1
i
2000
2、用指针变量对变量赋值
如,int I,j,*p;
p=&I;
*p=3;
j=*p+2;
定义后 p=&I后 *p=3后
I
j
p
,
,
,
,
1000
1002
1004
I
j
p
1000
1002
1004
I
j
p
1000
1002
1004
I
j
p 1000
3
1000
1000
1002
1004
j=*p+2后
3
5
1000
例 10.1
main()
{ int a,b;
int *pointer1,*pointer2;
a= 100; b=10;
pointer1= &a; /*把变量 a的地址赋给 pointer_1*/
pointer2=&b; / *把变量 b的地址赋给 pointer_2*/
printf (, % d,% d\n”,a,b);
printf(, % d,% d\n”,*pointer1,* pointer2);
}
关于运算符 &和 * 的几点说明,
( 1) 结合性 自右向左
如,int a,b,*p1,*p2;
p1=&a;
p2=&b;
&*p1=? 相当于 &(*p1) 等于
&(*p1)=&(a)=&a=p1
*&a 相当于 *(&a)=a
( 2) 优先级 处于第二位
(2) 自增自减运算
如,int a,b,*p;
p=&a; a=3; b=5;
(*p)++; 相当于 a++ a==4
*p++; 相当于 *(p++)
若 p为 1000,则 p++为 1002
若 float a,*p; p=&a;
当 p为 1000,则 p++为 1004
++与 *同一级别,均为从右至左
例 [10,2]输入整数 a和 b,按大小的顺序输出。
main ( )
{
int *p1,*p2,*p,a,b;
scanf("% d,% d",&a,&b);
p1=& al; p2=& b;
if (a< b)
{p=p1; pl=p2; p2=p;}
printf ( "\na=% d,b=% d\n”,a,b);
printf( "max=% d,min=% d\n",*pl,
*p2);
}
交换地址(指针变量),使 p1指向大数 b,而变量 a,b未
变。
&a
&b 9
&b
&a
p1 a p1 a
p2 b p2 b
5 5
9
p p
三、指针变量作为函数参数
说明:将一个变量的地址传送到另一函数中,实参和
形参均用指针。
[例 10,3] 对整数按大小顺序输出。用函数处理,且用
指针作函数参数。
swap( int *p1, int *p2)
{ int p;
p=*p1; *p1=*p2; *p2=p; }
main()
{ int a,b;
int *p1,*p2;
scanf("% d,% d”,&a,&b);
p1= &a; p2=& b;
if( a< b) swap ( p1, p2);
printf ( "\n% d,% d\n"a,b); }
传递的是变量的指针,在函数中交换的是变量中的内容,主
函数中读取交换了的内容。这里指针未变,与上题相反。
以下代码可以实现变量 a与 b的交换吗?
swap( int *pl,int *p2)
{ int *p;
p= pl;
p1=p2;
p2= p; }
main()
{ int a,b;
int *p1,*p2;
scanf (, % d% d”,&a,& b);
p1=&a; p2=& b;
if( a< b) swap( p1, p2);
printf(, \n% d,% d\n”,* p1,*p2); }
出错的原因是参数不是双向传递。
&a
&b
5
9
&a
&b
5
9
&b 5 &b 5
&a 9 &a 9
P1 a p1 a p1 a p1 a
p2 b p2 b p2 b p2 b
(a) (b) (c ) (d)
例 10,4 输入 a,b,c三个整数,按大小顺序输出。
swap( int *pt1, int *pt2)
{ int p;
p=*p1;
*ptl=*pt2;
*pt2=p; }
exchange( int *q1,int *q2,int *q3)
{ if( *ql< *q2) swap ( q1,q2);
if( *ql< *q3) swap( q1,q3);
if( *q2< *q3) swap ( q2,q3);
}
main()
{
int a,b,c,*p2,*p3;
scanf(, % d,% d% d",&a,& b,& c);
pl= &a; p2=& b;p3= &c;
exchange ( p1,p2,p3) ;
printf("\n%d,%d,%d\n",a,b,c);
}
运行情况如下,
输入,3,2,4
输出,4,3,2
10,3 数组与指针
内容:指向数组元素的指针变量及其应用
指向一维数组的指针变量及其应用
数组的指针是数组的起始地址,数组元素的指针是元素的地址。
引用数组可以用下标法,也可以用指针法,通过指向数组的元素的指针找
到数组元素。
使用指针法能使目标程序占内存少,运行速度快。
一,指向数组元素的指针
1、定义,同变量的指针定义相同
类型标示符 * 变量名
如,int a[10]; int *p;
float b[10]; float *p1; ---类型相同
2、赋值,将某元素或数组的首地址赋给指针变量。
如,int a[10],*p;
p=&a[0]; 或 p=a;
可 定义 时赋值,如,
int a[10];
int *p=&a[0]; 或 int *p=a;
(此与 语句 *p=&a[0], p=a 不同)
注意:对 p赋的是地址,而不是数值 。
二、通过指针引用数组元素
元素赋值、取值
如,int a[10],*p; p=a;
*p=1; 相当于 a[0]=1;
1、各元素地址(用以上定义)
指针 p 增一便指向下一单元 。 所以
p 指向 a[0]
p+1 指向 a[1]
p+2 指向 a[2]
,
p+i 指向 a[i]
,
p+9 指向 a[9]
a[0]
a[1]
a[2]
a[3]
a[ i]
a[7]
a[8]
a[9]
p
p,( 2000)
p+1,a+1
p+2,a+2
p+3,a+3
p+i,a+i
p+7,a+7
p+8,a+8
p+9,a+9
1
2
3
4
8
9
0
2000
A数组
因 p=a,因此 p==a,p+i 与 a+i 等价。所以有
a 指向 a[0]
a+1 指向 a[1]
a+2 指向 a[2]
,
a+i 指向 a[i]
a+9 指向 a[9]
2、引用元素
直接引用 a[0]=1; a[i ]=6; 通过地址应用
如,*p=1; …,,*(p+i)=6;
或者 *a=1; …,,*(a+i)=6;
, [ ]” 实际是 变址 运算符。如 a[i ]在这里变的值为 i*2,
,2”是整型数据占两个字节。
3、指向数组的指针变量也可带下标
如,p[ i] 与 *( p+i) 等价。
若 p=a; 则 p[ i] 与 a[i ] 等价。
例 10,5 输出一数组的全部元素。
main()
{ int a[10]={1,2,3,4,5,6,7,8,9,0};
int *p,i;
for(i=0;i<10;i++)
printf("%d ",a[i]); printf("\n");
for(i=0;i<10;i++)
printf("%d ",*(a+i)); printf("\n");
for(p=a;p<(a+10);p++)
printf("%d ",*p); printf("\n"); }
几个注意的问题,
( 1) p可变,而 a不可变。即有 p++,而无 a++。
( 2)当 p 变时注意 p当前的值。
例 10.6 输出数组 a的全部元素。
main()
{ int *p,i,a[10];
p=a;
printf("Enter 10 numbers:\n");
for(i=0;i<10;i++)
scanf("%d",p++);
printf("\n"); p=a;
for(i=0;i<10;i++,p++)
printf(" %d ",*p);}
( 3)关于指针变量的一些运算(设 p=a)
1) p++, p--;
2) *p++ 等价于 *( p++),
先得到 *p的值,再做 p++
*p-- 等价于 *( p--),
先得到 *p的值,再做 p--;
3) *( p++) p先自增 1,再得到 *p的值
*( p--) p先自减 1,再得到 *p的值;
4)( *p) ++ 使元素值加 1,
( *p) -- 使元素值减 1。
三、数组名作函数参数
1、数组名作函数参数
2、指针变量作函数参数
例 10.7 将数组 a中 n个数反序排列
void inv(int x[],int n)
{ int t,i,j,
m=(n-1)/2;
for(i=0; i<m; i++)
{j=n-1-i;
t=x[i]; x[i]=x[j];
x[j]=t;}
return; }
main()
{ static int i,a[10]={0,1,2,3,4,5,6,7,8,9};
printf("the orginal array:\n");
for(i=0;i<10;i++)
printf("%d ",a[i]);
printf("\n");
inv(a,10);
printf("the array has been inverted:\n");
for(i=0;i<10;i++)
printf("%d ",a[i]);
printf("\n");
}
运行结果,9 8 7 6 5 4 3 2 1 0
指针变量作函数参数(形参)
void inv(int *x,n)
{int *p,t,*i,*j;
int m=(n-1)/2;
j=x+n-1; p=x+m;
for(i=x; i<p; i++,j--)
{ t=*i; *i=*j; *j=t;}
return;
}
:,
9
8
7
6
3
2
1
0
a[0]
a[1]
a[2]
a[3]
a[6]
a[7]
a[8]
a[9]
i,x----
p=x+m---
j ----
a数组
:,
0
1
2
3
6
7
8
9
a[0]
a[1]
a[2]
a[3]
a[6]
a[7]
a[8]
a[9]
i,x----
p=x+m---
j ----
a数组
提示,
数组的指针和指向数组的指针变量
int a[10],*p;
p=a;
第 i个元素的地址,p+i 或 a+i, &a[i]
第 i个元素,*( p+i) 或 *( a+i),a[i]
例 10.8 p240
例 10.9 p241
例 10.10 p245
四、指向多维数组的指针和指针变量
(一)多维数组的地址
根据数组的性质,二维数组看作是一维数组,如
static int a[3][4]={{1,3,5,7},{9,11,13,15},{17,19,21,23}};
存放情况如下图所示。看成一维数组时,元素为 a[0],a[1]、
a[2],每个元素又是一维数组。
1、数组的首地址 为 a
2,各行的首地址
( 1)用 a表示 a+i为第 i行首地址
第 0行首地址为 a, 第 1行首地址为
a+1,第 2行为 a+2。 因为看成一维
数组,所以 a+1指向了 a[1],即第一行 。
( 2)用 a[i ]表示
因为 第 0行的地址为 a[0],第 1行的地址为 a[1], 第 2
行的地址为 a[2]。( 因为 a[0],a[1],a[2]为数组名)
a[i ]为第 i 行、第 0列元素的地址 。
各行首地址的变化情况,
设数组首地址为 2000,则各行首地址为,
2000,2008,2016,各行相差 4*2=8。
3、某行某列的地址 (即各元素的地址)
可有多种表示方式,
( 1)用 a[i ]表示,第 i行、第 j列元素的地址为,a[i ]+j
( 2) 用 a 表示为,
对一维数组 a[0],a[1],a[2]和 *(a+0),*(a+1)、
*(a+2)均表示元素,所以
a[0]与 *( a+0)等价,即 a[0]与 *a等价
a[1]与 *( a+1)等价,
a[2]与 *( a+2)等价
对二维数组,则 a[0],a[1],a[2]和 *(a+0),*(a+1)、
*(a+2)表示 各行的首地址,所以
第 i行、第 j列元素的地址为, *( a+i) +j
( 3)用 &a[i ][j]表示
4、元素的表示方法,即第 i行、第 j列的元素表示方法
( 1)下标法,a[i ][j]
( 2)用 a[i ]+j 地址表示,*( a[i ]+j )
( 3)用 *(a+i)+j 地址表示,*(*(a+i)+j )
5、地址表示方法汇总
例 10.11
#define format "%d,%d\n"
main()
{static int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};
printf(format,a,*a); 158,158
printf(format,a[0],*(a+0)); 158,158
printf(format,&a[0],&a[0][0]); 158,158
printf(format,a[1],a+1); 166,166
printf(format,&a[1][0],*(a+1)+0); 166,166
printf(format,a[2],*(a+2)); 174,174
printf(format,&a[2],a+2); 174,174
printf(format,a[1][0],*(*(a+1)+0)); 9,9
}
(二)多维数组的指针
1、指向数组元素的指针变量
例 10.12 用指针变量输出元素值。
main()
{static int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};
int *p;
for(p=a[0];p<a[0]+12;p++)
{if((p-a[0])%4==0) printf("\n");
printf("%3d",*p);
}
}
运行结果为,1 3 5 7 9 11 13 15 17 19 21 23
a[0]为第 0行第 0列元素的地址
第 i行、第 j列元素的相对地址为 i*m+j
m表示列数 实际地址为 a[0]+ i*m+j
如元素 a[2][3] 的地址为 a[0]+2*4+3=a[0]+11
2、指向一维数组的指针变量
变量的指针只能存放变量的地址,而不能存放数组的地址,
可定义 数组的指针变量 来存放数组的地址。
定义的一般形式,
类型说明符 ( *指针变量) [数组长度 ]
如,int (*p)[4]
p为一指向具有 4个整型元素的一维数组的指针变量
(不能写成 *p[4],此为指针数组)
说明,( 1) p的增值以一维数组的长度为单位
( 2)用 p表示数组的行地址
如, int a[3][4],(*p)[4]; p=a;
则 p+1 指向第一行,与 a+1相同,表示第一行的首地址
p+i 表示第 i行的首地址
( 3) 用 p表示数组元素的地址
*p 表示第 0行第 0列的地址
*( p+1) 表示第 1行第 0列元素的地址
*( p+i) 表示第 i行第 0列元素的地址
所以,*( p+i) +j 表示第 i行第 j列元素的地址 理解同前面
的 *( a+i) +j 表示第 i行第 j列的地址,p与 a等价。
( 4)用 p表示数组元素
*( *( p+i) +j) 表示第 i行第 j列的元素
p
例 10.10 输出二维数组任一行任一列的值 。
main() /*用数组的指针变量进行操作 */
{static int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};
int (*p)[4],i,j;
p=a;
printf(“input i,j:”\n);
scanf(“%d,%d”,&i,&j);
printf(”\na[%d][%d]=%d\n”,i,j,*(*(p+i)+j));
}
运行情况,
输入 1,2 回车
输出 a[1][2]=13
(三)多维数组的指针作为函数参数
实参是地址或指针;
形参有两种形式,
( 1)用指向变量的指针变量,
( 2)用指向一维数组的指针变量。
考察例题。
例 10.13 有一个班, 3个学生, 各 4门课程, 计算总平均分数
和查找第 n个学生的成绩 。
main()
{ void average(); void search();
static float
score[3][4]={{65,67,70,60},{80,87,90,81},
{90,99,100,98}};
scanf(“%d”,&n); /*int n=2;*/
average(*score,12); 或 average(score,12)
search(score,n);
}
void average(p,n)
float *p;int n;
{ float *p_end;
float sum=0,aver;
p_end=p+n;
for(;p<p_end;p++)
sum=sum+(*p);
aver=sum/n;
printf("average=%5.2f\n",aver);
}
void search(p,n)
float (*p)[4];int n;
{int i;
printf("the score of No.%d are,\n",n);
for (i=0;i<4;i++)
printf("%5.1f",*(*(p+n)+i));
printf("\n");
} 程序运行结果为,
average=82.25
the score of No.2 are,
90.0 99.0 100.0 98.0
例 10.14 在上题的基础上, 查找有一门以上课程不及格
的学生, 打印出他们的全部课程的成绩 。
main()
{
void search();
static float
score[3][4]={{65,57,70,60},{58,87,90,81},
{90,99,100,98}};
search(score,3);
}
void search(float (*p)[4],int n)
{ int i,j,flag;
for (j=0;j<n;j++) /*查找人 */
{flag=0; /*不及格标志 */
for(i=0;i<4;i++) /*查找某人的成绩 */
if(*(*(p+j)+i)<60) flag=1;
if(flag)
{printf("No,%d is fail,his(her) scores
are:\n",j+1);
for (i=0;i<4;i++)
printf("%5.1f ",*(*(p+j)+i));
printf("\n");
}
} }
10,4 字符串的指针和指向字符串的指针变量
重点,
( 1)字符串指针的定义、引用及作函数参数;
( 2)字符串指针变量与字符数组的区别。
一、字符串的表示形式
1、用字符数组
如,char a[10]=“CHINA”
2、用字符指针
例 10.15
main()
{ char *string=“I love China !”;
printf(“%s\n”,string);
}
char *string=“I love China !”;
等价于下面两行,
char *string;
string =“I love China !”;
提示:对字符串中字符的存取,可用下标法,
也可以用指针法。
例 10.17 将字符串 a复制到字符串 b。
main()
{ char a[]="I am a boy.",b[20];
int i;
for (i=0;*(a+i)!='\0';i++) *(b+i)=*(a+i);
*(b+i)='\0';
printf("string a is,%s\n",a);
printf("string b is,%s\n",b);
for(i=0;b[i]!='\0';i++) printf("%c",*(b+i));
printf("\n");
}
例 10.18 用指针变量来处理例 10.17。
main()
{ char a[]="I am a boy.",b[20],*p1,*p2;
int i;
p1=a; p2=b;
for (;*p1!='\0';p1++,p2++) *p2=*p1;
*p2='\0';
printf("string a is,%s\n",a);
printf("string b is,%s\n",b);
for(i=0;b[i]!='\0';i++) printf("%c",*(b+i));
printf("\n");
} 注意:指针必须先赋地址才能使用 。
二、字符串指针作函数参数
参数是字符串或字符串指针,传递的是地址 。
在被调函数中改变后,可在主调函数中得到改
变后的数据。
例 10.19 用函数调用实现字符串的复制。
( 1)用字符数组作参数
void copy_string( char from[],char to[])
{ int i=0;
while(from[i]!='\0')
{to[i]=from[i];i++;}
to[i]='\0';
}
main()
{char a[]="I am a teacher.",
b[]="you are a student.";
int i;
printf("string a is,%s\n",a);
printf("string b is,%s\n",b);
copy_string(a,b);
printf("string a is,%s\n",a);
printf("string b is,%s\n",b);
for(i=0;i<18;i++)
printf("%c",b[i]);
}
运行结果,I am a teacher,
( 2)形参用字符指针变量
void copy_string(char *from,char *to)
{ int i=0;
for(;*from!='\0’,from++,to++)
*to=*from;
*to='\0';
}
main()
{char *a="I am a teacher.",
*b="you are a student.";
int *p;
printf("string a is,%s\n",a);
printf("string b is,%s\n",b);
copy_string(a,b);
printf("string a is,%s\n",a);
for(p=b;*p!=?\0?;p++)
printf("%c",p);
}
运行结果,string a is, I am a teacher,
I am a teacher,
这里的 p是指向字符数组元素的指针变量。
( 3)函数体的几种简化形式
1) void copy_string(char *from,char *to)
{while((*to=*from)!=?\0?)
{to++;from++;}
}
2) { while((*to++=*from++)!=?\0?); }
3) {while(*from!=?\0?)
*to++=*from++;
*to=?\0?; }
4) { while(*to++=*from++); }
5) { for(;( *to++=*from++) !=?\0?;); }
或 { for(;*to++=*from++;); }
6) 用指针变量
void copy_string(from,to)
char from[ ],to[ ];
{char *p1,*p2;
p1=from; p2=to;
while((*p2++=*p1++)!=?\0?);
}
用数组名、指针变量作函数参数的几种组合,
数组名 数组名
数组名 字符指针变量
字符指针变量 数组名
字符指针变量 字符指针变量
三、字符指针变量与字符数组
虽二者均能实现对字符串的存储和计算,但二者有区别,
主要有以下几点,
1、字符数组由元素组成,元素中放字符;字符指针变量放
地址(如字符串的首地址等);
2、赋初值的方式不同;对字符数组赋初值一般用 static,如
static char str[ ]=“I love China!”;
而字符指针变量赋初值一般不用 static,如,
char *p=“I love China!”;
3、赋值方式不同;
( 1)对字符数组要么赋初值(即定义时赋值),要么对元
素赋值,下面的赋值方式是错误的,
char str[14]; str[14 ]=“I love China!”;
( 2)字符指针变量赋值一般可赋初值,或先定义后赋值,
如 char *a; a=,I love China!”;
等价于,char *a=,I love China!”;
4、定义指针变量时,编译后便分配了存储单元,但为空;
如,char str[10]; scanf(“%s”,str); 为正确,
但 char *a; scanf(“%s”,a); 为错误,
而 char *a,str[10]; a=str; scanf(“%s”,a); 为正确。
5、指针变量的值可改变;
例 10.21
main() 运行结果为,
{ char *a="I love China"; China
a=a+7;
printf("%s",a);
}
但对数组下面的是错误的,
char str[]="I love China";
str=str+7;
printf("%s",str);
也可用下标形式引用指针变量
例 10.21
main()
{ char *a="I love China!";
int i;
printf("The sixth character is %c\n",a[5]);
for(i=0;a[i]!='\0';i++)
printf("%c",a[i]);
} 运行结果为,The sixth character is e
I love China!
6、指针变量更方便。
( 1)赋初值简单,不用 static;
( 2)定义时可直接赋值;
( 3)可作地址用,也可使用下标。
10,5 函数的指针和指向函数的指针变量
重点:函数指针的概念及指向函数的指针变量
用指向函数的指针变量作函数参数
一、函数的指针及指向函数的指针变量
1、函数指针的概念
一个函数在编译时被分配一个入口地址,这个入口地址 称
为
函数的指针。
2、指向函数的指针变量
定义的一般形式,
数据类型标识符 ( *指针变量名) ()
例 10.23 求 a和 b中的大者。
main()
{int max();
int a,b,c;
char *format="a=%d,b=%d,max=%d";
scanf("%d,%d",&a,&b);
c=max(a,b);
printf(format,a,b,c);
printf("\n");
}
int max(int x,int y)
{int z;
return(z=x>y?x:y);
}
二、用指向函数的指针变量作函数的参数
传递的是函数的入口地址,
一般的使用形式,
,
sub( f1,f2); /* 主调函数 /*
,
sub(x1,x2)
int (*x1)( ),(*x2)( );
{int a,b,i,j;
, /* 次主调函数 */
a=(*x1)(i); /*x1,x2为被调函数 */
b=(*x2)(i,j);
,
}
f1
x1 f1
f2
x2 f2
说明,
( 1)一般在嵌套调用中使用,传递的是函数的入口地址;
( 2)次主调函数对其它函数操作,完成一定的功能;
( 3)主调函数的参数(实参)用函数名。
例 10.24 设一个函数 process,在调用它时, 每次实现
不同的功能, 调用三次, 分别求出 a,b中较大者, 较小者
和二者之和 。
main()
{int max(),min(),add();
int a,b;
printf("Enter a and b:");
scanf("%d,%d",&a,&b);
printf("max=");
process(a,b,max);
printf("min=");
process(a,b,min);
printf("sum=");
process(a,b,add);
printf("\n");
}
int max(int x,int y)
{ int z;
return(z=x>y?x:y);
}
int min(int x,int y)
{int z;
return(z=x<y?x:y);
}
int add(int x,int y)
{ int z;
z=x+y;
return(z);
}
process(x,y,fun)
int x,y;
int (*fun)();
{ int result;
result=(*fun)(x,y);
printf("%d\n",result);
}
例 10.25 求定积分,
y1=?10 (1+x2)dx
y2=?20 (1+x+x2+x3)dx
略
? ??
5.3
0
2
)
1
(3 dx
x
xy
10.6 返回指针值的函数
一、指针函数的定义
存储类别 数据类型标识符 *函数名(形参表列)
{ 函数体 }
如,int *a(int x,int y) /*存储类别为外部函数 /
{……,.}
二、指针函数的应用
例 10.26 有若干个学生的成绩(每个学生有 4门课
程),要求输入学生序号后,输出该生的全部成绩。
用指针函数来实现。
main()
{ static float score[][4]={{60,70,80,90},
{56,89,67,88},{34,78,90,66}};
float *search();
float *p;
int i,m;
printf("enter the number of student:");
scanf("%d",&m);
printf("the scores of No.%d are:\n",m);
p=search(score,m);
for(i=0;i<4;i++)
printf("%5.1f\t",*(p+i));
}
float *search(pointer,n)
float (*pointer)[4];
int n;
{float *pt;
pt= *(pointer+n);
return(pt);
}
运行结果,
enter the number of student:1 回车
the scores of No.1 are,
56.0 89.0 67.0 88.0
例 10.27对上例中的学生, 找出其中有不及格课程的学生及
其学生号 。
main()
{static float score[][4]={{60,70,80,90},
{50,89,67,88},{34,78,90,66}};
float *search(); float *p; int i,j;
for (i=0;i<3;i++)
{p=search(score+i);
if (p==*(score+i))
{printf("No.%d scores,",i);
for(j=0;j<4;j++)
printf("%5.1f ",*(p+j));
printf("\n");}
}
}
float *search(pointer)
float (*pointer)[4];
{ int i;
float *pt;
pt=*(pointer+1);
for(i=0;i<4;i++)
if(*(*pointer+i)<60)
pt=*pointer;
return (pt);
}
运行结果,
No.1 scores,56.0 89.0 67.0 88.0
No.2 scores,34.0 78.0 90.0 66.0
10,7 指针数组和指向指针的指针
重点:指针数组及其应用
一、指针数组
其元素均为指针型数据的数组,称为指针数组。
定义形式,类型标识符 *数组名 [数组长度说明 ]
如,int *p[4];
不能写成 int (*p)[4]
指针数组主要用于指向多个字符串,使字符串处
理方便。
例 10.28 对字符串排序,按由小到大输出。
思路:定义一指针数组,存放各字符串的首地址,排序,
交换首地址。
#include"string.h"
main()
{void sort(); void print();
static char *name[]={"Follow me","BASIC",
"Great Wall","FORTRAN","Computer design"};
int n=5;
sort (name,n);
print(name,n);
}
void sort(name,n)
char *name[];int n;
{char *temp;
int i,j,k;
for(i=0;i<n-1;i++)
{k=i;
for(j=i+1;j<n;j++)
if(strcmp(name[k],name[j])>0) k=j;
if(k!=i)
{temp=name[i];name[i]=name[k];name[k]=temp;}
}
}
void print(name,n)
char *name[];int n;
{int i;
for(i=0;i<n;i++)
rintf("%s\n",name[i]);
}
运行结果,
BASIC
Computer design
FORTRAN
Follow me
Great Wall
void print(char *name[],int n)
{ int i;
char *p;
p=name[0];
while(i<n)
{p=*(name+i++);
printf("%s\n",p);
}
}
二、指向指针的指针
即指向指针数据的地址。
上面 name是指针数组,元素均是指针数据,可定义一指针
变量,指向数组,对数组各元素操作。
定义形式,类型标识符 * * 变量名
如,char **P;
p为一个指向字符指针变量的指针变量。一般指向一数组的
首地址。
如,char *name[4],**p;
p=name;
又如,
p=name+2;
printf(“%d,%d,%s”,p,*p,**p);
输出,2000,3016,Great wall
3016 2004
2000
例 10.29
main()
{static char *name[]={"Follow me","BASIC",
"Great Wall","FORTRAN","Computer design"};
char **p;
int i; 运行结果,
for(i=0;i<5;i++) Follow me
{p=name+i; BASIC
printf("%s\n",*p); Great Wall
} FORTRAN
} Computer design
指针数组的元素也可以不指向字符串,而指向整型数据或
实型数据,例如,
int a[5]={1,3,5,7,9};
int *num[5]={&[0],&[1],&[2],&[3],&[4]};
int **p;
此时为了得到数据 5,可以先使 p=num+2,然后输出 **p。 *p
是 p间接指向的对象的地址。而 **p是 p间接指向的对象的值。
见下图。
例 10.30
main()
{ static a[5]={1,3,5,7,9};
static int *num[5]={&a[0],&a[1],
&a[2],&a[3],&a[4]};
int **p,i;
p=num;
for(i=0;i<5;i++)
{printf("%d\t",**p);
p++;}
} 运行结果,
1 3 5 7 9
间接访问的,单级间址,和,二级间址,
用指针访问便是间接访问。
若指针变量中存放的是目标变量的地址,称为,单级间
址,,
指向指针的指针为,二级间址,。还可以有多级间址。
三、指针数组作 main函数的参数
指针数组的一个应用是作为 main函数的形参。
前面应用 main函数均无参数,但可以有参数。
一般形式为,main(argc,argv)
问题:( 1)实参从何处来;( 2)形参的意义。
1,形参从命令行得到
如执行 file1.exe 文件,且文件中有以上参数,则可输入
file1 China Beijing 此即为 main函数的实参
使用一般形式,文件名 参数 1 参数 2 …… 参数 n
2、形参的意义
( 1) argc, 参数的数目,为整型,int argc
如,file1 China Beijing 为三个参数,传过来后使 argc得到 3。
( 2) argv, 为指向字符串的指针变量,得到的是命令行各
字符串的首地址,定义形式为,
char *argv[ ] 数组长度由命令行各字符串的个数确定
如,file1 China Beijing
使 argv[ ]的长度为 3,argv[0]指向 file1,argv[1]指向
China,argv[2]指向 Beijing。见下图。
3,main函数带参数的例子
考察参数回送命令 echo的内容,文件名为 echo.c
main(argc,argv)
int argc; char *argv[ ];
{while(--argc>0)
printf(“%s%c”,*++argv,(argc>1)?? ?,?\n?);
}
运行,若输入,echo Computer and C Language 回车
则显示,Computer and C Language
运行时 argc得到 5,argv[ ]得到 5个字符串的首地址。
说明,
( 1) main函数的形参名可以为其它的;
( 2)命令行的参数可以不显示,作为程序中所需要的。
10.8 指针型数据小结
一、有关指针的数据类型 (见下表)
二、指针运算小结
1、指针变量加减一个整数
如,p++,p--,p+i,p-i,p+=i,p-=i 等
实际含义如 p+i 代表地址计算,p+c*i, c为字
节数,整型 c=2、实型 c=4、字符型 c=1。
2、指针变量赋值
如,p=&a a为变量,p为指针变量
p=array array为数组名
p=&array[i ]
p=max max为函数名
p1=p2 p2为指针变量
注意,不能把一个整数或常数赋给 p,如 p=1000
同样也不能把 p赋给整型变量,如 i=p
3、指针变量可以赋空值
如,p=NULL; 实际 NULL是整数 0。
4、两个指针变量可以相减
如两个指针变量指向同一个数组的元素(如
右图),则 p2-p1=4-1=3,但 p1+p2无意义。
5、两个指针变量比较
若两个指针指向同一个数组的元素,则可以比较,
如,p1<p2 p1>p2
三、空类型指针
ANSI新标准增加了一种空类型指针,定义的一般形式为,
void * 变量名
具体使用时要做类型转换,如,void *p; (int *)p;
本章结束
指针 ----C语言的精华
10, 1 地址和指针的概念
指针就是 地址 。有变量的、数组的、函数
的、字符串的指针等。
一、内存的访问方式
1、直接访问
如, i=2,j=6,k=9,通过变量 i,j,k 来
访问存储单元。
2、间接访问
如变量 i,不知道其值及地址,而只知道
它的地址放在 3010,3011中,因此可通过
存放地址的单元找到变量 i 的值,此为间接
访问。
对间接访问,可定义一变量存放地址,如
定义 ipointer,赋值 ipointer=&i
2000
2002
2004
3010
i
j
k
i_p
3
6
9
2000
,
,
,
内存用户数据区
,
,
二、指针的概念
一个变量的地址称为该变量的,指针,。
i的地址是 2000,则地址 2000是变量 I的指针。
存放地址的变量叫“指针变量”,指针变量存放地址。
ipointer存放的是变量 I的地址。
i 变量
ipointer 指针变量 ipointer=&i
10, 2 变量的指针和指向变量的指针变量
一、指针变量的定义
一般形式,类型标识符 *变量名
如,int *p1;
char *p2;
float *p3;
说明,(1) * 表示指针型变量,意为“指向 …,.的指针,
(2) 指针变量有确定的指向的数据类型。
思考,指针变量存放的是一个地址,为什么定义时需要指定类型?
二、指针变量的引用
1、给指针变量赋值 ( 赋变量的地址)
如,p1=&i 一般不直接赋数值。
*p1的意义为 p1所指向的变量 i;
*p与 i等价,i=3,则 *p1也为 3。
注意,p1是地址变量,而 *p 1是非地址变量,与 i等价。
,*”:为指针运算符或叫指向运算符、间接运算符,为
单目运算符。
2000 3
P1 *p1
i
2000
2、用指针变量对变量赋值
如,int I,j,*p;
p=&I;
*p=3;
j=*p+2;
定义后 p=&I后 *p=3后
I
j
p
,
,
,
,
1000
1002
1004
I
j
p
1000
1002
1004
I
j
p
1000
1002
1004
I
j
p 1000
3
1000
1000
1002
1004
j=*p+2后
3
5
1000
例 10.1
main()
{ int a,b;
int *pointer1,*pointer2;
a= 100; b=10;
pointer1= &a; /*把变量 a的地址赋给 pointer_1*/
pointer2=&b; / *把变量 b的地址赋给 pointer_2*/
printf (, % d,% d\n”,a,b);
printf(, % d,% d\n”,*pointer1,* pointer2);
}
关于运算符 &和 * 的几点说明,
( 1) 结合性 自右向左
如,int a,b,*p1,*p2;
p1=&a;
p2=&b;
&*p1=? 相当于 &(*p1) 等于
&(*p1)=&(a)=&a=p1
*&a 相当于 *(&a)=a
( 2) 优先级 处于第二位
(2) 自增自减运算
如,int a,b,*p;
p=&a; a=3; b=5;
(*p)++; 相当于 a++ a==4
*p++; 相当于 *(p++)
若 p为 1000,则 p++为 1002
若 float a,*p; p=&a;
当 p为 1000,则 p++为 1004
++与 *同一级别,均为从右至左
例 [10,2]输入整数 a和 b,按大小的顺序输出。
main ( )
{
int *p1,*p2,*p,a,b;
scanf("% d,% d",&a,&b);
p1=& al; p2=& b;
if (a< b)
{p=p1; pl=p2; p2=p;}
printf ( "\na=% d,b=% d\n”,a,b);
printf( "max=% d,min=% d\n",*pl,
*p2);
}
交换地址(指针变量),使 p1指向大数 b,而变量 a,b未
变。
&a
&b 9
&b
&a
p1 a p1 a
p2 b p2 b
5 5
9
p p
三、指针变量作为函数参数
说明:将一个变量的地址传送到另一函数中,实参和
形参均用指针。
[例 10,3] 对整数按大小顺序输出。用函数处理,且用
指针作函数参数。
swap( int *p1, int *p2)
{ int p;
p=*p1; *p1=*p2; *p2=p; }
main()
{ int a,b;
int *p1,*p2;
scanf("% d,% d”,&a,&b);
p1= &a; p2=& b;
if( a< b) swap ( p1, p2);
printf ( "\n% d,% d\n"a,b); }
传递的是变量的指针,在函数中交换的是变量中的内容,主
函数中读取交换了的内容。这里指针未变,与上题相反。
以下代码可以实现变量 a与 b的交换吗?
swap( int *pl,int *p2)
{ int *p;
p= pl;
p1=p2;
p2= p; }
main()
{ int a,b;
int *p1,*p2;
scanf (, % d% d”,&a,& b);
p1=&a; p2=& b;
if( a< b) swap( p1, p2);
printf(, \n% d,% d\n”,* p1,*p2); }
出错的原因是参数不是双向传递。
&a
&b
5
9
&a
&b
5
9
&b 5 &b 5
&a 9 &a 9
P1 a p1 a p1 a p1 a
p2 b p2 b p2 b p2 b
(a) (b) (c ) (d)
例 10,4 输入 a,b,c三个整数,按大小顺序输出。
swap( int *pt1, int *pt2)
{ int p;
p=*p1;
*ptl=*pt2;
*pt2=p; }
exchange( int *q1,int *q2,int *q3)
{ if( *ql< *q2) swap ( q1,q2);
if( *ql< *q3) swap( q1,q3);
if( *q2< *q3) swap ( q2,q3);
}
main()
{
int a,b,c,*p2,*p3;
scanf(, % d,% d% d",&a,& b,& c);
pl= &a; p2=& b;p3= &c;
exchange ( p1,p2,p3) ;
printf("\n%d,%d,%d\n",a,b,c);
}
运行情况如下,
输入,3,2,4
输出,4,3,2
10,3 数组与指针
内容:指向数组元素的指针变量及其应用
指向一维数组的指针变量及其应用
数组的指针是数组的起始地址,数组元素的指针是元素的地址。
引用数组可以用下标法,也可以用指针法,通过指向数组的元素的指针找
到数组元素。
使用指针法能使目标程序占内存少,运行速度快。
一,指向数组元素的指针
1、定义,同变量的指针定义相同
类型标示符 * 变量名
如,int a[10]; int *p;
float b[10]; float *p1; ---类型相同
2、赋值,将某元素或数组的首地址赋给指针变量。
如,int a[10],*p;
p=&a[0]; 或 p=a;
可 定义 时赋值,如,
int a[10];
int *p=&a[0]; 或 int *p=a;
(此与 语句 *p=&a[0], p=a 不同)
注意:对 p赋的是地址,而不是数值 。
二、通过指针引用数组元素
元素赋值、取值
如,int a[10],*p; p=a;
*p=1; 相当于 a[0]=1;
1、各元素地址(用以上定义)
指针 p 增一便指向下一单元 。 所以
p 指向 a[0]
p+1 指向 a[1]
p+2 指向 a[2]
,
p+i 指向 a[i]
,
p+9 指向 a[9]
a[0]
a[1]
a[2]
a[3]
a[ i]
a[7]
a[8]
a[9]
p
p,( 2000)
p+1,a+1
p+2,a+2
p+3,a+3
p+i,a+i
p+7,a+7
p+8,a+8
p+9,a+9
1
2
3
4
8
9
0
2000
A数组
因 p=a,因此 p==a,p+i 与 a+i 等价。所以有
a 指向 a[0]
a+1 指向 a[1]
a+2 指向 a[2]
,
a+i 指向 a[i]
a+9 指向 a[9]
2、引用元素
直接引用 a[0]=1; a[i ]=6; 通过地址应用
如,*p=1; …,,*(p+i)=6;
或者 *a=1; …,,*(a+i)=6;
, [ ]” 实际是 变址 运算符。如 a[i ]在这里变的值为 i*2,
,2”是整型数据占两个字节。
3、指向数组的指针变量也可带下标
如,p[ i] 与 *( p+i) 等价。
若 p=a; 则 p[ i] 与 a[i ] 等价。
例 10,5 输出一数组的全部元素。
main()
{ int a[10]={1,2,3,4,5,6,7,8,9,0};
int *p,i;
for(i=0;i<10;i++)
printf("%d ",a[i]); printf("\n");
for(i=0;i<10;i++)
printf("%d ",*(a+i)); printf("\n");
for(p=a;p<(a+10);p++)
printf("%d ",*p); printf("\n"); }
几个注意的问题,
( 1) p可变,而 a不可变。即有 p++,而无 a++。
( 2)当 p 变时注意 p当前的值。
例 10.6 输出数组 a的全部元素。
main()
{ int *p,i,a[10];
p=a;
printf("Enter 10 numbers:\n");
for(i=0;i<10;i++)
scanf("%d",p++);
printf("\n"); p=a;
for(i=0;i<10;i++,p++)
printf(" %d ",*p);}
( 3)关于指针变量的一些运算(设 p=a)
1) p++, p--;
2) *p++ 等价于 *( p++),
先得到 *p的值,再做 p++
*p-- 等价于 *( p--),
先得到 *p的值,再做 p--;
3) *( p++) p先自增 1,再得到 *p的值
*( p--) p先自减 1,再得到 *p的值;
4)( *p) ++ 使元素值加 1,
( *p) -- 使元素值减 1。
三、数组名作函数参数
1、数组名作函数参数
2、指针变量作函数参数
例 10.7 将数组 a中 n个数反序排列
void inv(int x[],int n)
{ int t,i,j,
m=(n-1)/2;
for(i=0; i<m; i++)
{j=n-1-i;
t=x[i]; x[i]=x[j];
x[j]=t;}
return; }
main()
{ static int i,a[10]={0,1,2,3,4,5,6,7,8,9};
printf("the orginal array:\n");
for(i=0;i<10;i++)
printf("%d ",a[i]);
printf("\n");
inv(a,10);
printf("the array has been inverted:\n");
for(i=0;i<10;i++)
printf("%d ",a[i]);
printf("\n");
}
运行结果,9 8 7 6 5 4 3 2 1 0
指针变量作函数参数(形参)
void inv(int *x,n)
{int *p,t,*i,*j;
int m=(n-1)/2;
j=x+n-1; p=x+m;
for(i=x; i<p; i++,j--)
{ t=*i; *i=*j; *j=t;}
return;
}
:,
9
8
7
6
3
2
1
0
a[0]
a[1]
a[2]
a[3]
a[6]
a[7]
a[8]
a[9]
i,x----
p=x+m---
j ----
a数组
:,
0
1
2
3
6
7
8
9
a[0]
a[1]
a[2]
a[3]
a[6]
a[7]
a[8]
a[9]
i,x----
p=x+m---
j ----
a数组
提示,
数组的指针和指向数组的指针变量
int a[10],*p;
p=a;
第 i个元素的地址,p+i 或 a+i, &a[i]
第 i个元素,*( p+i) 或 *( a+i),a[i]
例 10.8 p240
例 10.9 p241
例 10.10 p245
四、指向多维数组的指针和指针变量
(一)多维数组的地址
根据数组的性质,二维数组看作是一维数组,如
static int a[3][4]={{1,3,5,7},{9,11,13,15},{17,19,21,23}};
存放情况如下图所示。看成一维数组时,元素为 a[0],a[1]、
a[2],每个元素又是一维数组。
1、数组的首地址 为 a
2,各行的首地址
( 1)用 a表示 a+i为第 i行首地址
第 0行首地址为 a, 第 1行首地址为
a+1,第 2行为 a+2。 因为看成一维
数组,所以 a+1指向了 a[1],即第一行 。
( 2)用 a[i ]表示
因为 第 0行的地址为 a[0],第 1行的地址为 a[1], 第 2
行的地址为 a[2]。( 因为 a[0],a[1],a[2]为数组名)
a[i ]为第 i 行、第 0列元素的地址 。
各行首地址的变化情况,
设数组首地址为 2000,则各行首地址为,
2000,2008,2016,各行相差 4*2=8。
3、某行某列的地址 (即各元素的地址)
可有多种表示方式,
( 1)用 a[i ]表示,第 i行、第 j列元素的地址为,a[i ]+j
( 2) 用 a 表示为,
对一维数组 a[0],a[1],a[2]和 *(a+0),*(a+1)、
*(a+2)均表示元素,所以
a[0]与 *( a+0)等价,即 a[0]与 *a等价
a[1]与 *( a+1)等价,
a[2]与 *( a+2)等价
对二维数组,则 a[0],a[1],a[2]和 *(a+0),*(a+1)、
*(a+2)表示 各行的首地址,所以
第 i行、第 j列元素的地址为, *( a+i) +j
( 3)用 &a[i ][j]表示
4、元素的表示方法,即第 i行、第 j列的元素表示方法
( 1)下标法,a[i ][j]
( 2)用 a[i ]+j 地址表示,*( a[i ]+j )
( 3)用 *(a+i)+j 地址表示,*(*(a+i)+j )
5、地址表示方法汇总
例 10.11
#define format "%d,%d\n"
main()
{static int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};
printf(format,a,*a); 158,158
printf(format,a[0],*(a+0)); 158,158
printf(format,&a[0],&a[0][0]); 158,158
printf(format,a[1],a+1); 166,166
printf(format,&a[1][0],*(a+1)+0); 166,166
printf(format,a[2],*(a+2)); 174,174
printf(format,&a[2],a+2); 174,174
printf(format,a[1][0],*(*(a+1)+0)); 9,9
}
(二)多维数组的指针
1、指向数组元素的指针变量
例 10.12 用指针变量输出元素值。
main()
{static int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};
int *p;
for(p=a[0];p<a[0]+12;p++)
{if((p-a[0])%4==0) printf("\n");
printf("%3d",*p);
}
}
运行结果为,1 3 5 7 9 11 13 15 17 19 21 23
a[0]为第 0行第 0列元素的地址
第 i行、第 j列元素的相对地址为 i*m+j
m表示列数 实际地址为 a[0]+ i*m+j
如元素 a[2][3] 的地址为 a[0]+2*4+3=a[0]+11
2、指向一维数组的指针变量
变量的指针只能存放变量的地址,而不能存放数组的地址,
可定义 数组的指针变量 来存放数组的地址。
定义的一般形式,
类型说明符 ( *指针变量) [数组长度 ]
如,int (*p)[4]
p为一指向具有 4个整型元素的一维数组的指针变量
(不能写成 *p[4],此为指针数组)
说明,( 1) p的增值以一维数组的长度为单位
( 2)用 p表示数组的行地址
如, int a[3][4],(*p)[4]; p=a;
则 p+1 指向第一行,与 a+1相同,表示第一行的首地址
p+i 表示第 i行的首地址
( 3) 用 p表示数组元素的地址
*p 表示第 0行第 0列的地址
*( p+1) 表示第 1行第 0列元素的地址
*( p+i) 表示第 i行第 0列元素的地址
所以,*( p+i) +j 表示第 i行第 j列元素的地址 理解同前面
的 *( a+i) +j 表示第 i行第 j列的地址,p与 a等价。
( 4)用 p表示数组元素
*( *( p+i) +j) 表示第 i行第 j列的元素
p
例 10.10 输出二维数组任一行任一列的值 。
main() /*用数组的指针变量进行操作 */
{static int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};
int (*p)[4],i,j;
p=a;
printf(“input i,j:”\n);
scanf(“%d,%d”,&i,&j);
printf(”\na[%d][%d]=%d\n”,i,j,*(*(p+i)+j));
}
运行情况,
输入 1,2 回车
输出 a[1][2]=13
(三)多维数组的指针作为函数参数
实参是地址或指针;
形参有两种形式,
( 1)用指向变量的指针变量,
( 2)用指向一维数组的指针变量。
考察例题。
例 10.13 有一个班, 3个学生, 各 4门课程, 计算总平均分数
和查找第 n个学生的成绩 。
main()
{ void average(); void search();
static float
score[3][4]={{65,67,70,60},{80,87,90,81},
{90,99,100,98}};
scanf(“%d”,&n); /*int n=2;*/
average(*score,12); 或 average(score,12)
search(score,n);
}
void average(p,n)
float *p;int n;
{ float *p_end;
float sum=0,aver;
p_end=p+n;
for(;p<p_end;p++)
sum=sum+(*p);
aver=sum/n;
printf("average=%5.2f\n",aver);
}
void search(p,n)
float (*p)[4];int n;
{int i;
printf("the score of No.%d are,\n",n);
for (i=0;i<4;i++)
printf("%5.1f",*(*(p+n)+i));
printf("\n");
} 程序运行结果为,
average=82.25
the score of No.2 are,
90.0 99.0 100.0 98.0
例 10.14 在上题的基础上, 查找有一门以上课程不及格
的学生, 打印出他们的全部课程的成绩 。
main()
{
void search();
static float
score[3][4]={{65,57,70,60},{58,87,90,81},
{90,99,100,98}};
search(score,3);
}
void search(float (*p)[4],int n)
{ int i,j,flag;
for (j=0;j<n;j++) /*查找人 */
{flag=0; /*不及格标志 */
for(i=0;i<4;i++) /*查找某人的成绩 */
if(*(*(p+j)+i)<60) flag=1;
if(flag)
{printf("No,%d is fail,his(her) scores
are:\n",j+1);
for (i=0;i<4;i++)
printf("%5.1f ",*(*(p+j)+i));
printf("\n");
}
} }
10,4 字符串的指针和指向字符串的指针变量
重点,
( 1)字符串指针的定义、引用及作函数参数;
( 2)字符串指针变量与字符数组的区别。
一、字符串的表示形式
1、用字符数组
如,char a[10]=“CHINA”
2、用字符指针
例 10.15
main()
{ char *string=“I love China !”;
printf(“%s\n”,string);
}
char *string=“I love China !”;
等价于下面两行,
char *string;
string =“I love China !”;
提示:对字符串中字符的存取,可用下标法,
也可以用指针法。
例 10.17 将字符串 a复制到字符串 b。
main()
{ char a[]="I am a boy.",b[20];
int i;
for (i=0;*(a+i)!='\0';i++) *(b+i)=*(a+i);
*(b+i)='\0';
printf("string a is,%s\n",a);
printf("string b is,%s\n",b);
for(i=0;b[i]!='\0';i++) printf("%c",*(b+i));
printf("\n");
}
例 10.18 用指针变量来处理例 10.17。
main()
{ char a[]="I am a boy.",b[20],*p1,*p2;
int i;
p1=a; p2=b;
for (;*p1!='\0';p1++,p2++) *p2=*p1;
*p2='\0';
printf("string a is,%s\n",a);
printf("string b is,%s\n",b);
for(i=0;b[i]!='\0';i++) printf("%c",*(b+i));
printf("\n");
} 注意:指针必须先赋地址才能使用 。
二、字符串指针作函数参数
参数是字符串或字符串指针,传递的是地址 。
在被调函数中改变后,可在主调函数中得到改
变后的数据。
例 10.19 用函数调用实现字符串的复制。
( 1)用字符数组作参数
void copy_string( char from[],char to[])
{ int i=0;
while(from[i]!='\0')
{to[i]=from[i];i++;}
to[i]='\0';
}
main()
{char a[]="I am a teacher.",
b[]="you are a student.";
int i;
printf("string a is,%s\n",a);
printf("string b is,%s\n",b);
copy_string(a,b);
printf("string a is,%s\n",a);
printf("string b is,%s\n",b);
for(i=0;i<18;i++)
printf("%c",b[i]);
}
运行结果,I am a teacher,
( 2)形参用字符指针变量
void copy_string(char *from,char *to)
{ int i=0;
for(;*from!='\0’,from++,to++)
*to=*from;
*to='\0';
}
main()
{char *a="I am a teacher.",
*b="you are a student.";
int *p;
printf("string a is,%s\n",a);
printf("string b is,%s\n",b);
copy_string(a,b);
printf("string a is,%s\n",a);
for(p=b;*p!=?\0?;p++)
printf("%c",p);
}
运行结果,string a is, I am a teacher,
I am a teacher,
这里的 p是指向字符数组元素的指针变量。
( 3)函数体的几种简化形式
1) void copy_string(char *from,char *to)
{while((*to=*from)!=?\0?)
{to++;from++;}
}
2) { while((*to++=*from++)!=?\0?); }
3) {while(*from!=?\0?)
*to++=*from++;
*to=?\0?; }
4) { while(*to++=*from++); }
5) { for(;( *to++=*from++) !=?\0?;); }
或 { for(;*to++=*from++;); }
6) 用指针变量
void copy_string(from,to)
char from[ ],to[ ];
{char *p1,*p2;
p1=from; p2=to;
while((*p2++=*p1++)!=?\0?);
}
用数组名、指针变量作函数参数的几种组合,
数组名 数组名
数组名 字符指针变量
字符指针变量 数组名
字符指针变量 字符指针变量
三、字符指针变量与字符数组
虽二者均能实现对字符串的存储和计算,但二者有区别,
主要有以下几点,
1、字符数组由元素组成,元素中放字符;字符指针变量放
地址(如字符串的首地址等);
2、赋初值的方式不同;对字符数组赋初值一般用 static,如
static char str[ ]=“I love China!”;
而字符指针变量赋初值一般不用 static,如,
char *p=“I love China!”;
3、赋值方式不同;
( 1)对字符数组要么赋初值(即定义时赋值),要么对元
素赋值,下面的赋值方式是错误的,
char str[14]; str[14 ]=“I love China!”;
( 2)字符指针变量赋值一般可赋初值,或先定义后赋值,
如 char *a; a=,I love China!”;
等价于,char *a=,I love China!”;
4、定义指针变量时,编译后便分配了存储单元,但为空;
如,char str[10]; scanf(“%s”,str); 为正确,
但 char *a; scanf(“%s”,a); 为错误,
而 char *a,str[10]; a=str; scanf(“%s”,a); 为正确。
5、指针变量的值可改变;
例 10.21
main() 运行结果为,
{ char *a="I love China"; China
a=a+7;
printf("%s",a);
}
但对数组下面的是错误的,
char str[]="I love China";
str=str+7;
printf("%s",str);
也可用下标形式引用指针变量
例 10.21
main()
{ char *a="I love China!";
int i;
printf("The sixth character is %c\n",a[5]);
for(i=0;a[i]!='\0';i++)
printf("%c",a[i]);
} 运行结果为,The sixth character is e
I love China!
6、指针变量更方便。
( 1)赋初值简单,不用 static;
( 2)定义时可直接赋值;
( 3)可作地址用,也可使用下标。
10,5 函数的指针和指向函数的指针变量
重点:函数指针的概念及指向函数的指针变量
用指向函数的指针变量作函数参数
一、函数的指针及指向函数的指针变量
1、函数指针的概念
一个函数在编译时被分配一个入口地址,这个入口地址 称
为
函数的指针。
2、指向函数的指针变量
定义的一般形式,
数据类型标识符 ( *指针变量名) ()
例 10.23 求 a和 b中的大者。
main()
{int max();
int a,b,c;
char *format="a=%d,b=%d,max=%d";
scanf("%d,%d",&a,&b);
c=max(a,b);
printf(format,a,b,c);
printf("\n");
}
int max(int x,int y)
{int z;
return(z=x>y?x:y);
}
二、用指向函数的指针变量作函数的参数
传递的是函数的入口地址,
一般的使用形式,
,
sub( f1,f2); /* 主调函数 /*
,
sub(x1,x2)
int (*x1)( ),(*x2)( );
{int a,b,i,j;
, /* 次主调函数 */
a=(*x1)(i); /*x1,x2为被调函数 */
b=(*x2)(i,j);
,
}
f1
x1 f1
f2
x2 f2
说明,
( 1)一般在嵌套调用中使用,传递的是函数的入口地址;
( 2)次主调函数对其它函数操作,完成一定的功能;
( 3)主调函数的参数(实参)用函数名。
例 10.24 设一个函数 process,在调用它时, 每次实现
不同的功能, 调用三次, 分别求出 a,b中较大者, 较小者
和二者之和 。
main()
{int max(),min(),add();
int a,b;
printf("Enter a and b:");
scanf("%d,%d",&a,&b);
printf("max=");
process(a,b,max);
printf("min=");
process(a,b,min);
printf("sum=");
process(a,b,add);
printf("\n");
}
int max(int x,int y)
{ int z;
return(z=x>y?x:y);
}
int min(int x,int y)
{int z;
return(z=x<y?x:y);
}
int add(int x,int y)
{ int z;
z=x+y;
return(z);
}
process(x,y,fun)
int x,y;
int (*fun)();
{ int result;
result=(*fun)(x,y);
printf("%d\n",result);
}
例 10.25 求定积分,
y1=?10 (1+x2)dx
y2=?20 (1+x+x2+x3)dx
略
? ??
5.3
0
2
)
1
(3 dx
x
xy
10.6 返回指针值的函数
一、指针函数的定义
存储类别 数据类型标识符 *函数名(形参表列)
{ 函数体 }
如,int *a(int x,int y) /*存储类别为外部函数 /
{……,.}
二、指针函数的应用
例 10.26 有若干个学生的成绩(每个学生有 4门课
程),要求输入学生序号后,输出该生的全部成绩。
用指针函数来实现。
main()
{ static float score[][4]={{60,70,80,90},
{56,89,67,88},{34,78,90,66}};
float *search();
float *p;
int i,m;
printf("enter the number of student:");
scanf("%d",&m);
printf("the scores of No.%d are:\n",m);
p=search(score,m);
for(i=0;i<4;i++)
printf("%5.1f\t",*(p+i));
}
float *search(pointer,n)
float (*pointer)[4];
int n;
{float *pt;
pt= *(pointer+n);
return(pt);
}
运行结果,
enter the number of student:1 回车
the scores of No.1 are,
56.0 89.0 67.0 88.0
例 10.27对上例中的学生, 找出其中有不及格课程的学生及
其学生号 。
main()
{static float score[][4]={{60,70,80,90},
{50,89,67,88},{34,78,90,66}};
float *search(); float *p; int i,j;
for (i=0;i<3;i++)
{p=search(score+i);
if (p==*(score+i))
{printf("No.%d scores,",i);
for(j=0;j<4;j++)
printf("%5.1f ",*(p+j));
printf("\n");}
}
}
float *search(pointer)
float (*pointer)[4];
{ int i;
float *pt;
pt=*(pointer+1);
for(i=0;i<4;i++)
if(*(*pointer+i)<60)
pt=*pointer;
return (pt);
}
运行结果,
No.1 scores,56.0 89.0 67.0 88.0
No.2 scores,34.0 78.0 90.0 66.0
10,7 指针数组和指向指针的指针
重点:指针数组及其应用
一、指针数组
其元素均为指针型数据的数组,称为指针数组。
定义形式,类型标识符 *数组名 [数组长度说明 ]
如,int *p[4];
不能写成 int (*p)[4]
指针数组主要用于指向多个字符串,使字符串处
理方便。
例 10.28 对字符串排序,按由小到大输出。
思路:定义一指针数组,存放各字符串的首地址,排序,
交换首地址。
#include"string.h"
main()
{void sort(); void print();
static char *name[]={"Follow me","BASIC",
"Great Wall","FORTRAN","Computer design"};
int n=5;
sort (name,n);
print(name,n);
}
void sort(name,n)
char *name[];int n;
{char *temp;
int i,j,k;
for(i=0;i<n-1;i++)
{k=i;
for(j=i+1;j<n;j++)
if(strcmp(name[k],name[j])>0) k=j;
if(k!=i)
{temp=name[i];name[i]=name[k];name[k]=temp;}
}
}
void print(name,n)
char *name[];int n;
{int i;
for(i=0;i<n;i++)
rintf("%s\n",name[i]);
}
运行结果,
BASIC
Computer design
FORTRAN
Follow me
Great Wall
void print(char *name[],int n)
{ int i;
char *p;
p=name[0];
while(i<n)
{p=*(name+i++);
printf("%s\n",p);
}
}
二、指向指针的指针
即指向指针数据的地址。
上面 name是指针数组,元素均是指针数据,可定义一指针
变量,指向数组,对数组各元素操作。
定义形式,类型标识符 * * 变量名
如,char **P;
p为一个指向字符指针变量的指针变量。一般指向一数组的
首地址。
如,char *name[4],**p;
p=name;
又如,
p=name+2;
printf(“%d,%d,%s”,p,*p,**p);
输出,2000,3016,Great wall
3016 2004
2000
例 10.29
main()
{static char *name[]={"Follow me","BASIC",
"Great Wall","FORTRAN","Computer design"};
char **p;
int i; 运行结果,
for(i=0;i<5;i++) Follow me
{p=name+i; BASIC
printf("%s\n",*p); Great Wall
} FORTRAN
} Computer design
指针数组的元素也可以不指向字符串,而指向整型数据或
实型数据,例如,
int a[5]={1,3,5,7,9};
int *num[5]={&[0],&[1],&[2],&[3],&[4]};
int **p;
此时为了得到数据 5,可以先使 p=num+2,然后输出 **p。 *p
是 p间接指向的对象的地址。而 **p是 p间接指向的对象的值。
见下图。
例 10.30
main()
{ static a[5]={1,3,5,7,9};
static int *num[5]={&a[0],&a[1],
&a[2],&a[3],&a[4]};
int **p,i;
p=num;
for(i=0;i<5;i++)
{printf("%d\t",**p);
p++;}
} 运行结果,
1 3 5 7 9
间接访问的,单级间址,和,二级间址,
用指针访问便是间接访问。
若指针变量中存放的是目标变量的地址,称为,单级间
址,,
指向指针的指针为,二级间址,。还可以有多级间址。
三、指针数组作 main函数的参数
指针数组的一个应用是作为 main函数的形参。
前面应用 main函数均无参数,但可以有参数。
一般形式为,main(argc,argv)
问题:( 1)实参从何处来;( 2)形参的意义。
1,形参从命令行得到
如执行 file1.exe 文件,且文件中有以上参数,则可输入
file1 China Beijing 此即为 main函数的实参
使用一般形式,文件名 参数 1 参数 2 …… 参数 n
2、形参的意义
( 1) argc, 参数的数目,为整型,int argc
如,file1 China Beijing 为三个参数,传过来后使 argc得到 3。
( 2) argv, 为指向字符串的指针变量,得到的是命令行各
字符串的首地址,定义形式为,
char *argv[ ] 数组长度由命令行各字符串的个数确定
如,file1 China Beijing
使 argv[ ]的长度为 3,argv[0]指向 file1,argv[1]指向
China,argv[2]指向 Beijing。见下图。
3,main函数带参数的例子
考察参数回送命令 echo的内容,文件名为 echo.c
main(argc,argv)
int argc; char *argv[ ];
{while(--argc>0)
printf(“%s%c”,*++argv,(argc>1)?? ?,?\n?);
}
运行,若输入,echo Computer and C Language 回车
则显示,Computer and C Language
运行时 argc得到 5,argv[ ]得到 5个字符串的首地址。
说明,
( 1) main函数的形参名可以为其它的;
( 2)命令行的参数可以不显示,作为程序中所需要的。
10.8 指针型数据小结
一、有关指针的数据类型 (见下表)
二、指针运算小结
1、指针变量加减一个整数
如,p++,p--,p+i,p-i,p+=i,p-=i 等
实际含义如 p+i 代表地址计算,p+c*i, c为字
节数,整型 c=2、实型 c=4、字符型 c=1。
2、指针变量赋值
如,p=&a a为变量,p为指针变量
p=array array为数组名
p=&array[i ]
p=max max为函数名
p1=p2 p2为指针变量
注意,不能把一个整数或常数赋给 p,如 p=1000
同样也不能把 p赋给整型变量,如 i=p
3、指针变量可以赋空值
如,p=NULL; 实际 NULL是整数 0。
4、两个指针变量可以相减
如两个指针变量指向同一个数组的元素(如
右图),则 p2-p1=4-1=3,但 p1+p2无意义。
5、两个指针变量比较
若两个指针指向同一个数组的元素,则可以比较,
如,p1<p2 p1>p2
三、空类型指针
ANSI新标准增加了一种空类型指针,定义的一般形式为,
void * 变量名
具体使用时要做类型转换,如,void *p; (int *)p;
本章结束