1
第九讲 指针
2
指针是 C语言中的一个重要概念。掌握指针的用
法,可使程序简洁、高效、灵活,但并不难学。
为了了解什么是指针,先看一个小故事
地下工作者阿金接到上级指令,要去寻找打开
密电码的密钥,这是一个整数。几经周折,才探知
如下线索,密钥藏在一栋三年前就被贴上封条的小
楼中。一个风雨交加的夜晚,阿金潜入了小楼,房
间很多,不知该进哪一间,正在一筹莫展之际,忽
然走廊上的电话铃声响起。艺高人胆大,阿金毫不
迟疑,抓起听筒,只听一个陌生人说:, 去打开
211房间,那里有线索, 。阿金疾步上楼,打开 211
房间,用电筒一照,只见桌上赫然 6个大字:地址
1000。阿金眼睛一亮,迅速找到 1000房间,取出重
要数据 66,完成了任务。
3
我们画出下图
1000 … 66 …
211 1000
P
说明,
1、数据藏在一个内存地址单元中,地址是 1000。
2、地址 1000又由 P单元所指认,P单元的地址为 211。
3,66的直接地址是 1000; 66的间接地址是 211; 211中
存的是直接地址 1000。
4、称 P为指针变量,1000是指针变量的值,实际上是有
用数据藏在存储器中的地址。
4
指针变量 ——用来存放另一变量地址的变量
变量的指针就是变量的地址。
1、指针的概念
指针 是一种特殊的变量,特殊性表现在类型和值上。
从变量讲,指针也具有变量的三个要素,
? ( 1)变量名,这与一般变量取名相同,由英文字符开始。
? ( 2)指针变量的类型,是指针所指向的变量的类型,而不
是自身的类型。
? ( 3)指针的值是某个变量的内存地址。
5
从上面的概念可知,指针本身类型是 int型,因为任何内存地
址都是整型的。但是指针变量的类型却定义成它所指向的
变量的类型。
2、指针的定义(说明,先说明后引用)
例如,
int *p,*q; // 定义 p,q为指向整数类型变量的指针
float *point; // 定义 point为指向 float型变量的指针
double *pd; // 定义 pd为指向 double型变量的指针
int (*pa)[10]; // 定义 pa为指向 int型数组的指针
int (*pu)(); // 定义 pu为指向 int型函数的指针
int **qq; // 定义 qq为指向 int型指针的指针
还有指向结构、联合的指针,后面再介绍
6
3、指针赋值

int akey; // 定义一个整型变量 akey
int *p,*q; // 定义 p,q为指向整型变量的指针变量
akey=66; // 将 66赋给 akey
p = &akey; //将变量 akey的地址赋给 p,这时见图 1
q = p; // 将 p的值赋给 q,见图 2
66
&a
p a
&p
p 变量的地址
&a
a 变量的地址
图 1 的说明:将 a 变量的地址赋给指针 p,意味着让指针
p 指向 a
图 1
7
66
&a
p a
图 2 的说明:当着执行 q = p; 之后,p 中所存的 a 变量的
地址值,也就被放到 q 变量中,意味着让指
针 q 也指向 a
图 2
&a
q
&p
&q
q =p ;
q = p; // 将 p的值赋给 q,见图 2
8
// 指针 1.c
#include <stdio.h> //预编译命令
void main() //主函数
{ //函数体开始
int a[5]={0,1,2,3,4}; //定义数组,赋初值
int *p1,*p2; //定义指针变量
p1=&a[1]; //赋值给指针变量,让 p1指向 a[1]
p2=&a[2]; //赋值给指针变量,让 p2指向 a[2]
printf("a[1]=%d;a[2]=%d\n",*p1,*p2); //输出 a[1]和 a[2]的值
} //函数体结束
9
说明:见图
0 1 2 3 4
&a[0] &a[1] &a[2] &a[3] &a[4]
&a[1] &a[2]
&p1 &p2
p1 p2
p1和 p2分别指向 a[1],a[2],这里
? & —— 取地址运算符
? * —— 指针运算符(间接访问运算符)
? *p1——间接访问 p1所指向的内存单元,当然是输出 a[1]的值
? *p2——间接访问 p2所指向的内存单元,当然是输出 a[2]的值
10
// 指针 2.c
#include <stdio.h> //预编译命令
void main() //主函数
{ //函数体开始
int akey,b; //定义整型变量
int *p,*q; //定义指针变量
akey=66; //赋值给变量 akey
p=&akey; //赋值给指针变量 p,让 p指向变量 akey
q=&b; //赋值给指针变量 q,让 q指向变量 b
*q=*p; //将 p所指向的 akey的值赋给 q所指向的变量 b
printf("b=%d\n",b); //输出 b的值
printf("*q=%d\n",*q); //输出 b的值
} //函数体结束
4、向指针所指向的内存单元赋值
11
66
66
& ak ey
p
& a ke y
* q= * p
& b
q
&b
12
// 指针 3.c
#include <stdio.h> //预编译命令
void main() //主函数
{ //函数体开始
int a[5]={1,3,5,7,9}; //定义数组,赋初值
int *p; //定义指针变量
int i; //定义整型变量
p=a; //赋值给指针变量,让 p指向 a数组
for(i=0;i<5;i=i+1)
{ //循环体开始
printf("a[%d]=%d\n",i,*p);//输出 a数组元素的值
p=p+1; //指针变量加 1
} //循环体结束
} //函数体结束
5、指针与数组
先看一个程序
13
说明
? (1) p=a; 这里数组名作为数组的起始地址,即 a[0]的地址。
因此 p=a 等效于 p=&a[0];
? (2) p=p+1; 如 p指向 a[0],则 p=p+1之后,p指向 a[1]
? (3) 如果 p=a 等效于 p=&a[0];
则 p=a+4 等效于 p=&a[4];
a[0]
a[1]
a[2]
a[3]
a[4]
*p
*(p+1)
*(p+2)
*(p+3)
*(p+4)
p
p+1
p+2
p
p+1
p+2
等效
14
// 指针 4.c
#include <stdio.h> //预编译命令
void main() //主函数
{ //函数体开始
int a[5]={1,3,5,7,9}; //定义数组,赋初值
int *p; //定义指针变量
int i=0; //定义整型变量,赋初值
for(p=a;p<a+5;p=p+1) //赋值给指针变量,让 p指向 a数组
{ //循环体开始
printf("a[%d]=%d\n",i,*p); //输出 a数组元素的值
i=i+1; //让 i加 1
} //循环体结束
} //函数体结束
做下面的实验
15
// 指针 5.c
#include <stdio.h>
void main()
{
char *p; // 定义指向字符类型的指针变量 p
static char s[] =,abcdefgh”; // 定义字符数组,并赋值
p=s; // 数组名是一个常量指针,
// 它指向该数组首地址
while (*p != ?\0?) // 当 p所指向的数组元素不为 ?\0?时
{
p=p+1; // 让指针加 1
}
printf(“字串长度为 %d\n”,p-s);// 输出字串长
}
数组名是一个常量指针,指向该数组的首地址,例
16
0 1 2 3 4 5 6 7 8
a b c d e f g h \0 s
s p
图中数组的首地址是 s[0]的地址,即 &s[0]。 s可看
作是指向 s[0]的指针。 s是不会动的,是常量指
针。
17
// 指针 6.c
#include <stdio.h> //预编译命令
void main() //主函数
{ //函数体开始
static char shuzi[]=“987654321”; //定义数组,
// 赋初值为数字字符串
char *p=&shuzi[8]; //让指针 p指向 shuzi[8]元素,
// 该处是字符‘ 1?
do // 直到型循环
{ // 循环体开始
putchar(*p); // putchar函数的作用是向终端
// 输出一个字符,该字符由 p指向
p=p-1; // 让 p减 1
} // 循环体结束
while (p>=shuzi); // 当 p>=shuzi时,继续循环
putchar(?\n?); // 换行
}
数组名是一个常量指针,指向该数组的首地址,例
18
0 1 2 3 4 5 6 7 8 9
9 8 7 6 5 4 3 2 1 \0
shuzi p
说明,
1、字符串,数字字符串。
2,p指向 shuzi[8],即指向串中的字符 ?1?。
3、直到型循环,用 putchar函数将 shuzi[8]输出到屏幕;之
后让 p=p-1。
4、在 while中,当 p>=shuzi则继续执行循环体。一旦
p<shuzi 则退出循环。这种做法使输出结果为
123456789
5、在本例中数组名 shuzi是一个常量指针,永远指向 shuzi[0]
的地址上。
思考:如何通过 p和 shuzi求该数字字符串的长度
19
// 指针 7.c
#include <stdio.h> //预编译命令
void main() //主函数
{ //函数体开始
int i;
char *p; //定义指针变量,赋初值
p="computer"; //指针赋初值,指向字苻串
printf("%s\n",p); //输出字苻串
for (i=0;i<8;i=i+1)
{ //循环体开始
putchar(p[i]); //输出第 i个字苻
} //循环体结束
putchar('\n'); //换行
while(*p)
{ //循环体开始
putchar(*p); //输出 p所指向的字符
p=p+1; //指针变量值加 1
} //循环体结束
putchar('\n'); //换行
} //函数体结束
20
上例中有三种不同的输出方式,请你自己分析一下,各有什么特

说明,
1、对字符指针变量赋值的写法
( 1) char *p; ( 2) char *p=“computer”;
p =,computer”;
以上两种都行。可以整体赋值。
2、对字符数组赋初值的写法
( 1) char as[12]=“department”;// 可以。在定义时可以整体赋值
char as[] =“department”;// 可以。在定义时可以整体赋值
( 2) char as[12];
as =,department”; // 不可以!不可以整体赋值
as[12]=“department”; //不可以!不可以整体赋值
21
#include <stdio.h> //预编译命令
void main() //主函数
{ //函数体开始
int i;
char s[] = "abcdef"; //s[ ]为字符数组
char* t = "abcdef"; //t为指向字符数组的指针变量
// p为指针数组变量,p数组元素依次指向
// 字符串 "abcdef","qhijk","lmnop","qrstuv"
char* p[]={"abcdef","ghijk","lmnop","qrstuv"};
//q为指针数组变量,q数组元素依次指向指针数组元素
char** q[]={p,p+2,p+3,p+1}; //p[0],p[2],p[3],p[1]
//r为 3级指针变量,是指向指针数组 q的指针,
//而 q又是指向指针数组 p的指针
char** *r=q;
例题分析
22
//输出 1.1:s的地址
printf("1.1:(printf(\"%%X\\n\",s)\t\t 输出 s的地址 -->
%X\n",s);
//输出 1.2:s的内容
printf("1.2:(printf(\"%%s\\n\",s)\t\t 输出 s的内容 -->
%s\n",s);
//输出 1.3:t指向的地址
printf("1.3:(printf(\"%%X\\n\",t)\t\t 输出 t指向的地址 -->
%X\n",t);
//输出 1.4:t指向的内容
printf("1.4:(printf(\"%%s\\n\",t)\t\t 输出 t指向的内容 -->
%s\n",t);
printf("\n");
23
1.1:(printf("%X\n",s) 输出 s的地址 --> 12FF54
1.2:(printf("%s\n",s) 输出 s的内容 --> abcdef
1.3:(printf("%X\n",t) 输出 t指向的地址 --> 406038
1.4:(printf("%s\n",t) 输出 t指向的内容 --> abcdef
结果
24
//输出 2.1,P[0]指向的字符串
printf("2.1:(printf(\"%%s\\n\",p[0])\t\t输出 P[0]指向的字符串 -
-> %s\n",p[0]);
//输出 2.2,P[1]指向的字符串
printf("2.2:(printf(\"%%s\\n\",p[1])\t\t输出 P[1]指向的字符串 -
-> %s\n",p[1]);
//输出 2.3,P[2]指向的字符串
printf("2.3:(printf(\"%%s\\n\",p[2])\t\t输出 P[2]指向的字符串 -
-> %s\n",p[2]);
//输出 2.4,P[3]指向的字符串
printf("2.4:(printf(\"%%s\\n\",p[3])\t\t输出 P[3]指向的字符串 -
-> %s\n",p[3]);
printf("\n");
25
2.1:(printf("%s\n",p[0]) 输出 P[0]指向的字符串 --> abcdef
2.2:(printf("%s\n",p[1]) 输出 P[1]指向的字符串 --> ghijk
2.3:(printf("%s\n",p[2]) 输出 P[2]指向的字符串 --> lmnop
2.4:(printf("%s\n",p[3]) 输出 P[3]指向的字符串 --> qrstuv
结果
26
//输出 3,P[0]指向的地址
printf("3,(printf(\"%%X\\n\",p[0])\t\t 输出 P[0]指向的地址 -->
%X\n",p[0]);
//输出 4,P[1]指向的地址
printf("4,(printf(\"%%X\\n\",p[1])\t\t 输出 P[1]指向的地址 -->
%X\n",p[1]);
//输出 5,P[2]指向的地址
printf("5,(printf(\"%%X\\n\",p[2])\t\t 输出 P[2]指向的地址 -->
%X\n",p[2]);
//输出 6,P[3]指向的地址
printf("6,(printf(\"%%X\\n\",p[3])\t\t 输出 P[3]指向的地址 -->
%X\n",p[3]);
printf("\n");
27
3,(printf("%X\n",p[0]) 输出 P[0]指向的地址 --> 406048
4,(printf("%X\n",p[1]) 输出 P[1]指向的地址 --> 406058
5,(printf("%X\n",p[2]) 输出 P[2]指向的地址 --> 406068
6,(printf("%X\n",p[3]) 输出 P[3]指向的地址 --> 406078
结果
28
//输出 7,p[0]本身的地址
printf("7,(printf(\"%%X\\n\",&p[0])\t\t输出 p[0]本身的地址 --
> %X\n",&p[0]);
//输出 8,p[1]本身的地址
printf("8,(printf(\"%%X\\n\",&p[1])\t\t输出 p[1]本身的地址 --
> %X\n",&p[1]);
//输出 9,p[2]本身的地址
printf("9,(printf(\"%%X\\n\",&p[2])\t\t输出 p[2]本身的地址 --
> %X\n",&p[2]);
//输出 10,p[3]本身的地址
printf("10:(printf(\"%%X\\n\",&p[3])\t\t输出 p[3]本身的地址 -
-> %X\n",&p[3]);
printf("\n");
29
7,(printf("%X\n",&p[0]) 输出 p[0]本身的地址 --> 12FF70
8,(printf("%X\n",&p[1]) 输出 p[1]本身的地址 --> 12FF74
9,(printf("%X\n",&p[2]) 输出 p[2]本身的地址 --> 12FF78
10:(printf("%X\n",&p[3]) 输出 p[3]本身的地址 --> 12FF7C
结果
30
//输出 11,q[0]本身的地址
printf("11:(printf(\"%%X\\n\",&q[0])\t\t输出 q[0]本身的地址 --
> %X\n",&q[0]);
//输出 12,q[1]本身的地址
printf("12:(printf(\"%%X\\n\",&q[1])\t\t输出 q[1]本身的内容 -
-> %X\n",&q[1]);
//输出 13,q[2]本身的地址
printf("13:(printf(\"%%X\\n\",&q[2])\t\t输出 q[2]本身的地址 -
-> %X\n",&q[2]);
//输出 14,q[3]本身的地址
printf("14:(printf(\"%%X\\n\",&q[3])\t\t输出 q[3]本身的地址 -
-> %X\n",&q[3]);
printf("\n");
31
11:(printf("%X\n",&q[0]) 输出 q[0]本身的地址 --> 12FF60
12:(printf("%X\n",&q[1]) 输出 q[1]本身的地址 --> 12FF64
13:(printf("%X\n",&q[2]) 输出 q[2]本身的地址 --> 12FF68
14:(printf("%X\n",&q[3]) 输出 q[3]本身的地址 --> 12FF6C
结果
32
//输出 15:q[0]指向的地址
printf("15:(printf(\"%%X\\n\",q[0]) \t\t输出 q[0]指向的地址 --
> %X\n",q[0]);
//输出 16:q[1]指向的地址
printf("16:(printf(\"%%X\\n\",q[1]) \t\t输出 q[1]指向的地址 --
> %X\n",q[1]);
//输出 17:q[2]指向的地址
printf("17:(printf(\"%%X\\n\",*q[0])\t\t输出 q[2]指向的地址 --
> %X\n",q[2]);
//输出 18:q[3]指向的地址
printf("18:(printf(\"%%X\\n\",*q[1])\t\t输出 q[3]指向的地址 --
> %X\n",q[3]);
printf("\n");
33
15:(printf("%X\n",q[0]) 输出 q[0]指向的地址 --> 12FF70
16:(printf("%X\n",q[1]) 输出 q[1]指向的地址 --> 12FF78
17:(printf("%X\n",*q[0]) 输出 q[2]指向的地址 --> 12FF7C
18:(printf("%X\n",*q[1]) 输出 q[3]指向的地址 --> 12FF74
结果
34
//输出 19:*q[0]的内容 (地址值 )
printf("19:(printf(\"%%X\\n\",*q[0])\t\t输出 *q[0]的内容 -->
%X\n",*q[0]);
//输出 20:*q[1]的内容 (地址值 )
printf("20:(printf(\"%%X\\n\",*q[1])\t\t输出 *q[1]的内容 -->
%X\n",*q[1]);
//输出 21:*q[2]的内容 (地址值 )
printf("21:(printf(\"%%X\\n\",*q[2])\t\t输出 *q[2]的内容 -->
%X\n",*q[2]);
//输出 22:*q[3]的内容 (地址值 )
printf("22:(printf(\"%%X\\n\",*q[3])\t\t输出 *q[3]的内容 -->
%X\n",*q[3]);
printf("\n");
35
19:(printf("%X\n",*q[0]) 输出 *q[0]的内容 --> 406048
20:(printf("%X\n",*q[1]) 输出 *q[1]的内容 --> 406068
21:(printf("%X\n",*q[2]) 输出 *q[2]的内容 --> 406078
22:(printf("%X\n",*q[3]) 输出 *q[3]的内容 --> 406058
结果
36
//输出 23:*q[0]的内容 (字符串 )
printf("23:(printf(\"%%s\\n\",*q[0])\t\t输出 *q[0]的内容 -->
%s\n",*q[0]);
//输出 24:*q[1]的内容 (字符串 )
printf("24:(printf(\"%%s\\n\",*q[1])\t\t输出 *q[1]的内容 -->
%s\n",*q[1]);
//输出 25:*q[2]的内容 (字符串 )
printf("25:(printf(\"%%s\\n\",*q[2])\t\t输出 *q[2]的内容 -->
%s\n",*q[2]);
//输出 26:*q[3]的内容 (字符串 )
printf("26:(printf(\"%%s\\n\",*q[3])\t\t输出 *q[3]的内容 -->
%s\n",*q[3]);
printf("\n");
37
23:(printf("%s\n",*q[0]) 输出 *q[0]的内容 --> abcdef
24:(printf("%s\n",*q[1]) 输出 *q[1]的内容 --> lmnop
25:(printf("%s\n",*q[2]) 输出 *q[2]的内容 --> qrstuv
26:(printf("%s\n",*q[3]) 输出 *q[3]的内容 --> ghijk
结果
38
//输出 27,r 的内容 (地址值 )
printf("27:(printf(\"%%X\\n\",r )\t\t输出 r 的内容 -->
%X\n",r ) ;
//输出 28:* r 的内容 (地址值 )
printf("28:(printf(\"%%X\\n\",*r )\t\t输出 *r 的内容 -->
%X\n",*r ) ;
//输出 29:**r 的内容 (地址值 )
printf("29:(printf(\"%%X\\n\",**r )\t\t输出 **r 的内容 -->
%X\n",**r ) ;
//输出 30:**r 的内容 (字符串 )
printf("30:(printf(\"%%s\\n\",**r )\t\t输出 **r 的内容 -->
%s\n",**r ) ;
printf("\n");
39
27:(printf("%X\n",r ) 输出 r 的内容 --> 12FF60
28:(printf("%X\n",*r ) 输出 *r 的内容 --> 12FF70
29:(printf("%X\n",**r ) 输出 **r 的内容 --> 406048
30:(printf("%s\n",**r ) 输出 **r 的内容 --> abcdef
结果
40
//输出 31:*r[0]的内容 (字符串 )
printf("31:(printf(\"%%s\\n\",*r[0])\t\t输出 *r[0]的内容 -->
%s\n",*r[0]) ;
//输出 32:*r[1]的内容 (字符串 )
printf("32:(printf(\"%%s\\n\",*r[1])\t\t输出 *r[1]的内容 -->
%s\n",*r[1]) ;
//输出 33:*r[2]的内容 (字符串 )
printf("33:(printf(\"%%s\\n\",*r[2])\t\t输出 *r[2]的内容 -->
%s\n",*r[2]) ;
//输出 34:*r[3]的内容 (字符串 )
printf("34:(printf(\"%%s\\n\",*r[3])\t\t输出 *r[3]的内容 -->
%s\n",*r[3]) ;
printf("\n");
41
31:(printf("%s\n",*r[0]) 输出 *r[0]的内容 --> abcdef
32:(printf("%s\n",*r[1]) 输出 *r[1]的内容 --> lmnop
33:(printf("%s\n",*r[2]) 输出 *r[2]的内容 --> qrstuv
34:(printf("%s\n",*r[3]) 输出 *r[3]的内容 --> ghijk
结果
42
//输出 35:**r+1的内容 (字符串 )
printf("35:(printf(\"%%s\\n\",**r+1)\t\t输出 **r+1的内容 -->
%s\n",**r+1) ;
//输出 36:**r+2的内容 (字符串 )
printf("36:(printf(\"%%s\\n\",**r+2)\t\t输出 **r+2的内容 -->
%s\n",**r+2) ;
//输出 37:*r[2]+3的内容 (字符串 )
printf("37:(printf(\"%%s\\n\",*r[2]+3)\t\t输出 *r[2]+3的内容 --
> %s\n",*r[2]+3) ;
//输出 38:*r[3]+2的内容 (字符串 )
printf("38:(printf(\"%%s\\n\",*r[3]+2)\t\t输出 *r[3]+2的内容 --
> %s\n",*r[3]+2) ;
printf("\n");
}
43
35:(printf("%s\n",**r+1) 输出 **r+1的内容 --> bcdef
36:(printf("%s\n",**r+2) 输出 **r+2的内容 --> cdef
37:(printf("%s\n",*r[2]+3) 输出 *r[2]+3的内容 --> tuv
38:(printf("%s\n",*r[3]+2) 输出 *r[3]+2的内容 --> ijk
结果
44
结 束