前言
基础知识的内容真是常看常新,这两天学习C++时看到了一段代码有些犯懵,所以弄明白之后将其记录一下。
问题起因
让我犯懵的主要是下面这段代码:
cpp
#include <iostream>
using namespace std;
void testPTR(int* p){
int a = 12;
p = &a;
}
int main(){
int a = 10;
int* b = &a;
testPTR(b);
cout << a << endl; //10
cout << *b << endl; //10
}
运行结果如下:
可以看到,a与 *b 的值都输出为10。
而我一开始认为这样不对,因为值传递嘛,b指针变量传入testPTR函数之后,应该相当于testPTR函数拷贝了一份b指针变量的内容,也就是testPTR函数内的指针p应该与main函数中的指针b指向同一个地方,那么在testPTR函数中对指针p指向进行修改以后,b指针也应该同时被修改指向啊?
那么在testPTR函数执行完毕之后其内存资源应该被清空,包括指针所指向的空间资源,那为什么main函数中的指针b还能正常输出呢?(先别骂先别骂,知道是很简单的问题,但是今天确实脑子抽了,反应了一会儿才想明白-_-||)
前置知识概述:指针基础
在正式分析刚刚的问题之前,我们需要明白一件非常重要的事情,即到底什么是指针:
1、指针其实就是地址,地址也就是指针。
2、因为指针本身也是一块内存区域,所以指针本身也有地址。
3、指针型变量和我们平常说的 int 型变量、char 型变量什么的是一个意思,只不过指针变量存储的内容是地址值,而如 int 型变量存储的则是 int 类型的数值。
4、所谓变量名,也就是声明了一个变量的动作内涵其实就是给一块内存地址空间取一个别名。
在上面的基础上,举个例子,从代码形式上理解:
cpp
//a 就是一个整形变量,其值是存储了一个字面值 10
int a = 10;
//因此直接输出 a ,输出的就是其所存值的内容
cout << a << endl; //10
//输出a的地址,因为a是一块内存地址空间的别名,因此其具有地址
cout << &a << endl;//比如为 0x123456
------------------------指针变量与上述整形变量是类似的概念---------------------------------
//b 就是一个指针变量,其值是存储了一个变量 a 的地址值,也就是上述的 0x123456
int* b = &a;
//因此直接输出 b,输出的就是其所存值的内容:a的地址值0x123456
cout << b << endl;//0x123456
//输出b的地址,因为b作为指针变量它也是一块内存地址空间的别名,也具有地址
cout << &b << endl;//如 0x123458
//通过解引用运算符*,我们可以拿到整形指针变量b所存储的地址空间上所存储的值
cout << *b << endl; //10
从图示上就更好理解了:
所谓变量其实就是个一块内存地址空间起一个名字,如上图所示,变量 a 就是地址空间为0x123456的名字,b就是地址空间为0x123458的名字。
而上图中地址空间内的大圆圈意思就是这块地址空间装了什么东西,从图上可以知道,地址空间0x123456内存储的就是字面值10;而地址空间0x123458内存储的就是地址空间0x123456,我们若是想要拿到地址空间0x123458所存储的地址空间值上的那块空间所存储的内容,使用解引用符号*即可:
cpp
cout << *b << endl; //输出10
通过实际的代码实践验证一下:
cpp
#include <iostream>
using namespace std;
int main(){
int a = 10;
cout << "整形变量a的值:" << a << endl;
cout << "整形变量a的地址值:" << &a << endl;
cout << "--------------------" << endl;
int* b = &a;
cout << "整形指针变量b的值为:" << b << endl;
cout << "整形指针变量b的地址值为:" << &b << endl;
cout << "整形指针变量b所存地址上的那块内存区域存储的值为:" << *b << endl;
return 0;
}
运行结果如下:
相信到这里为值,应该指针是很清晰了吧。
问题解决
还记得本文一开始提出的问题吗?原来是我搞混了所谓的值传递的概念(可能是因为太久没写C++代码了,一时犯懵)。
还是一开始让我犯懵的代码,但是不一样的是我们进行了一些地址信息的打印:
cpp
#include <iostream>
using namespace std;
void testPTR(int* p){
cout << "---------------------" <<endl;
//打印指针变量p的值
//这里p的值与main函数中的a的地址相同
//即与指针变量b的值相同
cout << "形参指针变量p的值为:" << p << endl;
cout << "形参指针变量p的的地址值为:" << &p << endl;
int a = 12;
//打印testPTR函数内局部变量a的地址
cout << "testPTR函数内局部变量a的地址为:" << &a << endl;
p = &a;
//修改指针变量p的指向之后
cout << "修改后的指针变量p的值为:" << p << endl;
cout << "修改后的指针变量p的地址值为:" << &p << endl;
cout << "---------------------" <<endl;
}
int main(){
int a = 10;
int* b = &a;
//这打印的是a的地址
cout << "a的地址:" << &a << endl;
//因为b指向了a的地址,所以b作为指针变量其值就是a的地址
cout << "b变量的值:" << b << endl;
cout << "指针变量b地址的值:" << &b << endl;
//将b变量传入testPTR函数
testPTR(b);
cout << "调用test函数之后的b指针变量的值:" << b << endl;
cout << "调用test函数之后的b指针变量的地址值:" << &b << endl;
cout << "打印a的值:" << a << endl; //10
cout << "打印指针变量b所指向的内存区域内的值:" << *b << endl; //10
}
其运行结果如下:
结合第二节中的指针基础的知识,应该能够很清楚的看懂上述代码了吧,之所以让我犯懵的原因是我想当然的认为了既然main函数中的b指针和testPTR中的p指针所指向的是同一个位置,那么p指针被修改指向则b指针也该修改,这显然是不对的。
在函数进行值传递的过程中,以上述代码中的main函数和testPTR函数为例进行说明。
main函数调用testPTR函数时,传入了一个参数:整形指针变量b。而众所周知,程序运行时函数所占的内存空间各自独立,因此testPTR函数在接收该整形指针变量 b 参数时,会自动创建一个新的指针变量 p 来对该值进行一个拷贝接收,此时指针变量p 与指针变量b 所指向的地址为同一个,即变量a 的地址。但指针变量b 与指针变量p已经不是同一个了,这一点我们从上图的运行结果中可以看出二者地址并不相同,但指向的空间是同一个,因此指针变量p 改为指向testPTR函数内部的局部变量a 的地址之后,并不影响main函数中的指针变量b。
总之只要记住,指针变量和整形变量什么的是一样的东西就可以分析清楚了(指针值传递弄不清楚,那普通变量值传递总弄得清楚吧),类比着来并不困难。