C++數組解析

C++數組:常類型數組和類對象數組

 

常類型數組:

1. 數組的定義和初始化:

一維數組:

1>

       //stack--------------------------------------------

       int a[10];

       //cout<<a[0]<<endl;  error 未初始化不能訪問

       int a[10]={1,2}; //a[0]==1,a[1]==2,a[3]==0,a[3~9]==0

       int b[]={1,3};  //length of b is 2.

       //heap----------------------------------------------

       int d=3;

       int *c=new int[d];

       delete c;//  防止內存泄漏

2> 動態數組的含義

       const int i=3;

       int a[i];// OK

       int j=3;

       int b[j];// error

       int *c=new int[j]; // OK

在定義int[]時,編譯器必須知道要在棧中分配多少內存,所以[]內的必須是常數或常量。在定義int *a=new的時候,由於是動態分配內存,所以編譯的時候不實際分配,在運行纔會指定內存分配的大小單元。

3> 求數組的長度通用做法

       int a[]={1,2,3,4,5,6};

       int length=sizeof(a)/sizeof(a[0]);

因爲這裏sizeof(a)==sizeof(int)*n;而sizeof(a[0])==sizeof(int)。所以可以求得數組的長度。

注意不能在其他函數中使用,因爲數組在傳參過程中會退化成指針,使得sizeof(a)==sizeof(int*):

void foo(int a[])

{

       cout<<sizeof(a)/sizeof(a[0])<<endl;

}

int _tmain(int argc, _TCHAR* argv[])

{

       int a[]={1,2,3,4,5,6};

       foo(a);

       system("pause");

       return 0;

}

在window 64bit結果是2。注意如果編譯64bit程序,vs必須調成64bit編譯。Vs默認的是32bit編譯

 

二維數組:

       //stack------------------------------------------------------------

       int a[5][5];

       int b[5][5]={{1,2},{5}};

       int c[][4]={{1,2},{6,9}};

       //int c[][]={{1,2},{6,9}}; error

       //heap--------------------------------------------------------------

       //1

       int m=2;const int n=3;

       int (*a)[n]=new int[m][n];//n必須是常量,m可以是變量

       a[0][0]=1;

       delete[] a;//因爲申請的空間是連續的,所以可以一下子釋放

       //2

       int i;

       int **d=new int*[m];//m可以是變量

       for(i=0;i<m;i++) d[i]=newint[i+1];//之後才能使用d[i][j]

       d[0][0]=2;

 

       for(i=0;i<m;i++) delete[]d[i];//因爲申請的空間不連續,所以要一個一個釋放

       delete[] d;//注意最後別忘了這句,感覺new的數量應該始終和delete一樣。

1> 注意:new和delete的數量應該始終是一樣的。因爲無論是delete還是delete[],它一次都只能釋放一塊連續的內存(對於普通類型來說)。而每次new出來的內存一般都是和上次new出來的內存是不連續的,所以要分別delete掉new出來的內存。

2> New是動態申請數組,後面申請的大小一般都可以是變量。而”=new”之前的,例如上面的int (*a)[n]=new。這裏編譯器需要知道n的大小,所以=new之前的一般必須都是常量。

3> 指針數組和數組指針

      int (*a)[10]=new int[10][10];//數組指針,a就是一個指針,它指向一個數組。

       int *p[10];//指針數組,其實應該這麼寫:int* p[10];這樣看就是一個一維數組,數組的每個元素都是一個指針。p是數組名。而不是指針,所以不能delete p

       for(inti=0;i<10;i++)

              p[i]=new int[10];

       p[0][0]=1;

       for(inti=0;i<10;i++)

              delete p[i];

4> 二維數組的訪問

       int (*a)[10]=new int[10][10];

       a[3][3]=2;

       cout<<(*(a+3))[3]<<endl;

       cout<<*(a[3]+3)<<endl;

       cout<<*(*(a+3)+3)<<endl;//3個其實都是訪問的a[3][3]

解釋一下:首先a可以看作該二維數組的首地址。而編譯器把二維數組a看成一個一維數組,只是這個一維數組的每個元素又是一個一維數組。所以*(a+3)其實是訪問a一維數組的第4個元素(也就是a[3])。因爲a[3]又是一個一維數組,所以*(a[3]+3)就是訪問a[3]這個數組中的第4個元素。即a[3][3]。

標準寫法:*(*(a+i)+j)    ==  a[i][j]

 

2. 數組和指針的關係

1. 數組名只是表示一種數據結構。而指向數組的指針,表示它存放的是數組的地址。

       char *a="123456";//編譯器會在常量區存放“123456”,而會在棧區存放一個指針aa指向這個常量區的內容。

                        //也就是說是絕不能去改a指向的內容的。不可以a[0]=='9'.

       char a[]="123456";//編譯器只是在棧區開闢一個連續的空間,就是數組。每個空間內分別存放'1','2','3','4','5','6','\0'

                      //該空間大小爲7,且這些存放的值是可以去改的。例如a[0]='9';

 

2. 指向數組的指針,是可以自加,自減,重新指向別的內容,可以deletep;

數組名是不可以修改的,有點像常量指針。更不可以delete a;

       char *a="123";

       a++;

       cout<<*a<<endl;

       char b[]="123";

       //b++;   error

 

3. 當數組名作爲實參傳遞時,無論形參的形式怎樣,編譯器都是用指針來接收實參的。

void foo(char a[])

{

       a++;

       cout<<*a<<endl;

}

int _tmain(int argc, _TCHAR* argv[])

{

       char b[]="123";

       foo(b);

       system("pause");

       return 0;

}

也就是說,在函數foo內,a就是一個指針,不再是函數名了。它可以自加,自減。完全就是一個指針了。

所以在函數foo內sizeof(a)是等於一個指針的大小的。

 

3. 數組的存儲格式

多維數組的在內存中的存儲是按照最低維連續的格式來存儲的。(注意和mat的區別,在mat中是按列儲存的)

比如:a[3][3]內存中的存儲是:

a[0][0]  a[1][0]  a[2][0]

a[0][1]  a[1][1]  a[2][1]

a[0][2]  a[1][2]  a[2][2]

 

類對象數組

1. 棧存儲,局部變量數組。

class A{};

class B

{

public: B(int i){}

};

class C

{

public: C(int i, int j){}

};

int _tmain(int argc, _TCHAR* argv[])

{

       Aa[10];//構造函數可以無參的。

       Bb[5]={1,2,3,4,5};//構造函數只有一個參數的,其實是利用了隱式轉換

       Cc[3]={C(0,1),C(1,2),C(2,3)};//構造函數有兩個以上的參數的,對象數組只能這樣定義。

       system("pause");

       return 0;

}

 

2. 堆存儲。

class A{};

class B

{

public: B(int i){}

              void foo(){}

};

int _tmain(int argc, _TCHAR* argv[])

{

       A*a=new A[10];

       delete[] a;//這裏只能是delete[] a,不能用delete adelete[] a調用a[0~9]的所有析構。而delete a只調用可一個a[0]的析構

                  //注意!!這裏由於A中沒有動態分配內存,所以delete a,這裏也不會產生內存泄漏。只是沒有調用全部的析構函數而已。

                    //如果Anew了堆內存,而在析構中釋放這些內存,就必須要delete[] a了。不然就會引發內存泄漏。

       B*b[10];  //有參的構造函數,想要在堆中分配,就要用到指針數組。

       for(inti=0;i<10;i++)

              b[i]=new B(i);

       b[0]->foo();

 

       for(inti=0;i<10;i++)

              delete b[i];//由於分開new的,所以要一個一個delete。否則一定會產生內存泄漏。

       _CrtDumpMemoryLeaks();//檢測內存是否泄漏函數

       system("pause");

       return 0;

}

總結一下:就是說delete和delete[]的本質區別,就是調用了一個析構函數還是調用一組析構函數。

 

引申總結:delete

1. delete和delete[]的本質區別?

衆所周知在普通類型對象中,delete和delete[]是沒有區別的。

                在類對象數組中是有區別的,區別是調用了析構函數的多少。

                delete只調用了一個析構。Delete[]調用了一組析構。

2. 對於數組的釋放,是一下子delete,還是要分開一個一個delete?

delete使用的次數取決於new的個數。

如果數組是連續的,一下子new出來的,只需要delete/delete[]一次即可。

如果數組是不連續的,分開new出來的,則需要一個一個delete/delete[]。

發佈了45 篇原創文章 · 獲贊 32 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章