欢迎来到天天文库
浏览记录
ID:56880802
大小:31.50 KB
页数:13页
时间:2020-07-19
《彻底了解针数组,数组指针,以及函数指针,以及堆中的分.doc》由会员上传分享,免费在线阅读,更多相关内容在行业资料-天天文库。
彻底了解指针数组,数组指针,以及函数指针,以及堆中的分配规则彻底了解指针数组,数组指针,以及函数指针,以及堆中的分配规则 2004年7月31日星期六 第二次更新,增加了对象的堆内存分配,细化了指针数组和数组指针内存的说明 !昨天给0405班辅导,当他们问我指针数组的问题的时候,由于好久没用,感觉有些陌生了,所以今天归纳总结了一下,希望以后的朋友可以少走些弯路:)一:关于指针和堆的内存分配先来介绍一下指针: 指针一种类型,理论上来说它包含其他变量的地址,因此有的书上也叫它:地址变量。既然指针是一个类型,是类型就有大小,在达内的服务器上或者普通的PC机上,都是4个字节大小,里边只是存储了一个变量的地址而已。不管什么类型的指针,char*,int*,int(*),string*,float*,都是说明了本指针所指向的地址空间是什么类型而已,了解了这个基本上所有的问题都好象都变的合理了。在C++中,申请和释放堆中分配的存贮空间,分别使用new和delete的两个运算符来完成:指针类型 指针变量名=new指针类型(初始化); delete指针名;例如:1、int*p=newint(0); 它与下列代码序列大体等价:2、inttmp=0,*p=&tmp;区别:p所指向的变量是由库操作符new()分配的,位于内存的堆区中,并且该对象未命名。 下面是关于new操作的说明 : 部分引自<> 1、new运算符返回的是一个指向所分配类型变量(对象)的指针。对所创建的变量或对象,都是通过该指针来间接操作的,而动态创建的对象本身没有名字。2、一般定义变量和对象时要用标识符命名,称命名对象,而动态的称无名对象(请注意与栈区中的临时对象的区别,两者完全不同:生命期不同,操作方法不同,临时变量对程序员是透明的)。3、堆区是不会在分配时做自动初始化的(包括清零),所以必须用初始化式(initializer)来显式初始化。new表达式的操作序列如下:从堆区分配对象,然后用括号中的值初始化该对象。下面是从堆中申请数组1、申请数组空间:指针变量名=new类型名[下标表达式];注意:“下标表达式”不是常量表达式,即它的值不必在编译时确定,可以在运行时确定。这就是堆的一个非常显著的特点,有的时候程序员本身都不知道要申请能够多少内存的时候,堆就变的格外有用。2、释放数组空间:delete[]指向该数组的指针变量名;注意:方括号非常重要的,如果delete语句中少了方括号,因编译器认为该指针是指向数组第一个元素的,会产生回收不彻底的问题(只回收了第一个元素所占空间),我们通常叫它“内存泄露”,加了方括号后就转化为指向数组的指针,回收整个数组。delete[]的方括号中不需要填数组元素数,系统自知。即使写了,编译器也忽略。<>上说过以前的delete[]方括号中是必须添加个数的,后来由于很容易出错,所以后来的版本就改进了这个缺陷。下面是个例子,VC上编译通过#includeusingnamespacestd;//#include //forVC#includevoidmain(){intn; char*p;cout<<"请输入动态数组的元素个数"<>n;//n在运行时确定,可输入17p=newchar[n];//申请17个字符(可装8个汉字和一个结束符)的内存空间strcpy(pc,“堆内存的动态分配”);//cout<>n; pc2=newCGoods[n];//动态建立数组,不能初始化,调用n次缺省构造函数 …… deletepc; deletepc1; delete[]pc2; }申请堆空间之后构造函数运行;释放堆空间之前析构函数运行;再次强调:由堆区创建对象数组,只能调用缺省的构造函数,不能调用其他任何构造函数。如果没有缺省的构造函数,则不能创建对象数组。---------------------下面我们再来看一下指针数组和数组指针―――――――――――――如果你想了解指针最好理解以下的公式:(1)int*ptr;//指针所指向的类型是int (2)char*ptr;//指针所指向的的类型是char (3)int**ptr;//指针所指向的的类型是int*(也就是一个int*型指针) (4)int(*ptr)[3];//指针所指向的的类型是int()[3]//二维指针的声明 (1)指针数组:一个数组里存放的都是同一个类型的指针,通常我们把他叫做指针数组。比如int*a[10];它里边放了10个int*型变量,由于它是一个数组,已经在栈区分配了10个(int*)的空间,也就是32位机上是40个byte,每个空间都可以存放一个int型变量的地址,这个时候你可以为这个数组的每一个元素初始化,在,或者单独做个循环去初始化它。例子:int*a[2]={newint(3),newint(4)}; //在栈区里声明一个int*数组,它的每一个元素都在堆区里申请了一个无名变量,并初始化他们为3和4,注意此种声明方式具有缺陷,VC下会报错例如 :int*a[2]={newint[3],newint[3]};deletea[0];deleta[10];但是我不建议达内的学生这么写,可能会造成歧义,不是好的风格,并且在VC中会报错,应该写成如下 :int*a[2];a[0]=newint[3];a[1]=newint[3];deletea[0];deleta[10];这样申请内存的风格感觉比较符合大家的习惯;由于是数组,所以就不可以deletea;编译会出警告.delete a[1];注意这里是一个数组,不能delete[];(2)数组指针 :一个指向一维或者多维数组的指针;int*b=newint[10]; 指向一维数组的指针b;注意,这个时候释放空间一定要delete[],否则会造成内存泄露,b就成为了空悬指针. int(*b2)[10]=newint[10][10];注意,这里的b2指向了一个二维int型数组的首地址.注意:在这里,b2等效于二维数组名,但没有指出其边界,即最高维的元素数量,但是它的最低维数的元素数量必须要指定!就像指向字符的指针,即等效一个字符串,不要把指向字符的指针说成指向字符串的指针。这与数组的嵌套定义相一致。int(*b3)[30][20]; //三级指针――>指向三维数组的指针;int(*b2)[20]; //二级指针;b3=newint[1][20][30];b2=newint[30][20]; 两个数组都是由600个整数组成,前者是只有一个元素的三维数组,每个元素为30行20列的二维数组,而另一个是有30个元素的二维数组,每个元素为20个元素的一维数组。 删除这两个动态数组可用下式:delete[]b3; //删除(释放)三维数组;delete[]b2; //删除(释放)二维数组;再次重申:这里的b2的类型是int(*),这样表示一个指向二维数组的指针。b3表示一个指向(指向二维数组的指针)的指针,也就是三级指针.(3)二级指针的指针看下例:int(**p)[2]=new(int(*)[3])[2]; p[0]=newint[2][2]; p[1]=newint[2][2]; p[2]=newint[2][2]; delete[]p[0]; delete[]p[1]; delete[]p[2]; delete[]p;注意此地方的指针类型为int(*),碰到这种问题就把外边的[2]先去掉,然后回头先把int**p=newint(*)[n]申请出来,然后再把外边的[2]附加上去;p代表了一个指向二级指针的指针,在它申请空间的时候要注意指针的类型,那就是int(*)代表二级指针,而int(**)顾名思义就是代表指向二级指针的指针了。既然是指针要在堆里申请空间,那首先要定义它的范围:(int(*)[n])[2],n个这样的二级指针,其中的每一个二级指针的最低维是2个元素.(因为要确定一个二级指针的话,它的最低维数是必须指定的,上边已经提到)。然后我们又分别为p[0],p[1],p[2]…在堆里分配了空间,尤其要注意的是:在释放内存的时候一定要为p[0],p[1],p[2],单独delete[],否则又会造成内存泄露,在delete[]p的时候一定先deletep[0];deletep[1],然后再把给p申请的空间释放掉delete[]p……这样会防止内存泄露。(3)指针的指针;int**cc=new(int*)[10];声明一个10个元素的数组,数组每个元素都是一个int*指针,每个元素还可以单独申请空间,因为cc的类型是int*型的指针,所以你要在堆里申请的话就要用int*来申请;看下边的例子 (vc&GNU编译器都已经通过); int**a=newint*[2]; //申请两个int*型的空间 a[1]=newint[3]; //为a的第二个元素又申请了3个int型空间,a[1]指向了此空间首地址处 a[0]=newint[4]; ////为a的第一个元素又申请了4个int型空间,a[0]指向了此空间的首地址处 int*b; a[0][0]=0; a[0][1]=1; b=a[0]; delete[]a[0] //一定要先释放a[0],a[1]的空间,否则会造成内存泄露.; delete[]a[1]; delete[]a; b++; cout<<*b< //GNU编译器g++实现usingnamespacestd;/* //vc的实现#include"stdafx.h"#include*/#defineDF(F)intF(){ cout<<"thisisinfunction"<<#F<>里的int(*(*f4())[10]();的声明,f4是一个返回指针的函数,该指针指向了含有10个函数指针的数组,这些函数返回整形值;不是这个函数有特别之处,而是BruceEckel说的“从右到左的辨认规则”是一种很好的方法,值得我们去学习,感谢他:)最后我想应该跟大家说一下,写程序应该就象JERRY所说的:简单就是美;我们应该遵循一个原则:KISS(KeepItSimple,Stupid,尽量保持程序简单出自:《PracticalCprogramming》),把自己的程序尽量的简单明了,这是个非常非常好的习惯。由于写的匆忙,可能其中有遗漏的地方,大家发现希望能指正:) GOODLUCK!
>n; pc2=newCGoods[n];//动态建立数组,不能初始化,调用n次缺省构造函数 …… deletepc; deletepc1; delete[]pc2; }申请堆空间之后构造函数运行;释放堆空间之前析构函数运行;再次强调:由堆区创建对象数组,只能调用缺省的构造函数,不能调用其他任何构造函数。如果没有缺省的构造函数,则不能创建对象数组。---------------------下面我们再来看一下指针数组和数组指针―――――――――――――如果你想了解指针最好理解以下的公式:(1)int*ptr;//指针所指向的类型是int (2)char*ptr;//指针所指向的的类型是char (3)int**ptr;//指针所指向的的类型是int*(也就是一个int*型指针) (4)int(*ptr)[3];//指针所指向的的类型是int()[3]//二维指针的声明 (1)指针数组:一个数组里存放的都是同一个类型的指针,通常我们把他叫做指针数组。比如int*a[10];它里边放了10个int*型变量,由于它是一个数组,已经在栈区分配了10个(int*)的空间,也就是32位机上是40个byte,每个空间都可以存放一个int型变量的地址,这个时候你可以为这个数组的每一个元素初始化,在,或者单独做个循环去初始化它。例子:int*a[2]={newint(3),newint(4)}; //在栈区里声明一个int*数组,它的每一个元素都在堆区里申请了一个无名变量,并初始化他们为3和4,注意此种声明方式具有缺陷,VC下会报错例如 :int*a[2]={newint[3],newint[3]};deletea[0];deleta[10];但是我不建议达内的学生这么写,可能会造成歧义,不是好的风格,并且在VC中会报错,应该写成如下 :int*a[2];a[0]=newint[3];a[1]=newint[3];deletea[0];deleta[10];这样申请内存的风格感觉比较符合大家的习惯;由于是数组,所以就不可以deletea;编译会出警告.delete a[1];注意这里是一个数组,不能delete[];(2)数组指针 :一个指向一维或者多维数组的指针;int*b=newint[10]; 指向一维数组的指针b;注意,这个时候释放空间一定要delete[],否则会造成内存泄露,b就成为了空悬指针. int(*b2)[10]=newint[10][10];注意,这里的b2指向了一个二维int型数组的首地址.注意:在这里,b2等效于二维数组名,但没有指出其边界,即最高维的元素数量,但是它的最低维数的元素数量必须要指定!就像指向字符的指针,即等效一个字符串,不要把指向字符的指针说成指向字符串的指针。这与数组的嵌套定义相一致。int(*b3)[30][20]; //三级指针――>指向三维数组的指针;int(*b2)[20]; //二级指针;b3=newint[1][20][30];b2=newint[30][20]; 两个数组都是由600个整数组成,前者是只有一个元素的三维数组,每个元素为30行20列的二维数组,而另一个是有30个元素的二维数组,每个元素为20个元素的一维数组。 删除这两个动态数组可用下式:delete[]b3; //删除(释放)三维数组;delete[]b2; //删除(释放)二维数组;再次重申:这里的b2的类型是int(*),这样表示一个指向二维数组的指针。b3表示一个指向(指向二维数组的指针)的指针,也就是三级指针.(3)二级指针的指针看下例:int(**p)[2]=new(int(*)[3])[2]; p[0]=newint[2][2]; p[1]=newint[2][2]; p[2]=newint[2][2]; delete[]p[0]; delete[]p[1]; delete[]p[2]; delete[]p;注意此地方的指针类型为int(*),碰到这种问题就把外边的[2]先去掉,然后回头先把int**p=newint(*)[n]申请出来,然后再把外边的[2]附加上去;p代表了一个指向二级指针的指针,在它申请空间的时候要注意指针的类型,那就是int(*)代表二级指针,而int(**)顾名思义就是代表指向二级指针的指针了。既然是指针要在堆里申请空间,那首先要定义它的范围:(int(*)[n])[2],n个这样的二级指针,其中的每一个二级指针的最低维是2个元素.(因为要确定一个二级指针的话,它的最低维数是必须指定的,上边已经提到)。然后我们又分别为p[0],p[1],p[2]…在堆里分配了空间,尤其要注意的是:在释放内存的时候一定要为p[0],p[1],p[2],单独delete[],否则又会造成内存泄露,在delete[]p的时候一定先deletep[0];deletep[1],然后再把给p申请的空间释放掉delete[]p……这样会防止内存泄露。(3)指针的指针;int**cc=new(int*)[10];声明一个10个元素的数组,数组每个元素都是一个int*指针,每个元素还可以单独申请空间,因为cc的类型是int*型的指针,所以你要在堆里申请的话就要用int*来申请;看下边的例子 (vc&GNU编译器都已经通过); int**a=newint*[2]; //申请两个int*型的空间 a[1]=newint[3]; //为a的第二个元素又申请了3个int型空间,a[1]指向了此空间首地址处 a[0]=newint[4]; ////为a的第一个元素又申请了4个int型空间,a[0]指向了此空间的首地址处 int*b; a[0][0]=0; a[0][1]=1; b=a[0]; delete[]a[0] //一定要先释放a[0],a[1]的空间,否则会造成内存泄露.; delete[]a[1]; delete[]a; b++; cout<<*b< //GNU编译器g++实现usingnamespacestd;/* //vc的实现#include"stdafx.h"#include*/#defineDF(F)intF(){ cout<<"thisisinfunction"<<#F<>里的int(*(*f4())[10]();的声明,f4是一个返回指针的函数,该指针指向了含有10个函数指针的数组,这些函数返回整形值;不是这个函数有特别之处,而是BruceEckel说的“从右到左的辨认规则”是一种很好的方法,值得我们去学习,感谢他:)最后我想应该跟大家说一下,写程序应该就象JERRY所说的:简单就是美;我们应该遵循一个原则:KISS(KeepItSimple,Stupid,尽量保持程序简单出自:《PracticalCprogramming》),把自己的程序尽量的简单明了,这是个非常非常好的习惯。由于写的匆忙,可能其中有遗漏的地方,大家发现希望能指正:) GOODLUCK!
此文档下载收益归作者所有
举报原因
联系方式
详细说明
内容无法转码请点击此处