首页 > 代码库 > C++数组解析
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=newint*[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]=newint[10][10];//数组指针,a就是一个指针,它指向一个数组。
int *p[10];//指针数组,其实应该这么写:int* p[10];这样看就是一个一维数组,数组的每个元素都是一个指针。p是数组名。而不是指针,所以不能delete p;
for(inti=0;i<10;i++)
p[i]=newint[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”,而会在栈区存放一个指针a,a指向这个常量区的内容。
//也就是说是绝不能去改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 a。delete[] a调用a[0~9]的所有析构。而delete a只调用可一个a[0]的析构
//注意!!这里由于A中没有动态分配内存,所以delete a,这里也不会产生内存泄漏。只是没有调用全部的析构函数而已。
//如果A中new了堆内存,而在析构中释放这些内存,就必须要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[]。
C++数组解析