科学网

 找回密码
  注册

tag 标签: 动态数组

相关帖子

版块 作者 回复/查看 最后发表

没有相关内容

相关日志

一种数组、链表、字典相结合的数据结构
Babituo 2020-3-2 10:10
刚写的一段程序注释,贴出来鼓励一下自己:——通向开放大世界设计的入口已经找到! //一种数组、链表、字典相结合的数据结构, //既有数组索引顺序不变,快速索引的优点, //又有链表快速维护的 优 点,删除中间元素后,其他元素顺序不改变 //想象有一个动态数组,可不断地向数组中加入元素, //每加入一个元素,该元素就很方便获得一个代表其空间位置的序号。 //如果在数组中间位置随意删除一个元素, //那么,以往的数组,其后每个元素的位置就要前移1位,序号要减1。 //因此,元素在数组中的序号,一般不宜作为加入数组中的元素的标识, //因为,序号会因为对其它元素的增减而发生变化。 //如果非要用数组元素的序号作为外部引用的索引(因为数组索引访问较快)。 //那么,当有元素增减维护之后,索引更新的开销(耗时复杂易错)是非常可观的。 // //假若数组元素加入时获得的序号和其空间位置的前后并没有关联, //那么,随意增减数组元素之后,就可以不影响到其他元素的序号。 //但,数组元素序号与空间位置的一致,正是带来数组索引快的原因。 //用这样的序号,就损失了数组按序号索引元素快的优点,也就不是数组下标了。 // //如何做到让元素获得不受维护操作影响的序号,并按此序号索引如同用数组下标一样快? //鲜度链的设计,解决了这个问题,代价是:使用更多的空间。 //一个顺序列表记录所有元素。(排队机) //一个链表记录元素在顺序列表中的位置与序号的关系。(取号机) //当需要新增元素时,由取号机负责分配一个序号给元素,而不是自动排到排队机的末尾。 //当需要删除元素时,由取号机负责回收这个序号,而不是把后面的元素自动前移。 //如果要按序号访问元素,取号机负责将序号转换为顺序队列的位置号,内部用位置号访问元素。 //所以,在取号机中,还需要增加一个字典,来记录序号和位置的对应关系。 //这样: //链表负责维护序号的前后关系,知道:前一序号是多少?后一序号是多少?谁在头?谁在尾? //顺序表负责存储元素,知道:哪个位置放着哪个元素? //字典负责维护序号和位置的关系,知道:哪个序号对应哪个位置? //如此一来: //链表、顺序表,字典封装成了一个新型的动态数组。关键是:位置号和序号分离的设计。 //封装的外观: //加入元素获得一个序号;提供序号,快速获得元素;删除元素,其他元素序号不变。 //这样: //可以动态增减元素;元素的序号可以当作外部索引用;用序号获取元素,依然相当的快。 //3D打印原理: //轮胎已经太多、太杂了,而轮胎设计制造技术已经日新月异, //找到合用的旧轮胎的开销,已经逐渐大于随手建一个“新轮胎”。 //软件的3D打印时代将至! 真正的创新,必须要做到能让人目瞪口呆!
个人分类: 虚构开放世界|2175 次阅读|0 个评论
C++ 中的创建和删除数组(new/delete 和 new[]/delete[])
linqy 2017-4-17 16:33
在 C++ 中,我们也许经常使用需要建立数组和释放内存的问题,在动态分配内存的时候一般有两种方式,一个是malloc和new两种形式。 比如使用malloc的时候,一般形式如下: har *Ptr = NULL; Ptr = (char *)malloc(100 * sizeof(char)); if (NULL == Ptr) { exit (1); } gets(Ptr); free(Ptr); 在使用new的时候,一般的形式如下: 1. 删除单变量地址空间 int *a = new int; delete a; //释放单个int的空间 2. 删除数组空间 int *a = new int ; delete 和 delete 出来的数组有时可以用 delete 释放有时又不行? 如果你对这些问题都有疑问的话,不妨看看我这篇文章。 new 和 delete 到底是什么? 如果找工作的同学看一些面试的书,我相信都会遇到这样的题:sizeof 不是函数,然后举出一堆的理由来证明 sizeof 不是函数。在这里,和 sizeof 类似,new 和 delete 也不是函数,它们都是 C++ 定义的关键字,通过特定的语法可以组成表达式。和 sizeof 不同的是,sizeof 在编译时候就可以确定其返回值,new 和 delete 背后的机制则比较复杂。 继续往下之前,请你想想你认为 new 应该要做些什么?也许你第一反应是,new 不就和 C 语言中的 malloc 函数一样嘛,就用来动态申请空间的。你答对了一半,看看下面语句: string *ps = new string(hello world); 你就可以看出 new 和 malloc 还是有点不同的,malloc 申请完空间之后不会对内存进行必要的初始化,而 new 可以。所以 new expression 背后要做的事情不是你想象的那么简单。在我用实例来解释 new 背后的机制之前,你需要知道 operator new 和 operator delete 是什么玩意。 operator new 和 operator delete 这两个其实是 C++ 语言标准库的库函数,原型分别如下: void *operator new(size_t); //allocate an object void *operator delete(void *); //free an object void *operator new (void *); //free an array 后面两个你可以先不看,后面再介绍。前面两个均是 C++ 标准库函数,你可能会觉得这是函数吗?请不要怀疑,这就是函数!C++ Primer 一书上说这不是重载 new 和 delete 表达式(如 operator= 就是重载 = 操作符),因为 new 和 delete 是不允许重载的。但我还没搞清楚为什么要用 operator new 和 operator delete 来命名,比较费解。我们只要知道它们的意思就可以了,这两个函数和 C 语言中的 malloc 和 free 函数有点像了,都是用来申请和释放内存的,并且 operator new 申请内存之后不对内存进行初始化,直接返回申请内存的指针。 我们可以直接在我们的程序中使用这几个函数。 new 和 delete 背后机制 知道上面两个函数之后,我们用一个实例来解释 new 和 delete 背后的机制: 我们不用简单的 C++ 内置类型来举例,使用复杂一点的类类型,定义一个类 A: class A { public: A(int v) : var(v) { fopen_s(file, test, r); } ~A() { fclose(file); } private: int var; FILE *file; }; 很简单,类 A 中有两个私有成员,有一个构造函数和一个析构函数,构造函数中初始化私有变量 var 以及打开一个文件,析构函数关闭打开的文件。 我们使用 class *pA = new A(10); 来创建一个类的对象,返回其指针 pA。如下图所示 new 背后完成的工作: 简单总结一下: 首先需要调用上面提到的 operator new 标准库函数,传入的参数为 class A 的大小,这里为 8 个字节,至于为什么是 8 个字节,你可以看看《深入 C++ 对象模型》一书,这里不做多解释。这样函数返回的是分配内存的起始地址,这里假设是 0x007da290。 上面分配的内存是未初始化的,也是未类型化的,第二步就在这一块原始的内存上对类对象进行初始化,调用的是相应的构造函数,这里是调用 A:A(10); 这个函数,从图中也可以看到对这块申请的内存进行了初始化,var=10, file 指向打开的文件。 最后一步就是返回新分配并构造好的对象的指针,这里 pA 就指向 0x007da290 这块内存,pA 的类型为类 A 对象的指针。 所有这三步,你都可以通过反汇编找到相应的汇编代码,在这里我就不列出了。 好了,那么 delete 都干了什么呢?还是接着上面的例子,如果这时想释放掉申请的类的对象怎么办?当然我们可以使用下面的语句来完成: delete pA; delete 所做的事情如下图所示: delete 就做了两件事情: 调用 pA 指向对象的析构函数,对打开的文件进行关闭。 通过上面提到的标准库函数 operator delete 来释放该对象的内存,传入函数的参数为 pA 的值,也就是 0x007d290。 好了,解释完了 new 和 delete 背后所做的事情了,是不是觉得也很简单?不就多了一个构造函数和析构函数的调用嘛。 如何申请和释放一个数组? 我们经常要用到动态分配一个数组,也许是这样的: string *psa = new string ; //array of 10 empty strings int *pia = new int ; //array of 10 uninitialized ints 上面在申请一个数组时都用到了 new psa; delete 表达式,注意这地方的 一个对象数组时,需要保存数组的维度,C++ 的做法是在分配数组空间时多分配了 4 个字节的大小,专门保存数组的大小,在 delete ; 时需要做的事情如下: 从这个图中我们可以看到申请时在数组对象的上面还多分配了 4 个字节用来保存数组的大小,但是最终返回的是对象数组的指针,而不是所有分配空间的起始地址。 这样的话,释放就很简单了: delete 函数的参数不是数组对象的指针 pAa,而是 pAa 的值减 4。 为什么 new/delete 、new 要配对使用? 其实说了这么多,还没到我写这篇文章的最原始意图。从上面解释的你应该懂了 new/delete、new 的工作原理了,因为它们之间有差别,所以需要配对使用。但偏偏问题不是这么简单,这也是我遇到的问题,如下这段代码: int *pia = new int ; delete pia; 换成 delete pia; 的话,会出问题吗? 这就涉及到上面一节没提到的问题了。上面我提到了在 new 时就没必要多分配那 4 个字节, delete 用 delete 来释放对象的提前是:对象的类型是内置类型或者是无自定义的析构函数的类类型! 我们看看如果是带有自定义析构函数的类类型,用 new ; delete pAa; 那么 delete pAa; 做了两件事: 调用一次 pAa 指向的对象的析构函数; 调用 operator delete(pAa); 释放内存。 显然,这里只对数组的第一个类对象调用了析构函数,后面的两个对象均没调用析构函数,如果类对象中申请了大量的内存需要在析构函数中释放,而你却在销毁数组对象时少调用了析构函数,这会造成内存泄漏。 上面的问题你如果说没关系的话,那么第二点就是致命的了!直接释放 pAa 指向的内存空间,这个总是会造成严重的段错误,程序必然会奔溃!因为分配的空间的起始地址是 pAa 指向的地方减去 4 个字节的地方。你应该传入参数设为那个地址! 同理,你可以分析如果使用 new 来分配,用 delete /delete[] 要配套使用总是没错的! 参考网址: http://blog.csdn.net/hazir/article/details/21413833#t0
个人分类: C++|21595 次阅读|0 个评论
[转载]mfc使用
majian 2012-2-12 12:13
(一),渐变色: dc- SetPixel(i,j,(unsigned long)(0x00000000 |0|j 8|i)); 根据用户点击彩色盘的坐标位置再结合你组合彩色的方法 (0x00000000 |j 16|i|i0)); ------即这个计算中i,j的位置 就可以知道用户选择的那种颜色. PHOTOSHOP的彩色盘的第三条拉条就是调整这个组合的. (二), radio的用法 问题1:如何让Radio1或者Radio2默认选上?如何知道哪个被选上了? 关键是选上,“默认”只要放在OnInitDialog()即可。三种方法可以让它选上, 第一种: ((CButton *)GetDlgItem(IDC_RADIO1))-SetCheck(TRUE);//选上 ((CButton *)GetDlgItem(IDC_RADIO1))-SetCheck(FALSE);//不选上 ((CButton *)GetDlgItem(IDC_RADIO1))-GetCheck();返回1表示选上,0表示没选上 第二种: 关联一个congtrol型变量(子类化),好ctrl+W(即打开classwizard),点开 Member Variables,咦?怎么没有IDC_RADIO1这个ID?原来是没有分组。因为radio button通常都是成组使用的,在一组里面是互斥的。取消,回到对话框资源面板,右键Radio1查看属性把Group选上,那么,Radio1和 Radio2就是一组了(怎么知道他们是一组的?后面说)。此时,就可以为Radio1增加一congtrol型变量m_ctrlRadio1了。如下: m_ctrlRadio1.SetCheck(TRUE); 同样可以使用GetCheck()获取状态。 第三种: 关联一个int型变量(同样需要先分组)m_nRadio1,打开对话框构造函数,你会发现有: m_nRadio1 = -1;m_nRadio1别赋值-1表示哪个都没有选上。如果你把-1改成0,就会发现Radio1默认被选上了,依此类推,m_nRadio1的值为1 就是第二个被选上了(这里同样有问题,哪个是第一个?哪个是第二个?)。获取状态很简单,UpdateData(TRUE)后判断m_nRadio1的值 即可。 问题2:如何使用多组? 多组和一组是一样的使用,只要搞清楚哪个是哪一组的就行了。再为对话框添加Radio3和Radio4。很简单, 先为这些Radio Button排个顺序 ( 这 个必须要做,比如你的一组控件有Radio1,Radio2,Radio3,就把它们的TAB顺序分别设为1,2,3,并将Radio1的Group属性 设为True,这样,当选中Radio1的时候和它关联的变量就是0,当选中Radio2的时候和它关联的变量就是1,依此类推 ),就是排 列他们的TAB ORDER。在对话框资源面板上Ctrl+D,然后按你自己的理想顺序用鼠标逐个点击就可以了。不妨假设Radio1、Radio2、Radio3、 Radio4分别是1、2、3、4。Radio1和Radio3都选上Group属性,那么,1、2是一组,3、4是另外一组,因为分组的原则是在选上 Group属性的这一个开始直到碰到下一个选上Group属性的。你不妨再Ctrl+D,令Radio1、Radio2、Radio3、Radio4分别 是1、3、2、4,那么Radio1和Radio3是一组,如果m_nRadio1=1,此时是Radio3被选上而不是Radio2被选上。分好了组就 分别使用它们吧。 嗯,也许你还要为它们添加鼠标单击事件,非常简单。 单选按钮控件(Radio Button)的使用 关键词: 单选按钮控件 使用方法 一、对单选按钮进行分组: 每组的第一个单选按钮设置属性:Group,Tabstop,Auto;其余按钮设置属性Tabstop,Auto。如: Radio1、Radio2、Radio3为一组,Radio4、Radio5为一组 设定Radio1属性:Group,Tabstop,Auto 设定Radio2属性:Tabstop,Auto 设定Radio3属性:Tabstop,Auto 设定Radio4属性:Group,Tabstop,Auto 设定Radio5属性:Tabstop,Auto 二、用ClassWizard为单选控件定义变量,每组只能定义一个。如:m_Radio1、m_Radio4。 三、用ClassWizard生成各单选按钮的单击消息函数,并加入内容: void CWEditView::OnRadio1() { m_Radio1 = 0; //第一个单选按钮被选中 } void CWEditView::OnRadio2() { m_Radio1 = 1; //第二个单选按钮被选中 } void CWEditView::OnRadio3() { m_Radio1 = 2; //第三个单选按钮被选中 } void CWEditView::OnRadio4() { m_Radio4 = 0; //第四个单选按钮被选中 } void CWEditView::OnRadio5() { m_Radio4 = 1; //第五个单选按钮被选中 } 四、设置默认按钮: 在定义控件变量时,ClassWizard在构造函数中会把变量初值设为-1,只需把它改为其它值即可。 如: //{{AFX_DATA_INIT(CUnitBlockTypeFlankPublicAdd) m_Radio1 = 0; //初始时第一个单选按钮被选中 m_Radio4 = 0; //初始时第四个单选按钮被选中 //}} //Radio Button 使用 使用方法一: 在工程中添加控件后,直接在控件的Click事件中写入需要的代码即可 使用方法二: 1.建立一个基于对话框的用用程序,在其中加入三个Radio Button,ID分别为: IDC_RADIO1,IDC_RADIO2,IDC_RADIO3 2.控件的初始化: 在对话框类的OnInitDialog中加入代码: CheckRadioButton(IDC_RADIO1,IDC_RADIO3,IDC_RADIO3); //第一个参数为该组的第一个单选按钮的ID //第二个参数为该组的最后一个单选按钮的ID //第三个参数为该组中被选中的单选按钮的ID 3.在加入一个Button控件,并为其写入Click事件代码: int iRadioButton; iRadioButton="GetCheckedRadioButton"(IDC_RADIO1,IDC_RADIO3); if(iRadioButton==IDC_RADIO1) MessageBox("Click Button1"); if(iRadioButton==IDC_RADIO2) MessageBox("Click Button2"); if(iRadioButton==IDC_RADIO3) MessageBox("Click Button3"); 其他说明: Radio Button成组使用时,只需保证在添加控件时资源号连续,并且第一个Radio Button的属性中Group被选中即可. 这样就可以给这一组控件添加成员变量使用 例: 1.建立一个基于对话框的应用程序,添加三个Radio Button,和一个Button 选中第一个Radio Button属性中的Group 2.在Button的Click事件中添加代码: UpdateData(TRUE); if(m_radio==0) MessageBox("0"); if(m_radio==1) MessageBox("1"); if (m_radio==2) MessageBox("2"); UpdateData(false); (三), 在VC中创建动态数组 怎样给多维数组动态分配内存 //Allocate: int **p = new int* ; for(int i = 0 ; i m ; i++) p = new int ; //Use: for(int i = 0 ; i m; i++) for(int j = 0 ; j n ; j++) p = i * j; //Free: for(int i = 0 ; i m ; i++) delete ; delete 的二维动态数组 /////////////////////////////////////////////////////////////////// int n1, n2; const int DIM1 = 2; const int DIM2 = 3; // 构造数组 int **ppi = new int* ; for(n1 = 0; n1 DIM1; n1++) { ppi = new int ; } // 填充数据 for(n1 = 0; n1 DIM1; n1++) { for(n2 = 0; n2 DIM2; n2++) { ppi = n1 * 10 + n2; } } // 输出 for(n1 = 0; n1 DIM1; n1++) { for(n2 = 0; n2 DIM2; n2++) { afxDump "ppi = " ppi "\n"; } } // 释放数组 for(n1 = 0; n1 DIM1; n1++) { delete ; } delete ) /////////////////////////////////////////////////////////////////// int n1, n2, n3; const int DIM1 = 2; const int DIM2 = 3; const int DIM3 = 4; // 构造数组 int ***ppi = new int** ; for(n1 = 0; n1 DIM1; n1++) { ppi = new int* ; for(n2 = 0; n2 DIM2; n2++) { ppi = new int ; } } // 填充数据 for(n1 = 0; n1 DIM1; n1++) { for(n2 = 0; n2 DIM2; n2++) { for(n3 = 0; n3 DIM3; n3++) { ppi = n1 * 100 + n2 * 10 + n3; } } } // 输出 for(n1 = 0; n1 DIM1; n1++) { for(n2 = 0; n2 DIM2; n2++) { for(n3 = 0; n3 DIM3; n3++) { afxDump "ppi = " ppi "\n"; } } } // 释放数组 for(n1 = 0; n1 DIM1; n1++) { for(n2 = 0; n2 DIM2; n2++) { delete ; } delete ; } delete ; //n 必须是整型变量 二维的呢,这样来, int **arr; int n,m; cin n m; arr = new int* ; for(int i=0;in;i++) { arr = new int ; } 上面的代码就可以通过动态输入n,m来实现二维数组的定义
个人分类: 技术|2948 次阅读|0 个评论

Archiver|手机版|科学网 ( 京ICP备07017567号-12 )

GMT+8, 2024-5-20 15:15

Powered by ScienceNet.cn

Copyright © 2007- 中国科学报社

返回顶部