接前一篇的內容,C++中數組在內存中也有靜態分配和動態分配的區別。靜態數組建立的方式為:A a[],它在棧上分配空間;動態方式是使用new,malloc在堆上分配。
數組要么在靜態存儲區被創建(如全局數組),要么在棧或堆上被創建。數組名對應着(而不是指向)一塊內存,其地址與容量在生命期內保持不變,只有數組的內容可以改變。看下例:
1 #include<iostream> 2 using namespace std; 3 void test() 4 { 5 char ch[]="hello"; 6 ch[0]='H'; 7 char*p="world"; 8 p[0]='W';//出錯 9 cout<<ch<<endl; 10 cout<<p<<endl; 11 } 12 int main() 13 { 14 test(); 15 return 0; 16 }
程序中用指針指向了一個常量字符串"world",C++常量字符串存在常量存儲區,且不能修改,故會出錯。
數組的在棧上分配,或堆上分配的區別可以看下例:將test和main函數修改為下
1 char* test() 2 { 3 char ch[]="hello";//在棧上 4 /* char* ch= new char[6];//在堆上 5 ch[0]='h'; 6 ch[1]='e'; 7 ch[2]='l'; 8 ch[3]='l'; 9 ch[4]='0'; 10 ch[5]='\0'; 11 */ 12 return ch; 13 } 14 int main() 15 { 16 char*p=test(); 17 cout<<p<<endl; 18 return 0; 19 }
很明顯程序程序編譯時出現:warning C4172: returning address of local variable or temporary。在test調用結束后在棧上分配的數組已經銷毀,p即為野指針指向無效內容。這里把數組名作為l函數返回值。如果換成下面注釋的代碼在堆上分配則沒有問題,注意最后的'\0',字符串的最后是以'\0'來判斷結束的,不然會出界輸出無效內容。這里可以看出C++數組在內存中的存儲形式與上篇內容介紹的一樣。將test改為如下:
1 char* test() 2 { 3 char* ch= new char[6];//在堆上 4 ch[0]='h'; 5 ch[1]='e'; 6 ch[2]='l'; 7 ch[3]='l'; 8 ch[4]='0'; 9 ch[5]='\0'; 10 char c[] = "hello world"; 11 char *p = c; 12 cout<< sizeof(c) << endl; // 12字節 13 cout<< sizeof(ch) << endl; // 4字節 14 cout<< sizeof(p) << endl; // 4字節 15 return ch; 16 }
靜態數組名用sizeof可以知道數組實際所占的內存大小,而指向數組的指針占用空間即為普通指針的大小。當數組作為函數的參數進行傳遞時,該數組自動退化為同類型的指針。
在上一篇關於C++中類在內存中分配的介紹舉例時發現一個問題,當兩個指針指向同一個對象時,發現delete一個指針銷毀該對象后,用另一個指針扔能調用該類的函數,這個是野指針應該有錯啊。看下面的例子:
1 #include<iostream> 2 using namespace std; 3 class A 4 { 5 public: 6 void fun() 7 { 8 cout<<"class of a:"<<endl; 9 } 10 int num; 11 }; 12 A* test() 13 { 14 A*p; 15 A a; 16 a.num=11; 17 p=&a; 18 p->fun(); 19 return p; 20 } 21 int main() 22 { 23 A*s=test(); 24 s->fun(); 25 return 0; 26 }
程序運行結果:
即兩次輸出class of a,一次是在test函數內,一次是s調用。test內的a分配在棧上,函數結束后應該就銷毀了,為什么s還能調用fun。原來類中的成員數據和函數是存放在不同位置的。C++類的方法存放在"程序代碼區",而類中的數據成員(對象數據成員)存放在類的實例對象中,即該成員數據存放在堆或棧中。s指向對象的成員數據已銷毀,而類的方法還在。
若將上面代碼第8行改為:cout<<"class of a:"<<this->num<<endl;程序運行結果為:
即s輸出的數據成員num為無效,因為該對象已釋放。關於C++內存管理還有很多內容,需要進一步加強學習。