第 六章 指针
地址和指针的概念
?指针是 C中一个重要概念, 也是 C的重要特色,
可以有效地表示复杂的数据结构, 能动态分配
内存, 能直接处理内存地址等 。
?指针的概念比较复杂, 使用很灵活, 不易掌握,
因此要多做练习 。
6-1 指针概述
地址与指针
地址与取地址运算
?C语言中变量、数组、函数等都占用一定的内存空间,
地址是指该内存空间的第一个字节的地址。
?C语言中取地址运算符,&。
指针与指针变量
?指针:就是地址,是变量的地址。
?指针变量:存放变量地址的变量 。
指针的类型与指针的定义
指针所指向的对象的类型即是指针的类型。
指针的定义:只要在变量名前加, *, 即可。
?例,int * p 表示 p是一个指向整型变量的指针。
?注意,* 表示其后的变量是指针,指针变量是 p而非 *p。
?指针变量一般占 2个字节,用来存放一个地址。
?例 L6-0-1 变量、地址、指针、指针的地址。
指针变量的引用
&运算符,取地址运算。
* 运算符,指针运算(指向运算,或间接运算),*p表
示 p所指向的变量。
例 L6-1 &与 *运算符。
i1
10 &i1
p1
i2
20 &i2
p2
i1
10 &i1
p1
i2
20 &i2
p2
例 L6-2 使两个指针变量交换指向。
i1
10 &i1
p1
i2
20 &i2
p2
i1
10 &i1
p1
i2
20 &i2
p2
例 L6-3 交换两个指针变量所指向变量的值。
指向指针的指针
int I,*p1,**p2;
3
i
&i
p1
&p1
p2
例 L6-3-2
多重指针:从理论上讲,可以有多重指针,但一
般极易出错,不提倡使用多重指针。
例 L6-4 多重指针。
习题 5-9 统计单词与空格数。
习题 5-14 字符串转换为大写、小写(数组法)
习题 5-14-2 字符串转换为大写、小写(指针法)
6-2 指针与数组
一维数组的指针表示
a[i]:下标法访问数组元素。
*(a+i):地址法访问数组元素。
( a+i表示:从数组 a的首地
址开始的第 i个元素。)
例 L6-5
A[0]=1
A[1]=3
A[2]=5
A[3]=7
A[4]=9
a
a+1
a+2
a+3
a+4
ffd0
ffd2
ffd4
ffd6
ffd8
注意,
对数组 a,不能用自增(减)进行运算 (a++,a--),
因为数组的首地址是一个常数。
虽然可用多种方法访问数组元素,效率不同,用
指针访问的速度最快,它不要进行转换。
用指针访问数组时 (主要是赋值 ),一定要记住不
能越界,否则可能带来严重后果。
用指针访问数组时,因为总是从指针当前位置开
始,所以要注意指针的当前值。(例 L6-5的最后
一次。)
例 L6-6 输入若干个温度,求平均值。(用数组)
例 L6-6-2 输入若干个温度,求平均值。(用指针)
强调一维数组的两个结论
C中,一维数组名代表该数组的起始地址。
C中,一维数组的任何一个元素的地址,都可用
其数组名加上一个偏移量来表示。
这两个结论可以推广至任意维数组都适用。
二维数组的指针表示法
二维数组 a 可以看成是由以下元素构成的一个一
维数组,
a[0],a[1],a[2],… a[i],…
所以 a[i]既是一个广义一维数组的元素,也是一
个一维数组 a[i]的名字 (一维数组的首地址 ),所
以 a是指向 a[i]起始地址的指针,因 a[i]是一个一
维数组名,即是一个一维数组的首地址,也就是
指针,可见 a是一个二级指针。
二维数组元素及地址的引用:例 L6-7-0
含义 表示形式
第 0行第 1列元素地址 a[0]+1,*a+1,*(a+0)+1,&a[0][1]
第 1行第 2列元素地址 a[1]+2,*(a+1)+2,&a[1][2]
第 1行第 2列元素的值 *(a[1]+2),*(*(a+1)+2),a[1][2]
例 L6-7 指向数组元素的指针变量。
指向一维数组的指针
int (*)p[5],表示 P是指向一个一维数组的指针
(指向该数组的首地址)。 P++表示将指针移向
下一个一维数组(指向其首地址)。所以可用它
处理二维数组,参见例 L6-8。
指针与字符串
字符串是存放在字符数组中的,因此可用指针来
访问字符中的字符。
例 L6-10 用三种方式输出字符串,
1、直接输出字符串数组 string,printf(“%s”,string)
2、直接用指向字符串的指针 p,printf(“%s”,p)
3、用指针逐个字符输出。
直接用指针表示字符串,
char *p=“C Language”;
表示将字符串, C Language”存放在一连续的内存空
间(以 ‘ \0’)结束,并将指针 P指向该空间的首地址。
上面的程序行也可写成两行,
char *p;
p=“C Language”;
例 L6-12-1
指针指向的字符串与数组表示的字符串的区别
虽然,
char a[]=“C Language”;
char *p=“C Language”;
都可表示字符串,但两者概念并不相同,
a[]表示是一个 11个元素的字符数组,将 C Language和
‘ \0’分别存入 11个单元。 a 表示 首地址,是一个常量,不
能改变,如 a++是错误的。
P只表示一个地址,指向 字符串 C Language的首地址。 P
的值的是可以改变的,如,p++表示将指针向后移一个单元。
指针数组
数组元素都是指针的数组称为指针数组,即指针
数组是用来存放一批地址的。主要用来处理字符
串数组,如,
char *name[5]={“Li”,“Zhang”,“Ling”,“Sun”,“Wang”}
它比用二维数组要节约内存空间(参见 P195、
196的两个图)。
例 L6-13在一批字符串中查找指定字符串。
指针数组举例
例 L6-15 用二级指针表示二维数组,对三个字
符串排升序。
例 L6-16 用二级指针表示二维数组,输出数组
元素。
6-3 指针与函数
主要有以下三个方面,
用指针作为函数参数。
函数的返回值是指针。
指向函数的指针。
指针作为函数参数
习题 5-14-2已经进行过介绍。
指针作为函数的参数时,传递的是地址,如果对
地址中的值进行了修改,则修改不是临时的。
比较以下两个例子,
例 L6-18 传递的是地址,且对地址中的值进行了改
变,所以在主程序中的改变是当然的。
例 L6-18-2 传递的是地址,没有对地址中的值进行修
改,所以在主程序的不变也是当然的。
数组指针作为函数的参数
用数组与指针作为函数的参数时,实参与形参可
以有以下四种组合,
实参 形参
数组名 数组名
数组名 指针
指针 数组名
指针 指针
例 L6-21 求一维数组中下标为奇数的元素之和。
指向函数的指针
函数包括一系列的指令,在内存中也占据一片存
储单元,也有一个地址,通过该地址就可找到该
函数,该地址就是该 函数的指针 (函数的入口地
址 )。
函数指针的定义,
类型 ( *指针名)();
给函数指针赋值,
指针名 =函数名;
用函数指针调用函数,
(*指针名 )(实参 )
例 L6-26 用函数指针两次调用不同的函数,分别实现不
同的功能。
main()函数中的参数
C语言中的 main()函数也可以有参数,一般格式
如下,
main(int argc,char *argv[] )
表示命令行
参数的个数
表示命令行
参数数组
main()参数举例
如,C编译后的文件名为 cfile.exe
而执行时用,cfile computer language 则,
argc=3
argv[]={“cfile”,“computer”,“language”};
例 L6-30 输出用户输入的参数。
常用指针类型变量归纳
定义形式 含义
int *p; P为指向整型数据的指针变量
int (*p)[n]; P为指向含 n个元素的一维数组的指针变量
int (*p)(); P为指向函数的指针,该函数带回一个整型值
int *p[n]; P为含 n个元素的指针数组,每个元素指向一
个整型数据。
int *p(); P为指针函数,该函数带回一个指向整型值的
指针。
int **p; P为指向指针的指针,被指向的指针变量是一
个指向整型值的指针。
int (**p)[n]; P为指向指针的指针,被指向的指针变量又指
向一个一维数组。
P++ ( 或 p+=1), 使 p指向下一元素 。
*p++,因 ++与 *同优先级, 结合方向为自右向左, 所以等
价于 *( p++ ),先用后加, 即先得到变量的值 (*p),再使 p
加 1。
*(p++) 与 *(++p)不同,前者是先取 *p值, 后使 p加 1;后
者是 p先加 1,再取 *p值 (下一单元的值 。 )
(*p)++,表示 p所指向的元素值加 1,而不是指针加 1。
如果 P当前指向数组 a的第 i 个元素, 则,
?*(p--) 相当于 a[i--],先对 p进行 *运算, 再使 p自减 。
?*(++p) 相当于 a[++i],先使 p自加, 再作 *运算 。
?*(--p) 相当于 a[--i],先使 p自减, 再作 *运算 。
指针变量的运算小结