第十章 指针
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
随机
?零指针与空类型指针
?零指针,(空指针 )
?定义,指针变量值为零
?表示,int * p=0;
p指向地址为 0的单元,
系统保证该单元不作它用
表示指针变量值 没有意义
#define NULL 0
int *p=NULL;
?p=NULL与未对 p赋值不同
?用途,
?避免指针变量的非法引用
?在程序中常作为 状态 比较
例 int *p;
,....,
while(p!=NULL)
{,..…
}
?void *类型指针
?表示, void *p;
?使用时要进行 强制类型转换
例 char *p1;
void *p2;
p1=(char *)p2;
p2=(void *)p1;
表示不指定 p是指向哪一种
类型数据的指针变 量
例 指针的概念
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
整型变量 a 5
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
9 5
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_2 2000 2002
(swap) 指针 p1
指针 p2
整型 p
5
9
2000
2002
COPY
5
例 将数从大到小输出
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_2 2000 2002
5
9
例 将数从大到小输出
运行结果,9,5
地址传递
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);
}
运行结果,9,9
编译警告!
结果不对!
int x;
int *p=&x;x;
例 将数从大到小输出 …..
.
2000
2008
200A
2002
2004
2006
200C
200E
2010,.,
5
9
整型变量 a
整型变量 b
(main)
指针 pointer_1
指针 pointer_2 2000 2002
9
9
2000
2002
COPY (swap) 指针 p1
指针 p2
指针 p ****
假设 2000
指针变量在使用前
必须赋值!
/*ch9_32.c*/
swap(int x,int y)
{ int t;
t=x; x=y; y=t;
}
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);
}
运行结果,5,9
例 将数从大到小输出
值传递
…...
2000
2008
200A
2002
2004
2006
200C
200E
2010,.,
5
9
整型 a
整型 b
(main)
pointer_1
pointer_2 2000 2002
9
COPY (swap) 整型 x
整型 b
整型 t 5 5
5 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("%d,%d",*pointer_1,*pointer_2);
}
…...
2000
2008
200A
2002
2004
2006
200C
200E
2010,.,
5
9
整型 a
整型 b
(main)
pointer_1
pointer_2 2000 2002
2000
2002
COPY (swap)
指针 p1
指针 p2
指针 p **** 2000
地址传递
2000
2002
指针与数组
指针与数组
指针与数组
§ 10.3 指针与数组
一、指针与一维数组
1,指向数组元素的指针变量
例 int array[10];
int *p;
p=&array[0]; //? p=array;
或 int *p=&array[0];
或 int *p=array; 整型指针 p
array[0]
array[1]
array[2]
array[3]
array[9]
..,
&array[0]
p
数组名 是表示数组 首地 址的 地址常量
2、通过指针或指针变量引用数组元素
C 语言规定:如果指针变量 p 以指向数组中的一个元素,则 p+1
指向同一数组中的下一个元素(而不是将 p简单的加 1)。例如:
数组元素是实型,每个元素占 4个字节,则 p+1 意味着使 p的值
加 4 个字节,以使 p指向下一个元素。 p+1所代表的地址实际上是
p+1× d,d 是一个数组元素所占的字节数(对整型 d=2; 实型
d=4 ; 字符型 d=1)
例如,
int a[4];
int *p=a;
则有,
p 指向数组 a的第一个元素 a[0]
p+1 指向数组 a的第二个元素 a[1]
即,p 指向地址 2000
p+1指向地址 2002
a[0]
a
a
p
a[1]
a[2]
a[3]
2000
2001
2002
2003
2004
2005
2006
2007
2008
P+1
a[0]
a[1]
a[2]
a[i]
a[7]
a[8]
a[9]
a数组 p
p+1,a+1
p+i,a+i
p+9,a+9
如果 p 的初值为 &a[0],
则,
1) p+i 和 a+i 就是元素 a[i]
的地址,即指向元素 a[i] 。
( p+i=p+i× 2,a+i=a+i× 2)
2) *(p+i) 或 *(a+i) 是 p+i 或
a+i 所指的元素,即 a[i]
例如 *(p+3) 或 *(a+3) 就是元素
a[3]。
p+3,a+3
a[3]
*(p+3) 或 *(a+3)
3) 变址运算符 ? [ ],
在 C语言中方括号 [] 是一个运算符,称为变址运算符。
对于数组元素 a[i], C语言在编译的时候就是通过变址
运算,找到元素 a[i] 对应的地址,变址运算的规则是,
把元素 a[i] 按 a+i 计算出地址,通过 *(a+i),来存
取元素 a[i] 的值。
4)指向数组的指针变量也可以带下标,如 p[i],
与 *(p+i)是等价的。
由以上的叙述,可得到:引用一个数组元素有两种方法。
(1) 下标法:如 a[i], p[i] 形式
(2) 指针法:如 *(a+i) 或 *(p+i)。其中 a是数组名,
p为指向数组元素的指针变量,其初值 p=a。
a
a[0]
a[1]
a[i]
p,a
*(p+0),*(a+0)
*(p+1),*(a+1)
p+1,a+1
p+i,a+i
*(p+i),*(a+i)
a[0]
a[1]
a[2]
a[3]
a[4]
例 数组元素的引用方法
main()
{ int a[5],*pa,i;
for(i=0;i<5;i++)
a[i]=i+1;
pa=a ;
for(i=0;i<5;i++)
printf("*(pa+%d):%d\n",i,*(pa+i));
for(i=0;i<5;i++)
printf("*(a+%d):%d\n",i,*(a+i));
for(i=0;i<5;i++)
printf("pa[%d]:%d\n",i,pa[i]);
for(i=0;i<5;i++)
printf("a[%d]:%d\n",i,a[i]);
}
1
2
3
4
5
pa
运行结果,
*(pa+0),1
*(pa+1),2
*(pa+2),3
*(pa+3),4
*(pa+4),5
运行结果,
*(a+0),1
*(a+1),2
*(a+2),3
*(a+3),4
*(a+4),5
运行结果,
pa[0],1
pa[1],2
pa[2],3
pa[3],4
pa[4],5
运行结果,
a[0],1
a[1],2
a[2],3
a[3],4
a[4],5
例 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 (?)
p[i]就是 *(p+i)也就是
a[i],所以 &a[i]是 a[i]
的地址
例 void main()
{ int a []={5,8,7,6,2,7,3};
int y,*p=&a[1];
y=(*--p)++;
printf(“%d,,y);
printf(“%d”,a[0]);
}
输出,5 6
p
p 5
8
7
6
2
7
3
0
1
2
3
4
5
6
a
例 注意指针变量的运算
6
/* y=(*(--p))++ y=a[0]++ * /
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);
}
例 注意指针的当前值
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
指针变量可以指到 数组后 的内存
单元
p
p
xx
xx
xx
xx
运行情况,
输入, 5 8 7 6 2 7 3
输出,125 118 437 0 12 33 57
3、数组名作函数参数 (地址传递 )
例如:设数组 a[10]={3,7,9,11,0,6,7,5,4,2};
要求:将数组 a中的 n个整数按相反顺序存放
算法:前后对称位置交换
3 7 9 11 0 6 7 5 4 2
i=0 j=9
for(i=0; i<=9/2 ; i++ ) j=9-i
例 将数组 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 j i j i j j i
11 7 6 0 5 9 4 7 2 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;
}
}
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
j i
j
i
例 将数组 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);
}
实参与形参均用指针变量
例 将数组 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);
}
实参用指针变量,形参用数组
数组名作函数参数,实参与形参的对应关系
实参 形参
数组名
指针变量
数组名
指针变量
数组名
数组名
指针变量
指针变量
4、指针变量与一维数组的关系
若有,int *p 与 int a[10]
1) 数组名 a是指针(地址),常量
2) p=a; p+i 是 a[i]的地址
3) 数组元素的表示方法,下标法 和 指针法,即若 p=a,则
p[i] ? a[i] ? *(p+i) ? *(a+i)
4) 形参数组 实质上是 指针变量,即 int r[ ] ? int *r
p+i,a+i a[i] *(p+i),*(a+i)
5) 在定义指针变量(不是形参)时,不能 把 int *p 写成 int p[];
6) 系统只给 p分配能保存一个指针值的内存区 (一般 2字节);而
给 a 分配 2*10字节的内存区
二,指针与二维数组
?二维数组的地址
首先把二维数组理解为“数组的数组”,
比如,int a[3][4];
则 a 是数组名,a 是包含三个元素的一维数组,三个元
素分别是,a[0],a[1],a[2],
把元素 a[0],a[1],a[2] 分别看成数组名,
则 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[2]包括,a[2][0],a[2][1],a[2][2],a[2][3]
对于二维数组,
( 1) a是 数组名,
包含三个元素
a[0],a[1],a[2]
( 2) 每个元素 a[i]
又是一个一维
数组,包含 4个
元素
a
a+1
a+2
*(*(a+0)+1)
*(a[0]+1) int a[3][4];
a[0]
a[1]
a[2]
2000
2008
2016
2000
2002
2008
2010
2016
2018
a[0][0]
a[0][1]
a[1][0]
a[1][1]
a[2][0]
a[2][1]
a[0][2]
a[0][3]
a[1][2]
a[1][3]
a[2][2]
a[2][3]
行指针与列指针
a[0]+1
a[1]+1
a[2]+1
*(a+0)+1
*(a+1)+1
*(a+2)+1
?对二维数组 int a[3][4],有
?a-----二维数组的首地址,即第 0行的首地址
?a+i-----第 i行 的首地址
?a[i] ? *(a+i)------第 i行第 0列 的元素地址
?a[i]+j ? *(a+i)+j -----第 i行第 j列 的元素地址
?*(a[i]+j) ? *(*(a+i)+j) ? a[i][j]
?a+i=&a[i]=a[i]=*(a+i) =&a[i][0],
值相等,含义不同
?a+i ? &a[i],表示第 i行首地址,指向行
?a[i] ? *(a+i) ? &a[i][0],表示第 i行第 0
列元素地址,指向列
int a[3][4];
a[0]
a[1]
a[2]
2000
2008
2016
2000
2002
2008
2010
2016
2018
a[0][0]
a[0][1]
a[1][0]
a[1][1]
a[2][0]
a[2][1]
a[0][2]
a[0][3]
a[1][2]
a[1][3]
a[2][2]
a[2][3]
a
a+1
a+2
int a[3][4];
a[0][0]
a[0][1]
a[1][0]
a[1][1]
a[2][0]
a[2][1]
a[0][2]
a[0][3]
a[1][2]
a[1][3]
a[2][2]
a[2][3]
二维数组元素表示形式,
( 1) a[1][2]
( 2) *(a[1]+2)
( 3) *(*(a+1)+2)
( 4) *(&a[0][0]+1*4+2)
地址表示,
(1) a+1
(2) &a[1][0]
(3) a[1]
(4) *(a+1)
(5)(int *) (a+1)
行指针
列指针
地址表示,
(1) &a[1][2]
(2) a[1]+2
(3) *(a+1)+2
(4)&a[0][0]+1*4+2
表示形式 含义 地址
a 二维数组名,
数组首地址
a[0],*(a+0),
*a
第 0行第 0列
元素地址
a+1 第 1行首地址
a[1],*(a+1)
第 1行第 0列
元素地址
a[1]+2,*(a+1)+2,
&a[1][2]
第 1行第 2列
元素地址
*(a[1]+2),
*(*(a+1)+2),
a[1][2]
第 1行第 2列
元素值
2000
2000
2008
2008
2012
13
int a[3][4];
a[0]
a[1]
a[2]
2000
2008
2016
2000
2002
2008
2012
2016
2018
a[0][0]
a[0][1]
a[1][0]
a[1][1]
a[2][0]
a[2][1]
a[0][2]
a[0][3]
a[1][2]
a[1][3]
a[2][2]
a[2][3]
a
a+1
a+2
?二维数组的指针变量
?指向二维数组元素的指针变量
例 指向二维数组元素的指针变量
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("%4d ",*p);
}
}
p=*a;
p=&a[0][0];
p=*(a+0);
p=a;
int a[3][4];
a[0][0]
a[0][1]
a[1][0]
a[1][1]
a[2][0]
a[2][1]
a[0][2]
a[0][3]
a[1][2]
a[1][3]
a[2][2]
a[2][3]
p
运行结果,
1 3 5 7
9 11 13 15
17 19 21 23
?指向 一维数组的指针变量(定义行指针变量)
?定义形式,数据类型 (*指针变量名 )[一维数组维数 ];
例 int (*p)[4];
( )不能少
int (*p)[4]与 int *p[4]不同
p的值是一维数组的
首地址,p是 行指针
?可让 p指向二维数组某一行
如 int a[3][4],(*p)[4]=a;
int a[3][4];
a[0][0]
a[0][1]
a[1][0]
a[1][1]
a[2][0]
a[2][1]
a[0][2]
a[0][3]
a[1][2]
a[1][3]
a[2][2]
a[2][3]
a
a+1
a+2
p
p+1
p+2
p[0]+1或 *p+1
p[1]+2或 *(p+1)+2
*(*p+1)或 (*p)[1]
*(*(p+1)+2)
一维数组指针变量维数和
二维数组 列数 必须相同
第 i 行、第 j 列元素
a[i][j] 的表示方法
是,*(*(p+i)+j)
例 指向一维数组的 指针变量 举例
main()
{ static int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};
int i,j,(*p)[4];
for(p=a,i=0;i<3;i++,p++)
{ for(j=0;j<4;j++)
printf("%4d",*(*p+j));
printf("\n");
}
}
p=a[0];
p=*a;
p=&a[0][0];
p=&a[0];
int a[3][4];
a[0][0]
a[0][1]
a[1][0]
a[1][1]
a[2][0]
a[2][1]
a[0][2]
a[0][3]
a[1][2]
a[1][3]
a[2][2]
a[2][3]
p
p
p
? p[0][j]
运行结果,
1 3 5 7
9 11 13 15
17 19 21 23
?二维数组的指针作函数参数
?用指向变量的指针变量
?用指向一维数组的指针变量
?用二维数组名
实参 形参
数组名 int x[][4]
行指针变量 int (*q)[4]
数组名 int x[][4]
行指针变量 int (*q)[4]
数组名 a
数组名 a
行指针变量 p1
行指针变量 p1
若 int a[3][4]; int (*p1)[4]=a; int *p2=a[0];
列指针变量 p2 列指针变量 int *q
例 3个学生各学 4门课,计算总平均分,并输出第 n个学生成绩
main()
{ void average(float *p,int n);
void search(float (*p)[4],int n);
float score[3][4]=
{{65,67,79,60},{80,87,90,81},
{90,99,100,98}};
average(*score,12);
search(score,2);
}
void average(float *p,int n)
{ float *p_end,sum=0,aver;
p_end=p+n-1;
for(;p<=p_end;p++)
sum=sum+(*p);
aver=sum/n;
printf("average=%5.2f\n",aver);
}
void search(float (*p)[4],int n)
{ int i;
printf(" No.%d,\n",n);
for(i=0;i<4;i++)
printf("%5.2f ",*(*(p+n)+i));
}
列指针
行指针
函数说明
float p[][4]
65 52 79 60
80 87 90 81
90 99 100 98
p
p
? p[n][i]
例 3个学生各学 4门课,查找一门以上课程不及格学生,
输出其各门课 成绩
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;break;}
if(flag==1)
{ printf("No.%d is fail,his scores are:\n",j+1);
for(i=0;i<4;i++)
printf("%5.1f ",*(*(p+j)+i));
printf("\n");
}
}
}
main()
{ void search(float (*p)[4],int n);
float score[3][4]={{...},{...},{...}};
search(score,3);
}
65 52 79 60
80 87 90 81
90 99 100 98
p
? p[j][i]
?二维数组与一维数组指针变量的关系
如 int a[5][10] 与 int (*p)[10];
?二维数组名是一个指向有 10个元素的一维数组的 指针常量
?p=a+i 使 p指向二维数组的第 i行
?*(*(p+i)+j) ? a[i][j]
?二维数组形参实际上是一维数组指针变量,
即 int x[ ][10] ? int (*x)[10]
?变量定义 (不是形参)时两者不等价
?系统只给 p分配能保存一个指针值的内存区 (一般 2字节);
而给 a分配 2*5*10字节的内存区
作业,258页 10.3 10.9
练习,
1、若已定义 int s[10]; 则在下面表达
式中不表示 s[1]的地址的是 ( )
A,s+1 B,s++ C,&s[0]+1 D.&s[1]
2、若定义 int a[3][4]; int (*p)[4] ; 则下
面是列指针的是( )
A,a B,a+1 C,p++ D,*p
3、若定义 int a[3][4] ; int (*p)[4] ; 则下面是行
指针的是( )
A,*a B,&a[2] C,p[2] D,a[1]
4、若有,int a[3][4] ;
int (*p)[4];
p=a[0]; 那么 p是行指针还是列指针?
5、若有,int a[3][4];
int *p;
p=a; 那么 p是行指针还是列指针?
本次课内容,
一、字符串与指针
二、指向函数的指针
三、返回指针值的函数
四、指针数组和指向指针的指针
一,指针与字符串
主要内容,**用字符型指针保存字符串
**字符型指针做参数
1、字符串表示形式
( 1)用字符数组实现
例 main( )
{ char string[]=“I love China!”;
printf(“%s\n”,string);
printf(“%s\n”,string+7);
}
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
I love China!
China!
( 2)用字符指针实现
例 #include <stdio.h>
main( )
{ char *string=“I love China!”;
printf(“%s\n”,string);
string+=7;
while(*string)
{ putchar(string[0]);
string++;
}
}
I
l
o
v
e
C
h
i
string
n
!
a
\0
字符指针 初始化,把字符串 首地址 赋给 string
? char *string;
string=“I love China!”;
string
*string!=0
I love China!
China!
2、字符串指针作函数参数
例 用函数调用实现字符串复制
( 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);
}
运行结果,
string_a=I am a teacher,
string_b=you are a student,
string_a=I am a teacher,
string_b=I am a teacher,
3、字符指针变量与字符数组
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); (?)
二,指针与函数
1、函数指针:函数在编译时被分配的入口地址这
个入口地址就称为函数的指针,用函数名表示
max
…...
指令 1
指令 2
?函数指针变量赋值,如 p=max;
函数返回值的数据类型 专门存放函数入口地址 可指向返回值类型相同的不同函数
2、指向函数的指针变量
?定义形式,数据类型 (*指针变量名 )();
如 int (*p)();
函数指针变量指向的函数必须有 函数说明
?函数调用形式,c=max(a,b); ? c=(*p)(a,b); ? c=p (a,b);
?对函数指针变量 p?n,p++,p--无意义
( )不能省
int (*p)() 与 int *p()不同
例 用函数指针变量调用函数,比较两个数大小
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);
}
3、用函数指针变量作函数参数
例 用函数指针变量作参数,求最大值、最小值和两数之和
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);
}
三,返回指针值的函数
? 函数定义形式,
类型标识符 *函数名 (参数表 );
例 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
四,指针数组和多级指针
用于处理二维数组或多个字符串
1、指针数组
( 1)定义:数组中的元素为指针变量
( 2)定义形式,数据类型 *数组名 [数组长度说明 ];
例 int *p[4];
指针所指向变量的数据类型 区分 int *p[4]与 int (*p)[4] ( 3)指针数组赋值与初始化 赋值,
main()
{ int b[2][3],*pb[2];
pb[0]=b[0];
pb[1]=b[1];
…….,
}
int *pb[2]
pb[0]
pb[1]
int b[2][3]
1
2
3
2
4
6
初始化,
main()
{ int b[2][3],*pb[ ]={b[0],b[1]};
…….,
}
int *pb[2]
pb[0]
pb[1]
int b[2][3]
1
2
3
2
4
6
( 4)指针数组赋值与初始化
L i s p \0
F o r t r a n \0
B a s i c \0
p[0]
p[1]
p[2]
p[3] 0
赋值,
main()
{ char a[]="Fortran";
char b[]="Lisp";
char c[]="Basic";
char *p[4];
p[0]=a; p[1]=b; p[2]=c; p[3]=NULL;
…….,
}
或,
main()
{ char *p[4];
p[0]= "Fortran";
p[1]= "Lisp";
p[2]= "Basic";
p[3]=NULL;
…….,
}
初始化,
main()
{ char *p[]={"Fortran","Lisp","Basic",NULL};
…….,
}
i s \
F r t r a \
a s i c \
[0]
[1]
[2]
[3]
char name[5][9]={“gain”,“much”,“stronger”,“point”,“bye”};
char *name[5]={“gain”,“much”,“stronger”,“point”,“bye”};
g a i n \0
s t r o n g e r \0
p o i n t \0
m u c h \0
name[0]
name[1]
name[2]
name[3]
name[4] b y e \0
g a i n \0
s t r o n g e r \0
p o i n t \0
m u c h \0
b y e \0
( 5)二维数组与指针数组区别,
二维数组存储空间固定
字符指针数组相当于 可变列长 的二维数组
指针数组元素的作用相当于二维数组的行名
但指针数组中元素是指针变量
二维数组的行名是 地址常量
例 对字符串排序(简单选择排序)
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
j k
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
BASIC k
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
BASIC k
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
printf(char *a[];int n)
{int j;
for(j=0;j<n;j++)
printf(“%s\n”,a[j]);
}
3、多级指针
( 1)定义, 指向指针的指针
( 2)一级指针,指针变量中存放目标变量的地址
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;
例 用二级指针处理字符串
#define NULL 0
void main()
{
char **p;
char *name[]={"hello","good","world","bye",""};
p=name+1;
printf("%o, %s ",*p,*p);
p+=2;
while(**p!=NULL)
printf("%s\n",*p++);
}
name[0]
name[1]
name[2]
name[3]
name[4]
char *name[5]
world
bye
\0
hello
good
name
p
运行结果,
644, good bye
用 *p可输出 地址 (%o或 %x),
也可用它输出 字符串 (%s)
p ?*(p++)
( 3)二级指针与指针数组的关系
int **p 与 int *q[10]
?指针数组名是二级指针 常量
?p=q; p+i 是 q[i]的地址
?指针数组作形参,int *q[ ]与 int **q完全等价;但作为变量定义
两者不同
?系统只给 p分配能保存一个指针值的内存区;而给 q分配 10块内
存区,每块可保存一个指针值
作业,259页 20题
预习实验:指针与函数
(写实验报告 /做完实验写)
下次理论可内容:第 11, 12章
?命令行参数
?命令行:在操作系统状态下,为执行某个程序而键入
的一行字符
?命令行一般形式,命令名 参数 1 参数 2……… 参数 n
main(int argc,char *argv[])
{ ………
}
?命令行参数传递
?带参数的 main函数形式,
C:\TC> copy[.exe] source.c temp.c
有 3个字符串参数的命令行
命令行中参数个数 元素指向命令行参数
中各字符串首地址
形参名任意
命令行 实参 main(形参 )
系统自动调用
main函数时传递
第一个参数, main所在的 可执行文件名
例 输出命令行参数
/*test.c*/
main(int argc,char *argv[])
{ while(argc>1)
{ ++argv;
printf("%s\n",*argv);
--argc;
}
}
main(int argc,char *argv[])
{ while(argc-->0)
printf("%s\n",*argv++);
}
1,编译、链接 test.c,生成可执行文件 test.exe
2,在 DOS状态下运行 (test.exe所在路径下)
例如,C:\TC> test[.exe] hello world!
运行结果,hello
world!
运行结果,test
hello
world!
argv[0]
argv[1]
argv[2]
char *argv[]
world
test
hello
argv
argc=3
定义 含义
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型变量