我們都知道指針C/C++的一大特色,但其實指針并不是C/C++獨有的,像C#和java等其實也是有指針的,只不過都被語言本身用其他的方式替代和封裝了一般程序員接觸不到,C/C++就不一樣,它是直接將指針暴露給開發者,因為大部分牽涉到指針的都與內存有關,而計算機內存很重要,萬一出什么問題可能系統都會崩潰,下面我們就簡單來看一下程序在運行時指針與內存之間到底是個什么樣的關系:
先看一段代碼:
#include<stdio.h>
#include<string>
#include<iostream>
#include<time.h>
usingnamespacestd;
classpeople
{
public:
people();
~people();
stringName;
intage;
boolsex;
charinfo[1024];
voidrun(){}
voideat(){}
private:
};
people::people()
{
}
people::~people()
{
}
intmain()
{
people*p1=newpeople();
cout<<p1<<endl;
cout<<&p1<<endl;
cout<<sizeof(p1)<<endl;
cout<<sizeof(*p1)<<endl;
system("pause");
return0;
}
運行看結果:
分析
接下來來一一進行分析:
首先people*p1=newpeople();這一句是類的一個實例化,系統會給people實例化一個對象*p并且給它在堆上開辟空間,注意是在堆上,開辟的空間用來存儲對象的數據。數據包括哪些?就是對象的屬性和虛函數指針,但是函數并不存儲在各對象中。因此run()和eat()方法是不存在對象*p指向的內存處的。
cout<<p1<<endl;輸出的是00279360,這是一個地址,是系統給newpeople()對象分配的地址。
cout<<&p1<<endl;輸出的是0012FD90,這也是地址,但這是指針變量p本身的地址。
cout<<sizeof(p1)<<endl;
cout<<sizeof(*p1)<<endl;
通過這兩個我們就更清晰的認識到了,p1本身只占用4個字節的空間,而他所指向的對象的地址所占的空間就很大,等于類中所有數據類型所占空間之和。
接下來我們在main函數里寫一點邏輯:
圖解
我們來看一下程序運行時間,指針和內存是怎么工作的。我畫一個圖給大家:
程序在運行時,數據主要是存儲在棧、堆、代碼區、全局區。代碼區主要就是存代碼中出現的一些字符常量、方法等,比如這里代碼中給對象的Name屬性賦的值“xiaoli”之類的都是存在此處,然后我們通過new出來的對象,都是由堆通過計算好類中各屬性所需空間然后開辟出來的。這里p3不是通過new開辟出來的,所以他是存在棧上的并且地址是固定的,是不能更改的,而p1和p2是能更改的。
改變地址
如此,我們三個對象互相賦值后會發生什么呢?
對比代碼和輸出結果我們發現了什么?賦值后p1和p2本身的地址并無改變,但是他所指向的內存都編程p3所在的內存了。下面用圖解給大家看一下:
注意,此處原來的p1和p2指向的內存由于是new出來的我們需要手動釋放它。所以我們在重新賦值之前要將這兩塊內存刪除掉deletep2;deletep1;
改變地址的值
如果我將代碼中的p2=&p3;換成*p2=p3呢?我們看下輸出結果:
造成這種情況的原因,其實這就牽涉到指針的兩種賦值問題:一種是改變指向的地址,一種是改變本身指向地址的值p2=&p3是改變指向地址,*p2=p3是改變指向地址的值。