1. 程式人生 > >C++ 陣列在記憶體中的分配

C++ 陣列在記憶體中的分配

接前一篇的內容,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++記憶體管理還有很多內容,需要進一步加強學習。