9.7 指针简介
从根本上看,指针(pointer)是一个值为内存地址的变量(或数据对象)。指针变量的值是地址。在C语言中,指针有许多用法。这里介绍指针作为函数参数使用,以及为何要这样用。
假设ptr是一个指针,可以编写如下语句:
ptr = &pooh; //把poth的地址赋给ptr
我们可以说ptr“指向”pooh。ptr和&pooh的区别是ptr是变量,&pooh是常量。或者,ptr是可修改的左值,&pooh是右值。还可以把ptr指向别处:
ptr = &bah; //把ptr指向bah,而不是pooh
现在ptr的值就是bah的地址。
要创建指针变量,先要声明指针变量的类型。
9.7.1 间接运算符:*
假设已知ptr指向bah,如下所示:
ptr = &bah;
然后使用间接运算符*(indirection operator)找出存储在bah中的值,该运算符有时也称为解引用运算符(dereferencing operator)。
不要把间接运算和二元乘法运算符(*)混淆,虽然它们使用的符号相同,但语法功能不同。
val = *ptr; //找出ptr指向的值
语句ptr = &bah;和val = *ptr;放在一起相当于下面的语句:
val = bah;
由此可见,使用地址和间接运算符可以间接完成上面这条语句的功能,这也是“间接运算符”的由来。
小结:与指针相关的运算符
间接(或解引用)运算符:*
一般注解:
后跟一个指针名或地址时,*给出存储在指针指向地址上的值。
9.7.2 声明指针
pointer ptr; //不能这样声明指针
不能这样声明指针的原因是因为声明指针变量时必须指定指针所指向变量的类型,因为不同的变量类型占用不同的存储空间,一些指针操作要求知道操作对象的大小。另外,程序必须知道存储在指定地址上的数据类型。long和float可能占用相同的存储空间,但是它们存储数字却大相径庭。
float *pf, *pg; //pf、pg都是指向float类型变量的指针
类型说明符表明了指针所指向对象的类型,星号(*)表明声明的变量是一个指针。
float *psun; //声明指针
psun = &sunmass; //把地址赋给指针
*psun //获取储存在该地址的值
*和指针名之间的空格可有可无。通常,程序员在声明时使用空格,在解引用变量时省略空格。
char *pc;
*pc是char类型。pc本身是什么类型?我们描述它的类型是“指向char类型的指针”。pc的值是一个地址,在大部分系统内部,该地址由一个无符号整数表示。但是,不要把指针认为是整数类型。一些处理整数的操作不能用来处理指针,反之亦然。指针实际上是一个新类型,不是整数类型。因此,如前所述,ANSI C专门为指针提供了%p格式的转换说明。
9.7.3 使用指针在函数间通信
/* swap3.c -- first attempt at a swapping function */
#include <stdio.h>
void interchange(int *u, int *v); /* declare function */
int main(void)
{
int x = 5, y = 10;
printf("Originally x = %d and y = %d.\n", x , y);
interchange( &x, &y ); //把地址发送给函数
printf("Now x = %d and y = %d.\n", x, y);
return 0;
}
void interchange(int *u, int *v) /* define function */
{
int temp;
temp = *u; //temp获得u所指向对象的值
*u = *v;
*v = temp;
}
/* 输出:
*/
interchange函数传递的不是x和y的值,而是它们的地址。因此,应该它们声明为指针。
temp = *u;
不要写成:
temp = u;
函数要交换的是x和y的值,而不是它们的地址。
使用指针和*运算符,该函数可以访问存储在这些位置上的值并改变它们。
如果这种形式的函数调用,传递的是x的值的形式:
function( x );
如果这种形式的函数调用,传递的是x的地址的形式:
function( &x );
第1种形式要求函数定义中的形式参数必须是一个与x的类型相同的变量:
int function( int num )
第2种形式要求函数定义中的形式参数必须是一个指向正确类型的指针:
int function( int *ptr )
如果要计算或处理值,那么使用第1种形式的函数调用;如果要在被调函数中改变主调函数的变量,则使用第2种形式的函数调用。
C++程序员可能认为,既然C和C++都使用指针变量,那么C应该也有引用变量。让他们失望了,C没有引用变量。
变量:名称、地址和值
编写程序时,可以认为变量有两个属性:名称和值(还有其他属性,如类型,暂不讨论)。计算机编译和加载程序时,认为变量有两个属性:地址和值。地址就是变量在计算机内部的名称。
在许多语言中,地址都归计算机管,对程序员隐藏。然而在C中,可以通过&运算符访问地址,通过*运算符获得地址上的值。
简而言之,普通变量把值作为基本量,把地址作为通过&运算符获得的派生量,而指针变量把指针作为基本量,把值作为通过*运算符获得的派生量。
打印地址并不是&运算符的主要用途。更重要的是使用&、*和指针可以操纵地址和地址上的内容。
小结:函数
如果要改变主调函数中的变量,应使用指针作为参数。如果希望把更多的值传回主调函数,必须这么做。
函数签名:
函数的返回类型和形参列表构成了函数签名。因此,函数签名指定了传入函数的值的类型和函数返回值的类型。
9.8 关键概念
把大型程序组织成若干函数非常有用,甚至很关键。要理解函数是如何把信息从一个函数传递到另一个函数,也就是说,要理解函数参数和返回值的工作原理。函数无法直接访问其他函数中的变量。这种限制访问保护了数据的完整性。但是,当确实需要在函数中访问另一个函数的数据时,可以把指针作为函数的参数。