指针与储物箱的关系
指针类型是C++、Pascal等语言中比较重要的数据类型。在使用上很灵活。在C++中可以使用如下的语句一个int指针类型:
如果要为指针变量赋值,可以使用如下的语句:
int *p;
p = &x;
对于初学者来说,可能理解指针比较困难。实际上,在定义指针变量时,就相当于为该指针变量分配一个32位的内存空间(4个字节长)来保存内存地址(仅限于32位操作系统)。为而指针变量赋的值实际上就是一个变量(可能是简单类型变量,也可能是复杂类型变量)的首地址。对于32位操作系统来说,不管是什么类型的变量,地址都是4位的(占一个int类型的空间)。 对于两个指向同一个地址的指针变量,改变一个指针变量所指向的数据,都么另一个指针变量所指向的数据也将改变,如下面的代码所示:
int *p1, *p2;
p1 = &x; p2 = &x;
*p1 = 12;
printf("%d", *p2);
上面的代码将输出12。
对于指针的概念及用途,我们也可以做一个形象的比喻。假设有两个储物箱A和B。有两个人P1和P2。 在A中放置了很多东西,而B是空的。P1拥有A和B的钥匙,而P2只拥有B的钥匙。并且P1不能直接给P2钥匙。 那么P2该如何取得A中的物品呢?(注意,不能直接把A撬开哦,要用钥匙打开)。
方法吗有如下两个:
1. P1将A和B打开,将A中的物品放在B中。
2. P1只将B打开,将A的钥匙放在B中。
第一种方法是直接将A中的物品放在了B中,这么做的好处是无论A发生的什么事,都不会影响B中的物品。但缺点是太麻烦,而且如果A中物品很多的话,是很浪费时间的。而且B的存储空间要和A的一样多才能存放A中所有的物品。
第二种方法是P1通过B将A的钥匙将给了P2,这种方法的好处是方便,而且B也不需要和A一样大,实际上,只要能放下一把钥匙即可。但缺点是A可能不只一把钥匙,如果其他人使用了A的钥匙打开A,并动了A中的物品,那么会直接影响到P2所取得的物品。
我们可以将A和B看作是内存中的两个存储区域。对于第一种方法来说,实际上相当于如下的代码:
{
int x
int y;
float abc;
} MyStruct;
// 相当于A中的物品
MyStruct A;
// B参数相当于B储物箱
void MyMethod(MyStruct B)
{
}
// 将A中的物品放入B中
MyMethod(A);
从上面的代码可以看出,将A传入MyMethod方法中需要将A中所有的内容复制到MyMethod的方法栈中,这是很耗费内存资源的。但在MyMethod方法中修改B中的内容,并不会影响到A。但如果使用下面的代码,就会是另外一个样子。
void MyMethod(MyStruct *B)
{
}
// 将A的钥匙(指针)放到B中
MyMethod(&A);
上面的方法很节省内存空间,但在MyMethod方法中修改B指向的结构体中变量的值,也同样会影响到A中相应变量的值。
读者在使用指针时,可以将指针相象成储物箱的钥匙。当定义一个指针变量时,就相当于建立一个只用于储放钥匙的储物箱。而我们为这个变量赋值时,只能放钥匙(指针)或相当于钥匙大小的其他物品。一个指针变量可以当成一个int变量来使用,如下面的代码也是正确的:
int *p;
p = (int*)x;
上面的代码将x中的值强行转换成了整型指针,实际上,这个指针的值就是1234。也就是说,x变量的值变是一个内存地址了。
那么指针的指针呢,也就是 int **p;,那么我们再加一个储物箱C吧。B保存了A的钥匙,而C保存了B的钥匙。只要取得了C的钥匙,就可以按图索骥地打开A。
也就是说 ,对于int **p,p中保存了是一个地址,但这个地址指向的内存空间保存了也是一个地址,而这个地址所指向的内存空间保存的才是真正的数据(int类型)。如果是int ***p,那就再加一个储物箱吧。哈哈。
也许有的读者可能会注意到本文前面所说的第一种方法是将A中的物品放到B中,那么A中不就没了,这不就相当于把A变量清空了,哈哈,没错。不过这个比喻只是为了使读者更容易理解指针的含义和优缺点。如果不想把A清空,就把A中的物品想象成可复制的就可以了,如光盘,把A中的光盘复制一份放到B中,那就更麻烦了。哈哈!