第八章 指 针
第八章 指 针
8.1 指针的概念与定义
8.2
8.3
8.4 指针与函数
8.5 复杂指针
第八章 指 针
8.1 指针的概念与定义
8.1.1 指针的概念
图 8.1 内存分配表
第八章 指 针
8.1.2
1,
指针是一种存放地址值的变量, 像其它变量一样, 必
须在使用前定义 。 指针变量的命名遵守与其它变量相同的
规则, 即必须是唯一的标识符 。 指针定义的格式如下,
类型名 *指针名;
第八章 指 针
例 8.1 指针与非指针的定义
char *pcl,*pc2; / * pcl和 pc2均为指向 char型的指针 */
float *pf,percent;/ * pf是 float型的指针, 而 percent为普通的 float型变量 */
例 8.2 指针的指向
int *point;
scanf(" %d",point);
第八章 指 针
2,指针的有关运算符
两个有关的运算符,
&,取地址运算符 。
*,指针运算符 ( 或称, 间接访问, 运算符 ) 。
例如,&a为变量 a的地址, *p为指针 p所指向的存贮单
元的内容 。
&运算符只能作用于变量, 包括基本类型变量和数组的
元素, 结构体类型变量或结构体的成员 ( 第九章 ), 不能
作用于数组名, 常量或寄存器变量 。 例如,
第八章 指 针
double r,a[ 20] ;
int i;
register int k;
表达式 &r,&a[ 0],&a[ i]是正确的,而 &(2*r),
&a,&k是非法表示。
单目运算符 *是&的逆运算, 它的操作数是对象的地址, *
运算的结果是对象本身 。 单目 *称为间访运算符,, 间访,
就是通过变量的地址而不是变量名存取 ( 或引用 ) 变量 。 例
如, 如果 pc是指向字符变量 c的指针, 则 *(&c)和 *pc表示同
一字符对象 c。 因而赋值语句
*(&c)=′a′;
*pc=′a′;
c=′a′;
效果相同,都是将 ′a′存入变量 c。
第八章 指 针
例 8.3 取地址运算符。
int variable,*point;
point=&variable;
第八章 指 针
3,指针的使用
例 8.4 指针的使用。
main( )
{int a,b,*p1,*p2;
a=10; b=20;
p1=&a; p2=&b;
printf(" %d\t%d\n",*p1,*p2);
p1=&b; p2=&a;
printf(" %d\t%d\n",*p1,*p2);
}
程序运行结果,
10 20
20 10
第八章 指 针
程序说明,
(1) 在两个 printf函数调用语句中的 *是指针运算符, 这一
单目运算符的运算对象应该是指针或地址, 它的作用是得
到指针指向变量的值 。
(2) 在第一个 printf函数调用时,可以假设内存的分配如
图 8.2所示。
图 8.2 内存分配表
第八章 指 针
(3) 在第二个 printf函数调用时,内存的分配将如图 8.3所示。
图 8.3 内存分配表
第八章 指 针
例 8.5 指针的使用。
main( )
{int a,*pi;
float f,*pf;
a=10; f=20.5;
pi=&a; pf=&f;
printf(" %d\t%4.1f\n",a,f);
printf(" %d\t%4.1f\n",*pi,*pf);
}
程序运行结果,
10 20.5
10 20.5
第八章 指 针
程序说明,
图 8.4 内存分配表
第八章 指 针
8.2 指针作函数参数
例 8.6 函数参数的传递。
void swap(int x,int y);
main()
{
int a,b;
a=10; b=20;
swap(a,b);
printf(" a=%d,b=%d\n",a,b);
}
第八章 指 针
void swap(int x,int y)
{ int temp;
temp=x;
x=y;
y= temp;
}
运行结果,
a=10,b=20
第八章 指 针
图 8.5 swap函数被调用时的内存分配图
图 8.6 swap函数调用结束时的内存分配图
第八章 指 针
例 8.7 指针作函数参数。
void swap(int * x,int * y);
main()
{
int a,b,*p1,*p2;
a=10; b=20; p1=&a; p2=&b;
swap(p1,p2);
printf(" a=%d,b=%d\n",a,b); /* 或 printf(″%d %d″,*p1,*p2) */
}
void swap( int *pa,int *pb
{
int temp;
temp=*pa;
*pa=*pb;
*pb=temp;
}
运行结果,
a=20,b=10
第八章 指 针
图 8.7 swap函数被调用时的内存分配图
图 8.8 swap函数调用结束时的内存分配图
第八章 指 针
例 8.8 指针作函数参数。
void swap(int *,int *);
main()
{
int a,b,*p1,*p2;
a=10; b=20; p1=&a; p2=&b;
swap(p1,p2);
printf(" a=%d,b=%d\n",a,b);
}
void swap( int *pa,int *pb
{
int *temp;
temp=pa;
pa=pb;
pb=temp;
}
运行结果,
a=10,b=20
第八章 指 针
图 8.9 swap函数被调用时的内存分配图
图 8.10 swap函数调用结束时的内存分配图
第八章 指 针
例 8.9 指针作函数参数。
void swap(int *,int *);
main()
{
int a,b,*p1,*p2;
a=10; b=20; p1=&a; p2=&b;
swap(p1,p2);
printf(" a=%d,b=%d\n",a,b);
}
void swap( int *pa,int *pb
{
int *temp;
*temp=*pa;
*pa=*pb;
*pb=*temp;
}
第八章 指 针
可以将 swap函数改变成这样,
void swap( int *pa,int *pb
{
int *temp,t;
temp=&t;
*temp=*pa;
*pa=*pb;
*pb=*temp;
}
此时可以得到运行结果,
a=20,b=10
第八章 指 针
8.3 指 针 与 数 组
8.3.1 指向一维数组的指针
图 8.11 指向数组元素的指针
第八章 指 针
实际上, C语言允许这样的表达方式,pa[ i] 和 *(a+i),
它们等价于 *(pa+i)和 a[ i] 。 由此可见, 引用数组元素有两
种等价的形式,通过下标引用和通过指针引用 。 以数组 a为
例, 假定 pa指向 a[ 0], 元素的下标引用和指针引用的对应
关系如下 ( 写在同一行上的表达式是对同一元素的等价引用
形式 ),
a[ 0] *pa *a或 *(a+0)
a[ 1] *(pa+1) *(a+1)
a[ 2] *(pa+2) *(a+2)
… … …
a[ 9] *(pa+9) *(a+9)
第八章 指 针
元素地址的对应关系如下,
&a[ 0] pa a或 a+0
&a[ 1] pa+1 a+1
&a[ 2] pa+2 a+2
… … …
&a[ 9] pa+9 a+9
第八章 指 针
8.3.2 数组作函数参数
例 8.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(" %5d",*p); p++; }
printf(" \n" );
}
sort(int x[],int n)
{ int i,j,k,t;
for(i=0; i<n-1; i++)
{ k=i;
for(j=i+1; j<n; j++)
if(x[ j] >x[ k] ) k=j;
if(k!=i)
{ t=x[ i] ; x[ i] =x[ k] ; x[ k] =t; }
}
}
第八章 指 针
函数 sort的形参 x可以认为是 main函数中数组 a的别名,
所以在函数 sort中对 x的操作就是对 a的操作, 使得数组 a得以
排序, 完成了程序的要求 。 注意在 main函数中使用指针 p时,
指针当前指向的变化 。 当然, main函数中调用 sort函数时实
参可以是指针, 也可以是数组名, 在函数 sort中形参可以是
数组名, 也可以是指针 。 可将上例的程序改写如下,
main()
{ int i,a[ 10] ;
for(i=0; i<10; i++)
scanf(" %d",&a[ i] );
sort(a,10);
for(i=0; i<10; i++)
第八章 指 针
printf(" %5d",a[ i] );
}
sort(int *x,int n)
{ int i,j,k,t;
for(i=0; i<n-1; i++)
{k=i;
for(j=i+1; j<n; j++)
if(x[ j] >x[ k] ) k=j;
if(k!=i)
{ t=x[ i] ; x[ i] =x[ k] ; x[ k] =t;
}
}
第八章 指 针
8.3.3 指针和字符串
在 C语言中, 字符串 ( 例如 "I am a student") 指在内存中
存放的一串以 ′\0′结尾的若干个字符 。 例如, 可以这样定义和
初始化一个字符数组,
char string[ ] =" I am a student";
数组长度由字符串长度加 1确定 。 也可以定义一个字符数组,
然后用标准输入函数从外部设备读入一个字符串 。 例如,
char string[ 20] ;
scanf(" %s",string);
第八章 指 针
数组长度应能足够存放读入的最大长度的字符串 。
利用指针也可以表达字符串, 而且比用字符数组更为方
便灵活 。 例如, 可以这样定义和初始化一个字符指针,
char *point=" I am a student";
point是指向字符串 "I am a student"的指针, 即字符串的首地
址赋给了字符指针, 因此使一个字符指针指向一个字符串 。
也可以采用下面的方式,
char *point;
point=" I am a student";
第八章 指 针
例 8.11 字符串拷贝函数。
# include< stdio,h
void my -strcpy( char *t,char *s
{
while(( *t=*s)! =′\0′
{ s++;
t++;
}
}
第八章 指 针
下列对 my-strcpy函数的调用都是正确的,
( 1) my-strcpy( s1," I am a student") ;
( 2) ps1=&s1[ 0] ;
ps2=" I am a student";
my-strcpy(ps1,ps2);
( 3) ps2=&s2[ 0];
my-strcpy( ps2," I am a student") ;
my-strcpy( s1,s2); 或 my-strcpy( s1,ps2);
第八章 指 针
my-strcpy的定义可以写成更简练的形式;
void my-strcpy(char *t,char *s)
{
while(( *t++=*s++) ! =′\0′) ;
}
复制过程继续的条件是被复制的字符为非 0( 非 ′\0′) 。
由于组成字符串的任何字符 ( ′\0′除外 ) 的值都为非 0,所以
my -strcpy还可以进一步简化为
void my -strcpy( char *t,char *s)
{
while( *t++=*s++) ;
}
第八章 指 针
例 8.12 字符串比较函数。
int my -strcmp(char *s,char *t)
{
for(; *s==*t; s++,t++)
if(*s==′\0′) return 0;
return(*s-*t);
}
第八章 指 针
8.3.4 指向多维数组的指针
static int a[ 2][ 4] ={{1,3,5,7},{2,4,6,8}};
我们可以假设数组 a在内存中的分配情况如下,
第八章 指 针
详细区分说明如下,
第八章 指 针
例 8.13 多维数组。
main()
{
static int a[ 3][ 4] ={{1,3,5,7},{2,4,6,8},{10,20,30,40}};
int *p;
for(p=a[ 0] ; p<a[ 0] +12; p++) /* 注 1 */
{
if((p-a[ 0] )%4==0) printf(" \n" ); /* 注 2 */
printf(" %4d",*p);
}
}
运行结果,
1 3 5 7
2 4 6 8
10 20 30 40
第八章 指 针
例 8.14 多维数组。
main()
{
static int a[ 3][ 4] ={{1,3,5,7},{2,4,6,8},{10,20,30,40}};
int i,j,(*p)[ 4] ;
p=a;
for(i=0; i<3; i++)
{
for(j=0; j<4; j++)
printf(" %4d",*(*(p+i)+j));
printf(" \n" );
}
}
运行结果,
1 3 5 7
2 4 6 8
10 20 30 40
第八章 指 针
( 1) 形参说明为指向数组元素的指针, 实参为数组元
素的地址或指向元素的指针 。
例如,
调用函数 f,用数组元素的地址作实参,
int a[ 2][ 3] ;
void f(int *,int);

f(a[ 0],2*3);

第八章 指 针
调用函数 f,用指向数组元素的指针作实参,
int a[ 2][ 3],*pi;
void f(int *,int);

pi=a[ 0] ; /* 或 pi=&a[ 0][ 0] */
f(pi,2*3);

pi是指向元素 a[ 0][ 0]的指针。
函数 f的定义,〖 HT5”〗
void f(int *pi,int size)
{

}
形参 pi说明为列指针。
第八章 指 针
( 2) 形参说明为行指针,实参为行地址或行指针。例如,
调用函数 f,用行指针作实参,
{
int a[ 2][ 3];
void f(int (*)[ 3],int);

f(a,2);

}
第八章 指 针
实参 a是行指针, 类型为 int(*)[ 3] ;实参 2是二维
数组 a的行数 。
调用函数 f,用行指针作实参,
int a[ 2] [ 3],(*pa)[ 3] ;
void f(int(*)[ 3],int);

pa=a;
f(pa,2);

第八章 指 针
pa是行指针,赋值语句, pa=a;, 使 pa指向 a的第 0行,
pa的类型与 a的类型相同。
函数 f的定义,
void f(int (*pa)[ 3],int size)
{

}
第八章 指 针
例 8.15 行指针作函数参数
输入一个用年, 月, 日表示的日期, 定义函数 day- of-
year将它转换成该年的第几天;输入某年的第几天, 定义函数
month-day将它转换成该年的某月某日 。
# include<stdio.h>
/* day-of-year,从月份和日期计算为一年中的第几天 */
int day -of -year(int year,int month,int day,int *pi)
{
int i,leap;
leap=year%4==0&&year%100![KG-*4]=0||year%400==0;
for(i=1; i<month; i++)
day+=*(pi+leap*13+i);
return (day);
}
第八章 指 针
/* month-day,从一年中的第几天计算月份和日期 */
void month -day(int year,int yday,int (*pdaytab)[ 13],int *pmonth,int pday)
{
int i,leap;
leap=year%4==0&&year%100![KG-*4]=0||year%400==0;
for(i=1; yday>*(*(pdaytab+leap)+i); i++)
yday-=*(*(pdaytab+leap)+i);
*pmonth=i;
*pday=yday;
}
int main(void)
{
第八章 指 针
int daytab[ 2][ 13] ={
{0,31,28,31,30,31,30,31,31,30,31,30,31},
{0,31,29,31,30,31,30,31,31,30,31,30,31}
},y,m,d,yd;
printf("input year,month,day,\n");
scanf("%d%d%d",&y,&m,&d);
yd=day -of -year(y,m,d,&daytab[ 0][ 0] );
printf("day of year is %d\n",yd);
printf("input year,day -of -year,\n");
scanf("%d%d",&y,&yd);
第八章 指 针
month -day(y,yd,daytab,&m,&d);
printf("%d,%d in %d\n",m,d,y);
return 0;
}
input year,month,day,(输出 )
1995 12 11 (输入 )
day of year is 345 (输出 )
input year,day -of -year,(输出 )
1994 280 (输入 )
10,7 in 1994 (输出 )
第八章 指 针
8.3.5 指针数组
指针变量可以同其它变量一样作为数组的元素, 由指
针变量组成的数组称为指针数组, 组成数组的每个元素都
是相同类型的指针 。
类型名 *数组名 [ 常量表达式 ],
其中, *数组名 [ 常量表达式 ], 是指针数组说明符 。 例
如,
int *ps[ 10] ;
第八章 指 针
设有二维数组说明,
int a[ 4] [ 4] ;
用指针数组表示数组 a,就是把 a看成 4个一维数组, 并
说明一个有 4个元素的指针数组 pa,用于集中存放 a的每一
行元素的首地址, 且使指针数组的每个元素 pa[ i] 指向 a
的相应行 。 于是可以用指针数组名 pa或指针数组元素 pa[ i]
引用数组 a的元素 。
指针数组 pa的说明和赋值如下,
第八章 指 针
int *pa[ 4],a[ 4] [ 4] ;
pa[ 0] =&a[ 0] [ 0] ; 或 pa[ 0] =a[ 0] ;
pa[ 1] =&a[ 1] [ 0] ; 或 pa[ 1] =a[ 1] ;
pa[ 2] =&a[ 2] [ 0] ; 或 pa[ 2] =a[ 2] ;
pa[ 3] =&a[ 3] [ 0] ; 或 pa[ 3] =a[ 3] ;
*(*(pa+i)+0),*pa[ i] 或 *(pa[ i] +0)(i=0,1,2,3)引用第 i
行第 0列元素 a[ i] [ 0]; *(*(pa+i)+1)或 *(pa[ i] +1)引用第 i行第 1列元素 a[ i]
[ 1] ; … 。
第八章 指 针
用指针数组表示二维数组在效果上与数组的下标表示是
相同的, 只是表示形式不同 。 用指针数组表示时, 需要额外
增加用作指针的存贮开销;但用指针方式存取数组元素比用
下标速度快, 而且每个指针所指向的数组元素的个数可以不
相同 。 例如, 可用有 5个元素的指针数组和 5个不同长度的整
型数组来描述下面的三角矩阵,
第八章 指 针
存贮三角矩阵的数组和每一行的指针可说明如下,
int a1[ 1],a2[ 2],a3[ 3],a4[ 4],a5[ 5],*pa[ 5] ;
下面的语句使 pa的每个元素指向三角矩阵的每一行,
pa[ 1] =&a1[ 0] ;
pa[ 2] =&a2[ 0] ;
pa[ 3] =&a3[ 0] ;
pa[ 4] =&a4[ 0] ;
pa[ 5] =&a5[ 0] ;
第八章 指 针
8.4 指 针 与 函 数
8.4.1 指向函数的指针
C语言可以定义指向函数的指针, 函数型指针的定义形式,
类型标识符 (*指针名 )()
例如,
int (*fp)()
说明, fp是指向 int类型函数的指针 。 与指向数组的指针说明类
似, 说明符中用于改变运算顺序的 ( ) 不能省 。 如果将 ( *fp)
()写成 * fp(),则 fp
第八章 指 针
例 8.16 指向函数的指针 。
设一个函数 operate,在调用它的时候, 每次实现不同的
功能 。 输入 a和 b两个数, 第一次调用 operate得到 a和 b中最大
值, 第二次得到最小值, 第三次得到 a与 b之和 。
main()
{
int max(),min(),sum(),a,b; /* 必须进行函数声明,否则无法调用 */
printf(" Enter two number," );
scanf(" %d%d",&a,&b);
printf(" max=" ); operate(a,b,max);
printf(" min=" ); operate(a,b,min);
printf(" sum=" ); operate(a,b,sum);
第八章 指 针
}
max(int x,int y)
{
if(x>y) return(x);
else return(y);
}
min(int x,int y)
{
if(x<y) return(x);
else return(y);
}
sum(int x,int y)
{
return(x+y);
}
operate(int x,int y,int (*fun)())
{
printf(" %d\n",(*fun)(x,y));
}
第八章 指 针
程序运行情况,
Enter two number,5 9
max=9
min=5
sum=14
第八章 指 针
8.4.2 返回指针的函数
C的函数可以返回除数组, 共用体变量和函数以外的任
何类型数据和指向任何类型的指针 。
指针函数定义的一般形式,
类型标识符 *函数名 ( 参数表 ) { }
其中, *函数名 ( 参数表 ), 是指针函数定义符 。 例如,
int *a(int x,int y){ }
第八章 指 针
例 8.17 指针函数
# include<string.h>
# define STRLEN 81
# include <stdio.h>
char *maxstr(char *str1,char *str2)
{
if(strcmp(str1,str2)>=0) return(str1);
else return(str2);
}
main()
{
char string1[ STRLEN],string2[ STRLEN],*result;
printf(" Input two strings,\n" );
scanf(" %s%s",string1,string2);
result=maxstr(string1,string2);
printf(" The max string is,%s\n",result);
}
第八章 指 针
程序运行情况,
Input two strings,
abcde abcee
The max string is,abcee
第八章 指 针
8.5 复 杂 指 针
8.5.1 指向指针的指针
定义了指针 pointer,它指向 char型, 用它可以存放字符
型变量的地址, 并且可以用它对所指向的变量进行间接访
问 。 进一步定义,
char **p -p;
从运算符 *的结合性可以知道, 上述定义相当于,
char *(*p -p);
第八章 指 针
例 8.18 指向指针的指针。
main()
{
int a,*pointer,**p -p;
a=20;
pointer=&a;
p -p=&pointer;
printf(" %d,%u,%u\n",a,pointer,p -p);
printf(" %d,%u,%d\n",*pointer,*p -p,* *p -p);
}
第八章 指 针
图 8.12 内存分配表
运行结果,
20,2000,2050
20,2000,20
第八章 指 针
例 8.19 指向指针的指针。
main()
{
static char *country[] ={" CHINA"," ENGLAND",
, FRANCE"," GERMANY" };
char * *p;
int i;
for(i=0; i<4; i++)
{
p=country+i;
printf(" %s\n",*p);
}
}
第八章 指 针
运行结果,
CHINA
ENGLAND
FRANCE
GERMANY
第八章 指 针
8.5.2 命令行参数
例 8.20 echo程序。
# include<stdio.h>
main(int argc,char *argv[] )
{
int i;
for(i=1; i<argc; i++)
printf(" %s%s",argv[ i],(i<argc-1)? " ", " \n" );
printf(" \n" );
return 0;
}
第八章 指 针
程序运行的命令行情况,
echo hello world
运行结果,
hello world
argv[ 0] 为所调用程序的名字, 所以 argc至少为 1。 如
果 argc为 1,则在程序名后面没有命令行参数 。
第八章 指 针
例 8.21 命令行的三个参数, 前两个为两个整数, 第三
个确定程序输出的为最大值还是最小值 。
# include<stdio.h>
# include<stdlib.h>
# include<string.h>
int max(int x,int y)
{
return(x>y?x,y);
}
int min(int x,int y)
{
return(x>y?y,x);
}
第八章 指 针
main(int argc,char *argv[] )
{
int a,b;
char *operate -flag;
if(argc<2)
{
printf(" usage,abc integer1 interger2 operate -flag\n" );
exit(0);
}
a=atoi(argv[ 1] );
b=atoi(argv[ 2] );
operate -flag=argv[ 3] ;
if(strcmp(operate -flag," max" )==0)
第八章 指 针
printf(" The %s of%s and %s is,%d\n",argv[ 3],argv[ 1],argv[
2],max(a,b));
else if(strcmp(operate -flag," min" )==0)
printf(" The %s of %s and %s is,%d\n",argv[ 3],argv[ 1],argv
[ 2],min(a,b));
else
printf(" operate -flag should be max or min\n" );
}
程序生成 abc.exe后,
abc 20 30 max
The max of 20 and 30 is,30
第八章 指 针
8.5.3
注意,( 1) 数组的元素不能为函数 ( 可以为函数的指针 ) 。
( 2) 函数的返回值不能为数组或函数(可以为函数或数组
的指针)。
*是指针类型的标志, [ ] 是数组类型的标志, ( ) 是函
数类型的标志 。 ( ) 和 [ ] 的优先级高于 *,当 [ ], *或 ( )
和 *同时出现时, 先解释 ( ) 和 [ ] 后解释 *; ( ) 和 [ ] 属于
同一优先级, 二者同时出现时, 按从左到右顺序解释; 整个说
明符解释完之后, 再加上类型标识符就是最终的解释 。 ( ) 也
用于改变由优先级和结合性隐含的运算顺序, 对嵌套的 ( ),
从内向外解释 。
第八章 指 针
(1) char **pp;
pp,指向字符指针的指针 。
(2) int (*daytab)[ 13] ;
daytab,指向由 13个整型元素组成的数组的指针 。
(3) int *daytab[ 13] ;
daytab,由 13个整型指针构成的指针数组 。
(4) void *comp();
comp,返回值为空类型指针的函数 。
(5) void (*comp)();
comp,指向无返回值函数的指针。
第八章 指 针
(6) int *p(char *a);
p是一个函数,它的形参是一字符型指针,它的返回
值是一整型指针。
(7) int (*p)(char*a);
p是一个指针,它指向一个函数,函数有一字符指针
作形参,
(8) int p(char (*a) [ ] );
p是一个返回整型量的函数,它有一个形参,形参的
类型是指向字符数组的指针。
(9) int *p(char *a[ ] );
p是一个函数,返回一整型指针,它的形参是一个字
符指针数组。