(三)知识准备
“编号”是人们常用的方法。比如超市的存包柜有箱子号、多媒体厅座位有座位号、楼房房间有房间号、书本每页有一个页码编号……通过编号可以准确找到位置。
计算机内存可以有很多字节,比如STC89C52单片机有512字节。人们使用为事物编号的方法为计算机内存的每个字节编号,以便管理内存中的字节。把第一个字节编为0号,第二个字节遍为1号,……,最后一个字节编为511号,如图8-1所示。
图8-1 计算机内存中的字节也有编号
计算机内存中的每个字节有一个编号,这就是地址,相当于教室的房间号。通过地址能够找到所需的变量单元,即地址指向该变量单元。因此,将地址形象化地称为“指针”。
那么什么是指针呢?指针就是地址,地址就是编号,就是内存中字节的编号。
变量位于内存中,例如定义变量
则变量a要占用内存中的两个字节(在Keil 4环境中)。
变量a要占用的字节由计算机分配,在不同的计算机上运行程序,或在同一计算机的不同时刻运行程序,变量被分配的位置也不相同。然而,位置是可以假设的,假设变量a占据了内存中编号为100和101的两个字节,则这两个字节就被标记名称为“a”,如图8-1所示。
用变量a保存一个整数,就是用这两个字节保存一个整数。
例如,执行语句
则5被保存到这两个字节中(转换为二进制形式),如图8-2所示。
如果学生处位于办公楼的202,称为学生处的地址是202。如果学生处比较大,占用了202、203两间房间,习惯上仍称学生处的地址为202,即取第一间房间的编号为地址。对于变量a,它占用了编号为100和101的两个字节,则变量a的地址为100,即取它的第一个字节的编号作为变量的地址。注意:变量a的地址为100,而变量a不只占了编号为100的这一字节。
在分析程序时,可以采用如图8-2所示的方式,在变量空间的左下角写出变量的地址就可以了。
图8-2 带地址的变量空间表示
小贴士
“变量的地址”和“变量的值”是两个不同的概念:
变量的地址:变量位于内存中的“门牌号”,即编号,在程序运行过程中,地址永久不变。写在变量的空间外部左下角。
变量的值:变量空间中所保持的数据内容,在程序运行期间,变量的值是可以改变的。写在变量空间内。
有了变量的地址后,访问变量有两种方式:
◆ 通过变量名。
◆ 通过变量的地址。
1.指针变量
在程序中,地址也需要变量来保存,然而地址不能被保存在普通变量中,C语言提供了一种特殊的变量用来存放地址,这种变量称为指针变量,指针变量也可以简称为指针。
什么是指针?“指针”应该包含两个意思:一是地址,二是指针变量。
2.定义指针变量
例如,定义整型变量a,并赋初值5:
定义指针变量,只需在变量名前加*号:
定义指针变量的一般形式为:
*号是一个标志,有了*号才表示所定义的是指针变量,才能保存地址;否则,p就是一个普通的int型变量,只能保存普通的int型数据,不能保存地址。
注意:指针变量的定义形式:
① 变量名是p,而不是*p。
② 变量p的类型是int *,而不是int。
int *类型表示指针变量p所指向的数据的类型是int型,也就是p要保存一个地址,而这个地址必须是一个int型数据的地址。
指向整型数据的指针类型表示为int *,读作指向int的指针,简称为int指针。
则
要想保存double型变量的地址,要定义double型指针:
小贴士
C语言是很讲究的,不仅用专用的指针变量保存地址,而且对于不同类型的数据,还专用不同类型的指针变量。
3.为指针变量赋值
为指针变量赋值,有两种方式:① 通过赋值语句的方式;② 在变量定义时赋初值。
(1) 通过赋值语句为指针变量赋值
在为指针变量赋值之前,指针变量的值是不确定的,也就是随机数,即随机的地址。千万不要使用未赋值的指针变量中保存的随机地址,否则,可能会导致整个系统崩溃。
(2) 定义指针变量时赋初值
注意:指针变量的初值必须是一个地址。
在这种赋值中,其含义仍然是p = &a;,而不是 * p = &a;。
*是与int结合的,变量的类型为int *,变量名是p,而不是* p。
(3) 指针变量之间互相赋值
所赋的值是其中保存的地址。赋值要求两个指针变量的基类型必须相同。
(4) 指针变量中只能存放地址,不能将一个整常数直接赋给一个指针变量
例如:
系统无法分辨100是地址,从形式上看,100是整型常数,只能赋给整型变量。
但是,允许把数值0赋给指针变量,仅此特例。
系统规定,如果一个指针变量里保存的地址为0,则说明这个指针变量不指向任何内容,称为空指针。
4.两个运算符与引用指针变量
◆ &,取地址运算符,获取变量的地址,写作 &变量名。&a是变量a的地址。
◆ *,*是指针运算符,又称“间接访问”运算符。写作*指针变量名。例如,*p表示指针变量p指向的对象,即以p为地址的内存单元的内容。
在引用指针变量时,可能有3种情况:
(1) 给指针变量赋值
例如:
指针变量p的值是变量a的地址,p指向a。
(2) 引用指针变量指向的变量
程序运行结果如图8-3所示。
图8-3 运行结果
(3) 引用指针变量的值
注意:
& 和 *都是单目运算符,结合方向为“自右向左”。 & 和 *互为逆运算,即一个&和一个*可以相互抵消。
例如,假设有变量a和指针变量p,并且p = &a;,则(用表示等价于):
(1)&*p p
&a
指针运算*、取地址运算&相互抵消,所以&*p 等价于p。
(2)*&a a
取地址运算&、指针运算*相互抵消,所以*&a 等价于a。
(3)&*&*p p
指针运算*、取地址运算&、指针运算*、取地址运算&相互抵消,所以&*&*p 等价于 p。
(4)*&*&*pa
指针运算*、取地址运算&、指针运算*、取地址运算&相互抵消,所以*&*&*p 等价于 *p,而p = &a,所以*&*&*p 等价于a。
这是C语言中典型的一符号多用现象。
*在C语言中的用法有:
① 定义指针变量时,*是一个标志,标志所定义的是指针变量。
例如
② 取指针变量所指向的内容,或改写所指向的内容。
例如,*p = 2;,*q = a;(注意:*前面没有int等类型说明符,*前无内容,*后有一个常量、变量、表达式等)
③ 在算术表达式中,*是乘法运算符。
例如,a*b(注意:*前后各有一个量,可以是常量、变量、表达式等)
&号在C语言中的用法有:
① 取地址运算符。
例如,p = &a;(注意:仅仅在&号右边有一个变量)
② 按位与运算符。
例如,c = a&b;(注意:&号前后都有量,可以是变量或者整常数等)