第十章 指针
C程序设计中使用指针可以,
使程序简洁、紧凑、高效
有效地表示复杂的数据结构
动态分配内存
得到多于一个的函数返回值
§ 10.1 指针的概念
变量与地址程序中,int i;
float k;
内存中每个字节有一个编号 -----地址
…...
…...
2000
2001
2002
2005
内存
0
2003
i
k
编译或函数调用时为其分配内存单元变量 是对程序中数据存储空间的抽象
…...
…...
2000
2004
2006
2005
整型变量 i
10
变量 i_pointer
2001
2002
2003
指针与指针变量
指针:一个变量的地址
指针变量:专门存放变量地址的变量叫 ~
2000
指针指针变量变量的 内容变量的 地址指针变量变量变量地址 (指针 )
变量值指向 地址存入指针变量
&与 *运算符
含义含义,取变量的地址单目运算符优先级,2
结合性,自右向左含义,取指针所指向变量的内容单目运算符优先级,2
结合性,自右向左
两者关系:互为 逆运算
理解
…...
…...
2000
2004
2006
2005
整型变量 i
10
变量 i_pointer
2001
2002
2003
2000 指针变量
i_pointer-----指针变量,它的内容是地址量
*i_pointer----指针的 目标变量,它的内容是数据
&i_pointer---指针变量占用内存的地址
2000 10
i_pointer *i_pointer
&i_pointer
i
i_pointer &i &(*i_pointer)
i *i_pointer *(&i)
i_pointer = &i = &(*i_pointer)
i = *i_pointer = *(&i)
直接访问与间接访问
直接访问:按变量地址存取变量值
间接访问:通过存放变量地址的变量去访问变量例 i=3; -----直接访问指针变量
…...
…...
2000
2004
2006
2005
整型变量 i
10
变量 i_pointer
2001
2002
2003
2000
3
例 *i_pointer=20; -----间接访问
20
指针变量
…...
…...
2000
2004
2006
2005
整型变量 i
10
变量 i_pointer
2001
2002
2003
2000
整型变量 k
例 k=i; --直接访问
k=*i_pointer; --间接访问
10

§ 10.2 指针变量指针变量 与其 所指向的变量 之间的关系
指针变量的定义
一般形式,[存储类型 ] 数据类型 *指针名;
3
变量 i
2000
i_pointer
*i_pointer
i *i_pointer
&i i_pointer
i=3; *i_pointer=3
变量合法标识符指针变量本身的存储类型 指针的目标变量的数据类型 表示定义指针变量 不是‘ *’运算符例 int *p1,*p2;
float *q ;
static char *name;
注意:
1,int *p1,*p2; 与 int *p1,p2;
2,指针变量名是 p1,p2,不是 *p1,*p2
3,指针变量只能指向定义时所规定类型的变量
4、指针变量定义后,变量值不确定,应用前必须先赋值
指针变量的初始化一般形式,[存储类型 ] 数据类型 *指针名 =初始地址值 ;
赋给指针变量,
不是赋给目标变量例 int i;
int *p=&i; 变量必须 已说明过类型 应一致例 int *p=&i;
int i;
例 int i;
int *p=&i;
int *q=p; 用已初始化指针变量作初值例 main( )
{ int i;
static int *p=&i;
..............
} (?)
不能用 auto变量的地址去初始化 static型指针例 main( )
{ int i=10;
int *p;
*p=i;
printf(“%d”,*p);
}
危险!
例 main( )
{ int i=10,k;
int *p;
p=&k;
*p=i;
printf(“%d”,*p);
}
指针变量必须 先赋值,再使用
…...
…...
2000
2004
2006
2005
整型变量 i
10
指针变量 p
2001
2002
2003
随机例 指针的概念
main()
{ int a;
int *pa=&a;
a=10;
printf("a:%d\n",a);
printf("*pa:%d\n",*pa);
printf("&a:%x(hex)\n",&a);
printf("pa:%x(hex)\n",pa);
printf("&pa:%x(hex)\n",&pa);
}
运行结果:
a:10
*pa:10
&a:f86(hex)
pa:f86(hex)
&pa:f88(hex)
…...
…...
f86
f8a
f8c
f8b
整型变量 a
10
指针变量 pa
f87
f88
f89 f86
例 输入两个数,并使其从大到小输出
main()
{ int *p1,*p2,*p,a,b;
scanf("%d,%d",&a,&b);
p1=&a; p2=&b;
if(a<b)
{ p=p1; p1=p2; p2=p;}
printf("a=%d,b=%d\n",a,b);
printf("max=%d,min=%d\n",*p1,*p2);
}
运行结果,a=5,b=9
max=9,min=5
…...
…...
指针变量 p1
指针变量 p
2000
2008
2002
2004
2006
指针变量 p2
整型变量 b
整型变量 a5
2006
9
2008
2006
2008
2006
指针变量作为函数参数 ——地址传递特点,共享内存,“双向,传递
swap(int x,int y)
{ int temp;
temp=x;
x=y;
y=temp;
}
main()
{ int a,b;
scanf("%d,%d",&a,&b);
if(a<b) swap(a,b);
printf("\n%d,%d\n",a,b);
}
例 将数从大到小输出 …...
…...
2000
2008
200A
2002
2004
2006
5 变量 a变量 b
(main)
9
变量 temp
变量 y
变量 x(swap)
5
5
95
9
COPY
指针变量作为函数参数 ——地址传递特点,共享内存,“双向,传递
swap(int x,int y)
{ int temp;
temp=x;
x=y;
y=temp;
}
main()
{ int a,b;
scanf("%d,%d",&a,&b);
if(a<b) swap(a,b);
printf("\n%d,%d\n",a,b);
}
例 将数从大到小输出值传递
…...
…...
2000
2008
200A
2002
2004
2006
5 变量 a变量 b
(main)
9
运行结果,5,9
swap(int *p1,int *p2)
{ int p;
p=*p1;
*p1=*p2;
*p2=p;
}
main()
{ int a,b;
int *pointer_1,*pointer_2;
scanf("%d,%d",&a,&b);
pointer_1=&a; pointer_2=&b;
if(a<b)swap(pointer_1,pointer_2);
printf("\n%d,%d\n",a,b);
}
…...
2000
2008
200A
2002
2004
2006
200C
200E
2010,..
5
9
整型变量 a
整型变量 b
(main)
指针 pointer_1
指针 pointer_220002002
(swap) 指针 p1
指针 p2
整型 p
5
9
2000
2002
COPY
5
例 将数从大到小输出
§ 10.3 指针与数组
指向数组元素的指针变量例 int array[10];
int *p;
p=&array[0]; //? p=array;
或 int *p=&array[0];
或 int *p=array;
array[0]
array[1]
array[2]
array[3]
array[9]
...
整型指针 p &array[0]
p
数组名 是表示数组 首地 址的 地址常量
指针的运算
指针变量的赋值运算
p=&a; (将变量 a地址?p)
p=array; (将数组 array首地址?p)
p=&array[i]; (将数组元素地址?p)
p1=p2; (指针变量 p2值?p1)
不能把一个整数?p,也不能把 p的值?整型变量如 int i,*p;
p=1000; (?)
i=p; (?)
指针变量与其指向的变量具有相同 数据类型
指针的算术运算:
p?i? p?i?d (i为整型数,d为 p指向的变量所占字节数 )
p++,p--,p+i,p-i,p+=i,p-=i等
若 p1与 p2指向同一数组,p1-p2=两指针间元素个数?(p1-p2)/d
p1+p2 无意义例 p指向 float数,则 p+1? p+1?4
例 p指向 int型数组,且 p=&a[0];
则 p+1 指向 a[1]
例 int a[10];
int *p=&a[2];
p++;
*p=1;
例 int a[10];
int *p1=&a[2];
int *p2=&a[5];
则,p2-p1=3;
a[0]
a[1]
a[2]
a[3]
a[4]
a[5]
a[6]
a[7]
a[8]
a[9]
a数组p
p+1,a+1
p+i,a+i
p+9,a+9
1
数组元素表示方法
a[0]
a[1]
a[2]
a[3]
a[9]
...
a
a+9
a+1
a+2
地址 元素下标法
a[0]
a[1]
a[2]
a[9]
a[0]
a[1]
a[2]
a[3]
a[9]
...
p
p+9
p+1
p+2
地址 元素指针法
*p
*(p+1)
*(p+2)
*(p+9)
[] 变址运算符
a[i]? *(a+i)
a[i]? p[i]? *(p+i)?*(a+i)
*a
*(a+1)
*(a+2)
*(a+9)
p[0]
p[1]
p[2]
p[9]
例 int a[]={1,2,3,4,5,6,7,8,9,10},*p=a,i;
数组元素地址的正确表示:
( A) &(a+1) ( B) a++ ( C) &p ( D) &p[i]?
数组名是 地址常量
p++,p-- (?)
a++,a-- (?)
a+1,*(a+2) (?)
main()
{ int i,*p,a[7];
p=a;
for(i=0;i<7;i++)
scanf("%d",p++);
printf("\n");
for(i=0;i<7;i++,p++)
printf("%d",*p);
}
例 10.6 通过指针变量输出 a数组的 10个元素注意指针的当前值
p=a;
p
p 5
8
7
6
2
7
3
0
1
2
3
4
5
6
a
p
p
p
p
p
p
指针变量可以指到 数组后 的内存单元
数组名作函数参数
数组名作函数参数,是 地址传递
数组名作函数参数,实参与形参的对应关系实参 形参数组名指针变量数组名指针变量数组名数组名指针变量指针变量例 10.7 将数组 a中的 n个整数按相反顺序存放
i j
3 7 9 11 0 6 7 5 4 2
0 1 2 3 4 5 6 7 8 9
i ji ji jji
117 6 05 94 72 3
实参与形参均用数组
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()
{ int i,a[10]={3,7,9,11,0,6,7,5,4,2};
inv(a,10);
printf("The array has been reverted:\n");
for(i=0;i<10;i++)
printf("%d,",a[i]);
printf("\n");
}
m=4
例 将数组 a中的 n个整数按相反顺序存放 (形参用指针变量 )
void inv(int *x,int n)
{ int t,*p,*i,*j,m=(n-1)/2;
i=x; j=x+n-1; p=x+m;
for(;i<=p;i++,j--)
{ t=*i; *i=*j; *j=t; }
}
main()
{ int i,a[10]={3,7,9,11,0,6,7,5,4,2};
inv(a,10);
printf("The array has been reverted:\n");
for(i=0;i<10;i++)
printf("%d,",a[i]);
printf("\n");
}
实参用数组,形参用指针变量
3
7
9
11
0
6
7
5
4
2
a[0]
a[1]
a[2]
a[3]
a[4]
a[5]
a[6]
a[7]
a[8]
a[9]
x
p=x+m
a数组
6
0
7
11
5
9
4
7
2
3
i
j
i
j
i
j
ji
j
i
例 10.9 将数组 a中的 n个整数按相反顺序存放
void inv(int *x,int n)
{ int t,*i,*j,*p,m=(n-1)/2;
i=x; j=x+n-1; p=x+m;
for(;i<=p;i++,j--)
{ t=*i; *i=*j; *j=t; }
}
main()
{ int i,a[10],*p=a;
for(i=0;i<10;i++,p++)
scanf("%d",p);
p=a; inv(p,10);
printf("The array has been reverted:\n");
for(p=a;p<a+10;p++)
printf("%d",*p);
}
实参与形参均用指针变量例 10.9 将数组 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;
}
}
main()
{ int i,a[10],*p=a;
for(i=0;i<10;i++,p++)
scanf("%d",p);
p=a; inv(p,10);
printf("The array has been reverted:\n");
for(p=arr;p<arr+10;p++)
printf("%d ",*p);
}
实参用指针变量,形参用数组
§ 10.4 指针与字符串
字符串表示形式
用字符数组实现例 main( )
{ char string[]=“I love China!”;
printf(“%s\n”,string);
}
I
l
o
v
e
C
h
i
string[0]
string[1]
string[2]
string[3]
string[4]
string[5]
string[6]
string[7]
string[8]
string[9]
string
string[10]
string[11]
string[12]
string[13]
n
!
a
\0
用字符指针实现访问字符串例 main( )
{ char *string=“I love China!”;
printf(“%s\n”,string);
}
I
l
o
v
e
C
h
i
string
n
!
a
\0
字符指针 初始化,把字符串 首地址 赋给 string
char *string;
string=“I love China!”;
string
*string!=0
字符串指针作函数参数例 用函数调用实现字符串复制
( 1)用字符数组作参数
( 2)用字符指针变量作参数
a
I
a
m
a
t
e
a
c
e
h
\0
r
.
from
a b
y
u
a
r
a
s
u
t
n
d
e
to
b
o
e
t
.
\0
I
a
a
e
c
e
h
\0
r
.
t
.
\0
m
t
a
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.";
char b[]="You are a student.";
printf("string_a=%s\n string_b=%s\n",a,b);
copy_string(a,b);
printf("\nstring_a=%s\nstring_b=%s\n",a,b);
}
void copy_string(char *from,char *to)
{ for(;*from!='\0';from++,to++)
*to=*from;
*to='\0';
}
main()
{ char *a="I am a teacher.";
char *b="You are a student.";
printf("string_a=%s\nstring_b=%s\n",a,b);
copy_string(a,b);
printf("\nstring_a=%s\nstring_b=%s\n",a,b);
}
字符指针变量与字符数组
char *cp; 与 char str[20];
str由若干元素组成,每个元素放一个字符;而 cp中存放字符串首地址
char str[20]; str=“I love China!”; (?)
char *cp; cp=“I love China!”; (?)
str是地址 常量 ; cp是地址变量
cp接受键入字符串时,必须 先开辟存储空间例 char str[10];
scanf(“%s”,str); (?)
而 char *cp;
scanf(“%s”,cp); (?)
改为,char *cp,str[10];
cp=str;
scanf(“%s”,cp); (?)
§ 10.5 指针与函数
函数指针:函数在编译时被分配的入口地址,用函数名表示
max
…...
指令 1
指令 2
函数指针变量赋值,如 p=max;
函数返回值的数据类型 专门存放函数入口地址可指向返回值类型相同的不同函数
指向函数的指针变量
定义形式,数据类型 (*指针变量名 )();
如 int (*p)();
函数指针变量指向的函数必须有 函数说明
函数调用形式,c=max(a,b);? c=(*p)(a,b);? c=p (a,b);
对函数指针变量 p?n,p++,p--无意义
( )不能省
int (*p)() 与 int *p()不同例 10.23 用函数指针变量调用函数,比较两个数大小
main()
{ int max(int,int);
int a,b,c;
scanf("%d,%d",&a,&b);
c=max(a,b);
printf("a=%d,b=%d,max=%d\n",a,b,c);
}
int max(int x,int y)
{ int z;
if(x>y) z=x;
else z=y;
return(z);
}
main()
{ int max(int,int),(*p)();
int a,b,c;
p=max;
scanf("%d,%d",&a,&b);
c=(*p)(a,b);
printf("a=%d,b=%d,max=%d\n",a,b,c);
}
int max(int x,int y)
{ int z;
if(x>y) z=x;
else z=y;
return(z);
}
用函数指针变量作函数参数例 10.24 用函数指针变量作参数,求最大值、最小值和两数之和
void main()
{ int a,b,max(int,int),
min(int,int),add(int,int);
void process(int,int,int (*fun)());
scanf("%d,%d",&a,&b);
process(a,b,max);
process(a,b,min);
process(a,b,add);
}
void process(int x,int y,int (*fun)())
{ int result;
result=(*fun)(x,y);
printf("%d\n",result);
}
max(int x,int y)
{ printf(“max=”);
return(x>y?x:y);
}
min(int x,int y)
{ printf(“min=”);
return(x<y?x:y);
}
add(int x,int y)
{ printf(“sum=”);
return(x+y);
}
§ 10.6 返回指针值的函数
函数定义形式:
类型标识符 *函数名 (参数表 );
例 int *f(int x,int y)
例 指针函数实现:有若干学生成绩,要求输入学生序号后,
能输出其全部成绩
main()
{ float score[][4]={{60,70,80,90},
{56,89,67,88},{34,78,90,66}};
float *search(float (*pointer)[4],int n),*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.2f\t",*(p+i));
}
float *search(float (*pointer)[4],int n)
{ float *pt;
pt=*(pointer+n);
return(pt);
}
pointer
pointer+1
34 78 90 66
56 89 67 88
60 70 80 90
score数组
p p p p
例 写一个函数,求两个 int型变量中居于较大值的变量的地址
int *f1(int *x,int *y)
{
if(*x>*y)
return x;
else
return y;
}
main()
{ int a=2,b=3;
int *p;
p=f1(&a,&b);
printf("%d\n",*p);
}
…...
2000
2008
200A
2002
2004
2006
2
3
指针变量 y
指针变量 x(f1)
2002
2000
COPY
变量 a
变量 b
(main)
指针变量 p**
§ 10.7 指针数组和指向指针的指针
指针数组
定义:数组中的元素为指针变量
定义形式,[存储类型 ] 数据类型 *数组名 [数组长度说明 ];
例 int *p[4];
指针所指向变量的数据类型指针本身的存储类型区分 int *p[4]与 int (*p)[4]?指针数组赋值与初始化例 对字符串排序(简单选择排序)
main()
{ void sort(char *name[],int n),print(char *name[],int n);
char *name[]={"Follow me","BASIC",
"Great Wall","FORTRAN","Computer "};
int n=5;
sort(name,n);
print(name,n);
}
void sort(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;}
}
}
name[0]
name[1]
name[2]
name[3]
name[4]
name
Great Wall
FORTRAN
Computer
Follow me
BASIC
k
jk
j
j
j
i=0
例 对字符串排序(简单选择排序)
main()
{ void sort(char *name[],int n),print(char *name[],int n);
char *name[]={"Follow me","BASIC",
"Great Wall","FORTRAN","Computer "};
int n=5;
sort(name,n);
print(name,n);
}
void sort(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;}
}
}
name[0]
name[1]
name[2]
name[3]
name[4]
name
Great Wall
FORTRAN
Computer
Follow me
BASICk
k
j
j
j
i=1
k
例 对字符串排序(简单选择排序)
main()
{ void sort(char *name[],int n),print(char *name[],int n);
char *name[]={"Follow me","BASIC",
"Great Wall","FORTRAN","Computer "};
int n=5;
sort(name,n);
print(name,n);
}
void sort(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;}
}
}
name[0]
name[1]
name[2]
name[3]
name[4]
name
Great Wall
FORTRAN
Computer
Follow me
BASICk
k j
j
i=2
例 对字符串排序(简单选择排序)
main()
{ void sort(char *name[],int n),print(char *name[],int n);
char *name[]={"Follow me","BASIC",
"Great Wall","FORTRAN","Computer "};
int n=5;
sort(name,n);
print(name,n);
}
void sort(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;}
}
}
name[0]
name[1]
name[2]
name[3]
name[4]
name
Great Wall
FORTRAN
Computer
Follow me
BASIC
k
k j
i=3
例 对字符串排序(简单选择排序)
main()
{ void sort(char *name[],int n),print(char *name[],int n);
char *name[]={"Follow me","BASIC",
"Great Wall","FORTRAN","Computer "};
int n=5;
sort(name,n);
print(name,n);
}
void sort(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;}
}
}
name[0]
name[1]
name[2]
name[3]
name[4]
name
Great Wall
FORTRAN
Computer
Follow me
BASIC
多级指针
定义,指向指针的指针
一级指针,指针变量中存放目标变量的地址
p1
&p2 &i 3
P2(指针变量 ) i(整型变量 )例 int **p1;
int *p2;
int i=3;
p2=&i;
p1=&p2;
**p1=5;
二级指针,指针变量中存放一级指针变量的地址例 int *p;
int i=3;
p=&i;
*p=5;
&i 3
P(指针变量 ) i(整型变量 )
一级指针 单级间接寻址二级指针 一级指针 目标变量二级间接寻址
定义形式,[存储类型 ] 数据类型 **指针名;
如 char **p;
例 int i,**p;
p=&i; (?)//p是二级指针,不能用变量地址为其赋值指针本身的存储类型 最终目标变量的数据类型 *p是 p间接指向对象的地址**p是 p间接指向对象的值例 int i=3;
int *p1;
int **p2;
p1=&i;
p2=&p1;
**p=5;
i
p1
p2
3
&i
&p1
**p2,*p1
*p2
多级指针例 三级指针 int ***p;
四级指针 char ****p;
例 10.28使用指向指针的指针main()
{char *name[]={"Follow me","BASIC",
"Great Wall","FORTRAN","Computer design "};
char **p;
int i;
for(i=0;i<5;i++)
{p=name+i;
printf(“%s\n”,*p);
}
}
定义 含义
int i;
int *p;
int a[n];
int *p[n];
int (*p)[n];
int f();
int *p();
int (*p)();
int **p;
定义整型变量 i
p为指向整型数据的指针变量定义含 n个元素的整型数组 a
n个指向整型数据的指针变量组成的指针数组 p
p为指向含 n个元素的一维整型数组的指针变量
f为返回整型数的函数
p为返回指针的函数,该指针指向一个整型数据
p为指向函数的指针变量,该函数返回整型数
p为指针变量,它指向一个指向整型数据的指针变量指针的数据类型例 下列定义的含义
( 1) int *p[3];
( 2) int (*p)[3];
( 3) int *p(int);
( 4) int (*p)(int);
( 5) int *(*p)(int);
( 6) int (*p[3])(int);
( 7) int *(*p[3])(int); 函数指针数组,函数返回 int型指针指针数组指向一维数组的指针返回指针的函数指向函数的指针,函数返回 int型变量指向函数的指针,函数返回 int 型指针函数指针数组,函数返回 int型变量