在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
较早的编译器是不同意这样做的,所以一些书籍比方以Tc解说的书本都说数组的下标不能是变量。在vc6.0下亦是如此。 只是在一些较新的编译器如dev c++已经支持了,例如以下代码不会报错 #include <stdio.h> #include <stdlib.h> int main() { int a; int i; scanf("%d",&a); int c[a]; for( i =0 ;i<a;i++) scanf("%d",&c[i]); for( i =0 ;i<a;i++) printf("%d",c[i]); } 这个和编译器有关,应该是设计到一些标准规范为题吧,不妨不要这样写~使用动态分配内存是比較把握和通用的 一维: cin>>n; int* a=new int[n]; if ((a) == NULL) {}//确保没有动态分配失败 delete []a; // 正确的使用方法 delete a; // 错误的使用方法 后者相当于delete a[0],漏掉了另外n-1个对象。 二维: 设有m行n列 cin>>m>>n; int **a = new int* [m]; for(int i = 0; i < m; i++) a[i] = new int [n]; PS:就相当于产生了一个二维数组a[m][n]了,可是对于我们平时声明的数组a[m][n],a[i][j]=a[i*n+j],由于是连续的一片内存,而这样动态声明的数组随意的a[k]都是一个int*类型,即一个地址了,所以仅仅能a[i][j]或者*(*(a+i)+j)来訪问数组的元素,而不能a[i*n+j]这样转换着用了 释放内存: for(int i = 0; i < m; ++i) delete []a[i]; delete []a; PS:事实上对于c++,我们全然能够充分利用它自己强大而方便的容器,比方vector,之所以动态声明数组,相比是大小不确定,声明太大了怕浪费空间,而vector就不用指定大小,当存的数据变多,自己主动扩大容量,比方如果vector默认大小是8,当你再往里存第9个元素时,容器自己主动扩容,变为16,16再不够用,扩为32,2倍2倍的增长,这样就依据须要扩容,不会浪费空间,也能够像普通数组那样直接指定vector的大小,总之普通数组能够的它都能够,普通数组没有的它更有; 一维: vector<int> a; a.push_back(k); k为待存入数组的数,使用方法一样,能够a[i]这样直接取数,还有各种自带的方法,使用方便极了 vector<int> a; vector<int> a(5); //指定数组大小是5 vector<int> a(5,3); //数组大小为5,并初始化数组全部元素值为3 二维: cin>>m>>n;//m行n列 vector<vector<int> > a(m, vector<int>(n)); for (i = 0; i < m; i++) for (j = 0; j < n; j++) a[i][j] = i*j; for (i = 0; i < m; i++) for (j = 0; j < n; j++) cout<<a[i][j]<<' '; cout<<endl; 转自;http://hi.baidu.com/zf2650/blog/item/1cac0e12f87e5f095baf53e7.html ------------------------------------------------------------------------------------- 今天归纳总结了一下,希望以后的朋友能够少走些弯路 一 :关于指针和堆的内存分配先来介绍一下指针 : 指针一种类型,理论上来说它包括其它变量的地址,因此有的书上也叫它:地址变量。既然指针是一个类型,是类型就有大小,在达内的server上或者普通的PC机上,都是4个字节大小,里边仅仅是存储了一个变量的地址而已。无论什么类型的指针,char * ,int * ,int (*) ,string * ,float * ,都是说明了本指针所指向的地址空间是什么类型而已,了解了这个基本上全部的问题都好象都变的合理了。在C++中,申请和释放堆中分配的存贮空间,分别使用new和delete的两个运算符来完毕: 指针类型 指针变量名=new 指针类型 (初始化); delete 指针名; 比如:1、 int *p=new int(0); 它与下列代码序列大体等价: 2、int tmp=0, *p=&tmp; 差别:p所指向的变量是由库操作符new()分配的,位于内存的堆区中,而且该对象未命名。 以下是关于new 操作的说明 : 部分引自<<C++面向对象开发>> 1、new运算符返回的是一个指向所分配类型变量(对象)的指针。对所创建的变量或对象,都是通过该指针来间接操作的,而动态创建的对象本身没有名字。 2、一般定义变量和对象时要用标识符命名,称命名对象,而动态的称无名对象(请注意与栈区中的暂时对象的差别,两者全然不同:生命期不同,操作方法不同,暂时变量对程序猿是透明的)。 3、堆区是不会在分配时做自己主动初始化的(包含清零),所以必须用初始化式(initializer)来显式初始化。new表达式的操作序列例如以下:从堆区分配对象,然后用括号里的值初始化该对象。 以下是从堆中申请数组1、申请数组空间:指针变量名=new 类型名[下标表达式]; 注意:“下标表达式”不是常量表达式,即它的值不必在编译时确定,可以在执行时确定。这就是堆的一个很显著的特点,有的时候程序猿本身都不知道要申请可以多少内存的时候,堆就变的格外实用。 2、释放数组空间: delete [ ]指向该数组的指针变量名; 注意:方括号非常重要的,假设delete语句中少了方括号,因编译器觉得该指针是指向数组第一个元素的,会产生回收不彻底的问题(仅仅回收了第一个元素所占空间),我们通常叫它“内存泄露”,加了方括号后就转化为指向数组的指针,回收整个数组。delete [ ]的方括号里不须要填数组元素数,系统自知。即使写了,编译器也忽略。<<Think in c++>>上说过曾经的delete []方括号里是必须加入个数的,后来因为非常easy出错,所以后来的版本号就改进了这个缺陷。 以下是个样例,VC上编译通过 #include<iostream> using namespace std; //#include <iostream.h> //for VC #include <string.h> void main(){ int n; char *p; cout<<"请输入动态数组的元素个数"<<endl; cin>>n; //n在执行时确定,可输入17 p=new char[n]; //申请17个字符(可装8个汉字和一个结束符)的内存空间strcpy(pc,“堆内存的动态分配”);// cout<<p<<endl; delete []p;//释放pc所指向的n个字符的内存空间return ; } 通过指针使堆空间,编程中的几个可能问题1.动态分配失败。 返回一个空指针(NULL),表示发生了异常,堆资源不足,分配失败。 data = new double [m]; //申请空间if ((data ) == 0)…… //或者==NULL
删除一个指针p(delete p;)实际意思是删除了p所指的目标(变量或对象等),释放了它所占的堆空间,而不是删除p本身,释放堆空间后,p成了空悬指针,不能再通过p使用该空间,在又一次给p赋值前,也不能再直接使用p。
new与delete 是配对使用的, delete仅仅能释放堆空间。假设new返回的指针值丢失,则所分配的堆空间无法回收,称内存泄漏,同一空间反复释放也是危急的,由于该空间可能已另分配,而这个时候又去释放的话,会导致一个非常难查出来的执行时错误。所以必须妥善保存new返回的指针,以保证不发生内存泄漏,也必须保证不会反复释放堆内存空间。
无名变量的生命期并不依赖于建立它的作用域,比方在函数中建立的动态对象在函数返回后仍可使用。我们也称堆空间为自由空间(free store)就是这个原因。但必须记住释放该对象所占堆空间,并仅仅能释放一次,在函数内建立,而在函数外释放是一件非常easy失控的事,往往会出错,所以永远不要在函数体内申请空间,让调用者释放,这是一个非常差的做法。你再怎么小心翼翼也可能会带来错误。
(1)指针数组:一个数组里存放的都是同一个类型的指针,通常我们把他叫做指针数组。 比方 int * a[10];它里边放了10个int * 型变量,因为它是一个数组,已经在栈区分配了10个(int * )的空间,也就是32位机上是40个byte,每个空间都能够存放一个int型变量的地址,这个时候你能够为这个数组的每个元素初始化,在,或者单独做个循环去初始化它。 样例:int * a[2]={ new int(3),new int(4) }; //在栈区里声明一个int * 数组,它的每个元素都在堆区里申请了一个无名变量,并初始化他们为3和4,注意此种声明方式具有缺陷,VC下会报错 比如 : int * a[2]={new int[3],new int[3]}; delete a[0]; delet a[10]; 可是我不建议达内的学生这么写,可能会造成歧义,不是好的风格,而且在VC中会报错,应该写成例如以下 : int * a[2]; a[0]= new int[3]; a[1]=new int[3]; delete a[0]; delet a[10]; 这样申请内存的风格感觉比較符合大家的习惯;因为是数组,所以就不能够delete a;编译会出警告.delete a[1]; 注意这里 是一个数组,不能delete [] ;
二 : 函数指针关于函数指针,我想在我们可能须要写个函数,这个函数体内要调用还有一个函数,但是因为项目的进度有限,我们不知道要调用什么样的函数,这个时候可能就须要一个函数指针;int a();这个一个函数的声明; ing (*b)();这是一个函数指针的声明; 让我们来分析一下,左边圆括弧中的星号是函数指针声明的关键。另外两个元素是函数的返回类型(void)和由边圆括弧中的入口參数(本例中參数是空)。注意本例中还没有创建指针变量-仅仅是声明了变量类型。眼下能够用这个变量类型来创建类型定义名及用sizeof表达式获得函数指针的大小: unsigned psize = sizeof (int (*) ()); 获得函数指针的大小 // 为函数指针声明类型定义 typedef int (*PFUNC) (); PFUNC是一个函数指针,它指向的函数没有输入參数,返回int。使用这个类型定义名能够隐藏复杂的函数指针语法,就我本人强烈建议我们大内弟子使用这样的方式来定义; 以下是一个样例,一个简单函数指针的回调(在GNU编译器上通过,在VC上须要改变一个头文件就OK了) #include<iostream> //GNU 编译器 g++ 实现 using namespace std; #define DF(F) int F(){ cout<<"this is in function "<<#F<<endl; return 0; } //声明定义DF(F)替代 int F();函数; DF(a); DF(b); DF(c); DF(d); DF(e); DF(f); DF(g); DF(h); DF(i); //声明定义函数 a b c d e f g h i // int (*pfunc)(); //一个简单函数指针的声明 typedef int(*FUNC)(); //一个函数指针类型的声明 FUNC ff[] = {a,b,c,d,e,f,g,h,i}; //声明一个函数指针数组,并初始化为以上声明的a,b,c,d,e,f,g,h,i函数 FUNC func3(FUNC vv){ //定义函数func3,传入一个函数指针,而且返回一个相同类型的函数指针 vv(); return vv; } int main(){ for(int i=0;i<sizeof(ff)/sizeof (FUNC);i++){ //循环调用函数指针 FUNC r=func3(ff[ i ]); cout<<r()<<endl; //输出返回值,仅仅是返回了0 } return 0; } 到眼下为止,我们仅仅讨论了函数指针及回调而没有去注意ANSI C/C++的编译器规范。很多编译器有几种调用规范。如在Visual C++中,能够在函数类型前加_cdecl,_stdcall或者_pascal来表示其调用规范(默觉得_cdecl)。C++ Builder也支持_fastcall调用规范。调用规范影响编译器产生的给定函数名,參数传递的顺序(从右到左或从左到右),堆栈清理责任(调用者或者被调用者)以及參数传递机制(堆栈,CPU寄存器等)。 好了,先到此为止吧,写这篇文章耗费了基本上快半天的时间了,非常多事情还没有做,等改天有时间再回来整理,全部的源程序都放在openlab3server上我的文件夹下lib/cpp下,大家能够去拿。不知道的登陆openlab3 然后cd ~chengx/lib/cpp就能够看到了。 还有非常复杂的声明可能也是一种挑战 比方<<Think in c++>>里的 int (*(*f4())[10]();的声明,f4是一个返回指针的函数,该指针指向了含有10个函数指针的数组,这些函数返回整形值;不是这个函数有特别之处,而是Bruce Eckel 说的“从右到左的辨认规则”是一种非常好的方法,值得我们去学习,感谢他:) 最后我想应该跟大家说一下,敲代码应该就象JERRY所说的:简单就是美;我们应该遵循一个原则 : KISS (Keep It Simple,Stupid ,尽量保持程序简单 出自 :《Practical C programming》),把自己的程序尽量的简单明了,这是个很很好的习惯。 |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论