重读了遍高质量C++又唤起了对指针的一些思考,记录如下。
关于指针:指针存储的是内存地址,而普通变量存储的是值,但是为什么值传递就不行,而指针传递就可以,但是究竟是什么本质的不同导致值传递和指针传递的效果不一样呢?
先来看两个链条。
指针变量名字---内存地址---内存存储值
普通变量名字---内存存储值
上面分别是指针变量和普通变量跟值之间的关系。值传递和指针传递之所以效果不一样,根本上就是由于上面这两条链的不同。举例子,比如
Int a=3
要更新这个变量,用值传递函数是这样的,需要return,才能完成值的返回。
int update(int a){
a=4
return a
}
用指针传递是这样---------------------------------------------------------------
Int *p=&a
Int update(p){
*p=4
}
用指针传递不需要return,我们就可以修改值了,有种隔山打牛的感觉。
但是究竟为什么为什么呢?
不论是值传递,还是指针传递,其实归根的目的都是为了实现对变量a的重新赋值。值传递之所以不可行,是因为不同可行域的隔离特性。值传递过程中,可行域之间唯一的通信的方式就是抛值过来,然后抛值过去,而可行域之间的抛值只是通信,并没有进行赋值操作,只有同一个可行域内才可以进行赋值操作。比如下面这个。
Int a=3
a=update(a)
从作用域的角度上int a=3是在一个作用域, intb给的是另一个作用域。而update(a)根本上只是将“一个值”扔到到另一作用域了,并没有将“变量名a对应值3”这个对应关系传递。因此,即使抛过去的3这个值再怎么变换,如果没有return这个操作,将值重新抛回来,a所在的可行域根本无法完成赋值这个过程,也就无法更新原来“变量名a—3”的这层映射关系。
那么为什么指针传递却可以完成呢?
关键的关键在于指针包含的是内存地址,是一个全局唯一的常量,全局常量的优势就是可以无视不同可行域之间的隔离,直接通过全局变量进行赋值,完成映射关系更新操作。
Int *p=&a
update(){
*p=4
}
其实从“传递上”来看,指针传递和值传递根本上是一样的,都是值传递,只不过一个值是普通数值,一个值是一串地址数值,没有区别。核心的在于普通数值是随处可见的,即使他告诉你有个别墅是你的,你也找不到在哪里。而只要你有了别墅的地址,这个全局唯一
总之,如果你想通过外包方式修改一个值a,你实质要做的就是给a重新赋值,就是让a等于一个新的值。
那么我负责任的告诉你:赋值只能在同一可行域才可以完成。
要么你让他们帮你把计算后的值return回来,让新的值回到自己的可行域。不然不同的可行域,值丢过去了,根本不会回来,也就无法完成赋值。
要么你就告诉他这个值的地址,也就是这个以全局为可行域的东西,通过它无视可行域的存在,指哪打哪,完成全局范围的赋值。
要么返回值,要么给全局地址,否则什么也不做让身处不同可行域的东西完成通信,简直百日做梦。
一个更加复杂的例子是这个
void GetMemory(char* p, int length) { p = (char *)malloc(sizeof(char) * num); } |
void Test(void) { char *str = NULL; GetMemory(str, 100); // str 仍然为 NULL strcpy(str, "hello"); // 运行错误 } |
猛一看,我们明明已经用了指针了,为什么还是不行,额,看多了就自然会说要用指针的指针或者加上return语句。但是如果一句话简单的说明原因呢?
这个问题其实和上面的问题实质是一样的。
从上面我们知道要让函数完成赋值(假定这个值是S)给变量的工作的话:
要么让函数计算完值value后,return值value回来,给我们的变量a。
要么把我们变量a的地址完全给函数,让他们自行搞定。
我们首先明确自己的目的是什么?就是赋值,就是给一个指针变量赋予它新的值。变量就是指针变量,值就是地址。
比方说我们指针变量是P,而地址值是0X0088,套回上面的公式,我们要做的是。
要么让函数计算完0x0088后返回来。也就是return一个地址。
要么告诉函数我们指针变量P的地址完全给函数,也就是给他们指针变量的地址。
因此,我们的出来,如果要实现“变量=值”
要么传值进去(就是传p进去),然后return
要么传变量的地址进去,也就是传&p( 其实就是传char **p这种指针的指针进去),不用return。
归根无论地址还是普通值,都是值。无论指针变量还是普通变量,都是包含值的变量。
void GetMemory(char* p, int length) { p = (char *)malloc(sizeof(char) * num); //方法一:return p } //方法二 void GetMemory(char** p, int length) { *p = (char *)malloc(sizeof(char) * num); //方法一:return p } |
void Test(void) { char *str = NULL; GetMemory(str, 100); // str 仍然为 NULL strcpy(str, "hello"); // 运行错误 } |
最后我又从自己退了的角度想了想,对指针的指针,指针,普通变量,值这些做了一些思考,也许和实际的物理机制不太一样,请不要见怪。
通过*p这个操作,可以完成蓝线的瞬间转换过程,也就是说*p在场和a在场的效果是完全一样的。比如
Int a=3
int *p=&a
那么*p=4这个过程其实就是瞬间将指针名p变身为a的过程。