1. 什么是指针
按变量地址存取变量值的方式称为直接存取方式,或者直接访问方式。程序经过编译后将变量名转换为变量的地址,对变量值的存取都是通过地址进行。
例如,语句cout << i;的执行是这样的:根据变量名与地址的对应关系,找到变量 i 的地址2000,i 是整型变量在内存中占4个字节,因此从2000开始的4个字节中取出数据(变量的值3),把它输出。输入时如果用“cin>>i;,在执行时,就把从键盘输入的值送到地址为2000开始的整型存储单元中。如果有语句“k=i+j;”,则从2000字节开始的整型变量存储单元中取出 i 的值(值为3),从2004字节开始的变量存储单元中取出 j 的值(值为6),将它们相加后再将其和(9)送到 k 所占用的2008字节开始的整型存储单元中。
可以通过下面语句将 i 的起始地址(2000)存放到 i_pointer中, i_pointer被定义用来存放一个整型变量的地址,编译系统给这个变量分配4个字节。将变量 i 的地址存放到一种专门用来存放地址的变量中,称为间接存取(间接访问)的方式。
i_pointer = &i; //&i 是变量i的存储单元的起始地址
一个变量的地址称为该变量的指针,通过指针能找到所需的变量单元。如果有一个变量是专门用来存放地址(即指针)的,则它称为指针变量。
2. 变量与指针
2.1 定义指针变量
C++规定所有变量在使用前必须先定义,即指定其类型。在编译时按变量类型分配存储空间。在Visual C++中,为每一个指针变量分配4个字节的存储空间。对指针变量必须将它定义为指针类型。下面给出一个具体例子:
int i,j; //定义整型变量i,j
int * pointer_1, *pointer_2; //定义指针变量 pointer_1,pointer_2
**指针变量的基类型就是该指针变量指向的变量的类型。**定义指针变量的一般形式为:
基类型 * 指针变量名;
int * pointer_1; //定义pointer_1为指向int型数据的指针变量
float * pointer_3; //定义pointer_3为指向float型数据的指针变量
char * pointer_4; //定义pointer_4为指向char型数据的指针变量
如何使一个指针变量指向另一个变量呢?只需把被指向的变量的地址赋给指针变量即可。例如:
i_pointer = &i; //将变量 i 的地址存放到指针变量i_pointer中
在定义指针变量时注意:
- 在定义指针变量时必须指定基类型
一个变量的指针包括两个方面的含义,一是以存储单元编号表示的地址,一是它指向的存储单元的数据类型(如int,char,float等),即基类型。指针变量是基本数据类型派生出来的类型,它不能离开基本类型而独立存在。 - 怎么表示指针类型。
比如:指向整型数据的指针类型表示为“ int * ”,读作“指向 int 的指针”或简称“ int 指针”。 - 不能用一个整数给一个指针变量赋值
- 一个指针变量只能指向同一个类型的变量。不能忽而指向一个整型变量,忽而指向一个双精度型变量。指向整型数据的指针变量中只能存放整型数据的地址,而不能存放浮点型或其他类型数据的地址。也就是说,指针变量pointer_1只能用来指向整型数据(例如 i 和 j),而不能指向浮点型变量 a 和 b。
2.2 引用指针变量
&a 为变量 a 的地址,*p 为指针变量 p 所指向的存储单元。
对 & 和 * 运算符的说明:
int a,b;
int * pointer_1, * pointer_2;
a = 100; b = 10;
pointer_1 = &a;
pointer_2 = &*pointer_1; #等价于pointer_2 = &a
b = *&a; #等价于b = *pointer_1 = a
- &pointer_1的含义
& 和 * 两个运算符的优先级别相同,但按自右向左方向结合,因此先进行pointer_1的运算,它就是变量 a ,再执行 & 运算。因此 &*pointer_1 与 &a 相同,即变量 a 的地址。 - &a 的含义
先进行 &a 的运算,得 a 的地址,再进行 * 运算,即 &a 所指向的变量,&a 与 a 等价。
下面给出一个例子:
//例 输入a,b两个整数,按先大后小的顺序输出a,b(用指针变量处理)
int main(){
int *p1, *p2, *p, a, b;
cout << "提示:请输入两个整数" << endl;
cin >> a >> b; //输入两个整数
p1 = &a; //使p1指向a
p2 = &b; //使p2指向b
if (a < b){
p = p1; p1 = p2; p2 = p; //如果a<b,使p1与p2的值互换 p1指向大的值
}
cout << "a=" << a << " b=" << b << endl;
cout << "max=" << *p1 << " min=" << *p2 << endl;
return 0;
}
解题思路:定义两个(int*)型指针变量 p1 和 p2,使它们分别指向 a 和 b 。使 p1 指向 a 和 b 中的较大者,p2 指向较小者。顺序输出 *p1 、 *p2就实现了按先大后小的顺序输出 a 和 b。
这个问题的算法是不交换整型变量的值,而是交换两个指针变量的值。变量 a 和 b 的内容并未交换,但 p1 和 p2 的值改变了。
2.3 用指针作函数参数
函数的参数不仅可以是整型、浮点型、字符型等数据,还可以是指针类型。它的作用是将一个变量的地址传送给被调用函数的形参。
下面给出一个指针作函数参数的例子:
//例 对输入的两个整数按大小顺序输出 今用函数处理 而且用指针类型的数据作函数参数
int main(){
void swap(int *p1, int *p2); //函数声明
int *pointer_1, *pointer_2, a, b;
cin >> a >> b;
pointer_1 = &a;
pointer_2 = &b;
if (a < b) swap(pointer_1, pointer_2);
cout << "max=" << a << " min=" << b << endl;
return 0;
}
void swap(int *p1, int *p2){ //函数swap的作用是将*p1和*p2的值互换
int temp;
temp = *p1; //temp是整型变量,而不是指针变量
*p1 = *p2;
*p2 = temp;
}
本例采取的方法是交换 a 和 b 的值,而 p1 和 p2 的值不变。 虚实结合是采用单向的“值传递”方式,只能从实参向形参传数据,形参值的改变无法回传给实参。 为了使在函数中改变了的变量值能被main函数所用,不能采取要把改变值的变量作为实参的办法,而应该用指针变量作为函数实参。在函数执行过程中使指针变量所指向的变量值发生变化,函数调用结束后,这些变量值的变化依然保留下来,这样就实现了 “通过调用函数使变量的值发生变化,在主调函数中使用这些改变了的值” 的目的。
如果想通过函数调用得到 n 个要改变的值,可以采取下面的步骤:
- 在主调函数中设 n 个变量,用 n 个指针变量指向它们;
- 编写被调用函数,其形参为 n 个指针变量,这些形参指针变量应当与主调函数中的 n 个指针变量具有相同的基类型。
- 在主调函数中将 n 个指针变量作实参,将它们的值(是地址值)传给所调用函数的 n 个形参指针变量,这样,形参指针变量也指向这 n 个变量;
- 通过形参指针变量的指向,改变该 n 个变量的值;
- 在主调函数中就可以使用这些改变了值的变量。
函数的调用可以(而且只可以)得到一个返回值(即函数值),而使用指针变量作函数参数,就可以通过指针变量改变主调函数中变量的值,相当于通过函数调用从被调用的函数中得到多个值。如果不用指针变量是难以做到这一点的。
下面给出一个例子:
//例 输入a,b,c 3个整数,按由大到小的顺序输出
int main(){
void exchange(int *, int *, int *); //对exchange函数的声明
int a, b, c, *p1, *p2, *p3;
cin >> a >> b >> c; //输入3个整数
p1 = &a;
p2 = &b;
p3 = &c;
exchange(p1, p2, p3); //交换p1,p2,p3指向的3个整型变量的值
cout << a << " " << b << " " << c << endl; //按由大到小的顺序输出3个整数
return 0;
}
void exchange(int *q1, int *q2, int *q3){
void swap(int *, int *); //对swap函数的声明
if (*q1 < *q2) swap(q1, q2); //调用 swap,将 q1 与 q2 所指向的变量的值互换
if (*q1 < *q3) swap(q1, q3); //调用 swap,将 q1 与 q3 所指向的变量的值互换
if (*q2 < *q3) swap(q2, q3); //调用 swap,将 q2 与 q3 所指向的变量的值互换
void swap(int *pt1, int *pt2){ //函数swap的作用是将pt1和pt2所指向的变量的值互换
int temp;
temp = *p1; //temp是整型变量,而不是指针变量
*p1 = *p2;
*p2 = temp;
}