c++指针改怎么用.doc_第1页
c++指针改怎么用.doc_第2页
c++指针改怎么用.doc_第3页
c++指针改怎么用.doc_第4页
c++指针改怎么用.doc_第5页
已阅读5页,还剩14页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

转c+指针|指针入门什么是指针?其实指针就像是其它变量一样,所不同的是一般的变量包含的是实际的真实的数据,而指针是一个指示器,它告诉程序在内存的哪块区域可以找到数据。这是一个非常重要的概念,有很多程序和算法都是围绕指针而设计的,如链表。开始学习如何定义一个指针呢?就像你定义一个其它变量一样,只不过你要在指针名字前加上一个星号。我们来看一个例子:下面这个程序定义了两个指针,它们都是指向整型数据。int* pNumberOne;int* pNumberTwo;你注意到在两个变量名前的“p”前缀了吗?这是程序员通常在定义指针时的一个习惯,以提高便程序的阅读性,表示这是个指针。现在让我们来初始化这两个指针: pNumberOne = &some_number;pNumberTwo = &some_other_number; &号读作“什么的地址”,它表示返回的是变量在内存中的地址而不是变量本身的值。在这个例子中,pNumberOne 等于 some_number的地址,所以现在pNumberOne指向some_number。 如果现在我们在程序中要用到some_number,我们就 可以使用pNumberOne。我们来学习一个例子:在这个例子中你将学到很多,如果你对指针的概念一点都不了解,我建议你多看几遍这个例子,指针是个很复杂的东西,但你会很快掌握它的。这个例子用以增强你对上面所介绍内容的了解。它是用C编写的(注:原英文版是用C写的代码,译者重新用C+改写写了所有代码,并在DEV C+ 和VC+中编译通过!)#include void main()/ 声明变量:int nNumber;int *pPointer;/ 现在给它们赋值:nNumber = 15;pPointer = &nNumber;/打印出变量nNumber的值:coutnNumber is equal to : nNumberendl;/ 现在通过指针改变nNumber的值:*pPointer = 25;/证明nNumber已经被上面的程序改变/重新打印出nNumber的值: coutnNumber is equal to :nNumberendl; 通读一下这个程序,编译并运行它,务必明白它是怎样工作的。如果你完成了,准备好,开始下一小节。陷井!试一下,你能找出下面这段程序的错误吗?#include int *pPointer;void SomeFunction();int nNumber;nNumber = 25;/让指针指向nNumber:pPointer = &nNumber;void main()SomeFunction(); /为pPointer赋值/为什么这里失败了?为什么没有得到25coutValue of *pPointer: *pPointerendl;这段程序先调用了SomeFunction函数,创建了个叫nNumber的变量,接着让指针pPointer指向了它。可是问题出在哪儿 呢?当函数结束后,nNumber被删掉了,因为这一个局部变量。局部变量在定义它的函数执行完后都会被系统自动删掉。也就是说当 SomeFunction 函数返回主函数main()时,这个变量已经被删掉,但pPointer还指着变量曾经用过的但现在已不属于这个程序的区域。 如果你还不明白,你可以再读读这个程序,注意它的局部变量和全局变量,这些概念都非常重要。但这个问题怎么解决呢?答案是动态分配技术。注意这在C和C+中是不同的。由于大多数程序员都是用C+,所以我用到的是C+中常用的称谓。动态分配动态分配是指针的关键技术。它是用来在不必定义变量的情况下分配内存和让指针去指向它们。尽管这么说可能会让你迷惑,其实它真的很简单。下面的代码就是一个为一个整型数据分配内存的例子: int *pNumber;pNumber = new int; 第一行声明一个指针pNumber。第二行为一个整型数据分配一个内存空间,并让pNumber指向这个新内存空间。下面是一个新例,这一次是用double双精型: double *pDouble;pDouble = new double; 这种格式是一个规则,这样写你是不会错的。但动态分配又和前面的例子有什么不同呢?就是在函数返回或执行完毕时,你分配的这块内存区域是不会被删除的所以我们现在可以用动态分配重写上面的程序: #include int *pPointer;void SomeFunction()/ 让指针指向一个新的整型pPointer = new int;*pPointer = 25;void main()SomeFunction(); / 为pPointer赋值coutValue of *pPointer: *pPointerendl; 通读这个程序,编译并运行它,务必理解它是怎样工作的。当SomeFunction 调用时,它分配了一个内存,并让pPointer指向 它。这一次,当函数返回时,新的内存区域被保留下来,所以pPointer始终指着有用的信息,这是因为了动态分配。但是你再仔细读读上面这个程序,虽然 它得到了正确结果,可仍有一个严重的错误。分配了内存,别忘了回收太复杂了,怎么会还有严重的错误!其实要改正并不难。问题是:你动 态地分配了一个内存空间,可它绝不会被自动删除。也就是说,这块内存空间会一直存在,直到你告诉电脑你已经使用完了。可结果是,你并没有告诉电脑你已不再 需要这块内存空间了,所以它会继续占据着内存空间造成浪费,甚至你的程序运行完毕,其它程序运行时它还存在。当这样的问题积累到一定程度,最终将导致系统 崩溃。所以这是很重要的,在你用完它以后,请释放它的空间,如: delete pPointer; 这样就差不多了,你不得不小心。在这你终止了一个有效的指针(一个确实指向某个内存的指针)。下面的程序,它不会浪费任何的内存:#include int *pPointer;void SomeFunction()/ 让指针指向一个新的整型pPointer = new int;*pPointer = 25;void main()SomeFunction(); /为pPointer赋值coutValue of *pPointer: *pPointerendl;delete pPointer;只有一行与前一个程序不同,但就是这最后一行十分地重要。如果你不删除它,你就会制造一起“内存漏洞”,而让内存逐渐地泄漏。(译者:假如在程序中调用了两次SomeFunction,你又该如何修改这个程序呢?请读者自己思考)传递指针到函数传递指针到函数是非常有用的,也很容易掌握。如果我们写一个程序,让一个数加上5,看一看这个程序完整吗?: #include void AddFive(int Number)Number = Number + 5;void main()int nMyNumber = 18;coutMy original number is nMyNumberendl; AddFive(nMyNumber);coutMy new number is nMyNumberendl; /得到了结果23吗?问题出在哪儿? 问题出在函数AddFive里用到的Number是变量nMyNumber的一个副本而传递给函数,而不是变量本身。因 此, Number = Number + 5 这一行是把变量的副本加了5,而原始的变量在主函数main()里依然没变。试着运行这个程序,自 己去体会一下。要解决这个问题,我们就要传递一个指针到函数,所以我们要修改一下函数让它能接受指针:把 void AddFive(int Number) 改成 void AddFive(int* Number)。下面就是改过的程序,注意函 数调用时要用号,以表示传递的是指针: #include void AddFive(int* Number)*Number = *Number + 5;void main()int nMyNumber = 18;coutMy original number is nMyNumberendl; AddFive(&nMyNumber);coutMy new number is nMyNumber ,来看下面的例子: class MyClasspublic:int m_Number;char m_Character;void main()MyClass *pPointer;pPointer = new MyClass;pPointer-m_Number = 10;pPointer-m_Character = s;delete pPointer;指向数组的指针你也可以让指针指向一个数组,按下面的方法操作: int *pArray;pArray = new int6; 程序会创建一个指针pArray,让它指向一个有六个元素的数组。另外一种方法,不用动态分配: int *pArray;int MyArray6;pArray = &MyArray0; 注意,&MyArray0 也可以简写成 MyArray ,都表示是数组的第一个元素地址。但如果写成pArray = & MyArray可能就会出问题,结果是 pArray 指向的是指向数组的指针(在一维数组中尽管与&MyArray0相等),而不是你想要 的,在多维数组中很容易出错。在数组中使用指针一旦你定义了一个指向数组的指针,你该怎样使用它呢?让我们来看一个例子,一个指向整型数组的指针:#include void main()int Array3;Array0 = 10;Array1 = 20;Array2 = 30;int *pArray;pArray = &Array0;coutpArray points to the value %dn*pArrayendl;如果让指针指向数组元素中的下一个,可以用pArray+.也可以用你应该能想到的pArray + 1,都会让指针指向数组的下一个元 素。要注意的是你在移动指针时,程序并不检查你是否已经移动地超出了你定义的数组,也就是说你很可能通过上面的简单指针加操作而访问到数组以外的数据,而 结果就是,可能会使系统崩溃,所以请格外小心。当然有了pArray + 1,也可以有pArray 1,这种操作在循环中很常用,特别是while循环中。另一个需要注意的是,如果你定义了一个指向整型数的指针:int* pNumberSet ,你可以把它当作是数组,如:pNumberSet0 和 *pNumberSet是相等的,pNumberSet1与*(pNumberSet + 1)也是相等的。在这一节的最后提一个警告:如果你用 new 动态地分配了一个数组, int *pArray;pArray = new int6; 别忘了回收, delete pArray; 这一句是告诉编译器是删除整个数组而不一个单独的元素。千万记住了。后话还有一点要小心,别删除一个根本就没分配内存的指针,典型的是如果没用new分配,就别用delete:void main()int number;int *pNumber = number;delete pNumber; / 错误 - *pNumber 没有用new动态分配内存.常见问题解答Q:为什么我在编译程序时老是在 new 和 delete语句中出现symbol undefined 错误?A:new 和 delete都是C+在C上的扩展,这个错误是说编译器认为你现在的程序是C而不C+,当然会出错了。看看你的文件名是不是.cpp结尾。Q:new 和 malloc有什么不同?A:new 是C+中的关健字,用来分配内存的一个标准函数。如果没有必要,请不要在C+中使用malloc。因为malloc是C中的语法,它不是为面向对象的C+而设计的。Q:我可以同时使用free 和 delete吗?A:你应该注意的是,它们各自所匹配的操作不同。free只用在用malloc分配的内存操作中,而delete只用在用new分配的内存操作中。引用(写给某些有能力的读者)这一节的内容不是我的这篇文章的中心,只是供某些有能力的读者参考。有些读者经常问我关于引用和指针的问题,这里我简要地讨论一下。在前面指针的学习中,我们知道()是读作“什么的地址”,但在下面的程序中,它是读作“什么的引用”int& Number = myOtherNumber;Number = 25; 引用有点像是一个指向myOtherNumber的指针,不同的是它是自动删除的。所以他比指针在某些场合更有用。与上面等价的代码是: int* pNumber = &myOtherNumber;*pNumber = 25; 指针与引用另一个不同是你不能修改你已经定义好的引用,也就是说你不能改变它在声明时所指的内容。举个例子: int myFirstNumber = 25;int mySecondNumber = 20;int &myReference = myFirstNumber;myReference = mySecondNumber;/这一步能使myReference 改变吗?coutmyFristNumbervalue; 当然了,这个指针仍然是属于第一种范筹-常规数据指针。 好了,我们现在需要一种指针,它指向MyStruct中的任一数据成员,那么它应该是这样的子: int MyStruct:* pMV = &MyStruct:value; /或 int MyStruct:* pMK = &MyStruct:key; 这种指针的用途是用于取得结构成员在结构内的地址。我们可以通过该指针来访问成员数据: int value = pMe-*pMV; / 取得pMe的value成员数据。 int key= me.*pMK; / 取得me的key成员数据。 那么,在什么场合下会使用到成员数据指针呢? 确实,成员指针本来就不是一种很常用的指针。不过,在某些时候还是很有用处的。我们先来看看下面的一个函数: int sum(MyStruct* objs, int MyStruct:* pm, int count) int result = 0; for(int i = 0; i count; +i) result += objsi.*pm; return result; 这个函数的功能是什么,你能看明白吗?它的功能就是,给定count个MyStruct结构的指针,计算出给定成员数据的总和。有点拗口对吧?看看下面的程序,你也许就明白了: MyStruct me10 = 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20 ; int sum_value = sum(me, &MyStruct:value, 10); /计算10个MyStruct结构的value成员的总和: sum_value 值 为 110 (2+4+6+8+20) int sum_key = sum(me, &MyStruct:key, 10); /计算10个MyStruct结构的key成员的总和: sum_key 值 为 100 (1+3+5+7+19) 也许,你觉得用常规指针也可以做到,而且更易懂。Ok,没问题: int sum(MyStruct* objs, int count) int result = 0; for(int i = 0; i count; +i) result += objsi.value; return result; 你是想这么做吗?但这么做,你只能计算value,如果要算key的话,你要多写一个函数。有多少个成员需要计算的话,你就要写多少个函数,多麻烦啊。 在C/C+中,数据指针是最直接,也最常用的,因此,理解起来也比较容易。而函数指针,作为运行时动态调用(比如回调函数 CallBack Function)是一种常见的,而且是很好用的手段。 我们先简单的说一下函数指针。(这一部份没什么价值,纯是为了引出下一节的内容) 2 常规函数指针 void(*fp)();fp 是一个典型的函数指针,用于指向无参数,无返回值的函数。 void(*fp2)(int);fp2 也是一个函数指针,用于指向有一个整型参数,无返回值的函数。当然,有经验人士一般都会建议使用typedef来定义函数指针的类型,如: typedef void(* FP)(); FP fp3; / 和上面的fp一样的定义。函数指针之所以让初学者畏惧,最主要的原因是它的括号太多了;某些用途的函数指针,往往会让人陷在括号堆中出不来,这里就不举例了,因为不是本 文讨论的范围;typedef 方法可以有效的减少括号的数量,以及理清层次,所以受到推荐。本文暂时只考虑简单的函数指针,因此暂不用到typedef。假如有如下两个函数: void f1() std:cout call f std:endl; void f2(int a) std:cout call f2( a ) std:endl; 现在需要通过函数指针来调用,我们需要给指针指定函数: fp = &f1; / 也可以用:fp = f1; fp2= &f2; / 也可以用:fp2= f2; void (*fp3)() = &f1; / 也可以用:void (*fp3)() = f1; /调用时如下: fp(); / 或 (*fp)(); fp2(1); / 或 (*fp2)(1); fp3(); / 或 (*fp3)();对于此两种调用方法,效果完全一样,我推荐用前一种。后一种不仅仅是多打了键盘,而且也损失了一些灵活性。这里暂且不说它。 C+强调类型安全。也就是说,不同类型的变量是不能直接赋值的,否则轻则警告,重则报错。这是一个很有用的特性,常常能帮我们找到问题。因此,有识之 士认为,C+中的任何一外警告都不能忽视。甚至有人提出,编译的时候不能出现任何警告信息,也就是说,警告应该当作错误一样处理。 比如,我们把f1赋值给fp2,那么C+编译器(vc7.1)就会报错: fp2 = &f1; / error C2440: “=” : 无法从“void (_cdecl *)(void)”转换为“void (_cdecl *)(int)” fp1 = &f1; / OK这样,编译器可以帮我们找出编码上的错误,节省了我们的排错时间。 考虑一下C+标准模板库的sort函数: / 快速排序函数 template void sort( RandomAccessIterator _First, / 需排序数据的第一个元素位置 RandomAccessIterator _Last, / 需排序数据的最后一个元素位置(不参与排序) BinaryPredicate _Comp / 排序使用的比较算法(可以是函数指针、函数对象等) );比如,我们有一个整型数组: int n5 = 3,2,1,8,9;要对它进行升序排序,我们需定义一个比较函数: bool less(int a, int b) return a doit(); void doit();main() mythread* pmt = new mythread; _beginthread(&mythread:doit, 0, (void*)pmt); 但是显然,C+程序员肯定不会因此而满足。这里头有许多被C+批判的不安定因素。它使用了C+中被认为不安全的类型转换,不安全的 void*指针,等等等等。但这是系统为C语言留下的调用接口,这也就认了。那么假如,我们就在C+程序中如何来调用成员函数指针呢?如下例,我们打算对vector中的所有类调用其指定的成员函数:#include #include #include #include using namespace std;class A int value;public: A(int v)value = v; void doit() cout value endl; static void call_doit(A& rThis) rThis.doit(); ;int main() vector va; va.push_back(A(1); va.push_back(A(2); va.push_back(A(3); va.push_back(A(4); /方法1: /for_each(va.begin(), va.end(), &A:doit); /error /方法2: for_each(va.begin(), va.end(), &A:call_doit); /方法3: for_each(va.begin(), va.end(), mem_fun_ref(&A:doit); system(Pause); return 0;方法1,编译不能通过。for_each只允许具有一个参数的函数指针或函数对象,哪怕A:doit默认有一个this指针参数也不行。不是for_each没考虑到这一点,而是根本做不到!方法2,显然是受到了beginthread的启发,使用一个静态函数来转调用,哈哈成功了。但是不爽!这不是C+。方法3,呼,好不容易啊,终于用mem_fun_ref包装成功了成员函数指针。 似乎方法3不错,又是类型安全的,又可以通用慢着,首先,它很丑,哪有调用普通C函数指针那么漂亮啊(见方法2),用了一大串包装,又是尖括号又是 圆括号,还少不了&号!其次,它只能包装不超过一个参数的函数!尽管它在for_each中够用了,但是你要是想用在超过一个参数的场合,那只有 一句话:不可能的任务。是的,在标准C+中,这是不可能的任务。但事情并不总是悲观的,至少有许多第三方库提供了超越mem_fun的包装。如 boost:function等等。但是它也有限制:它所支持的参数仍然是有限的,只有十多个,尽管够你用的了;同样,它也是丑陋的,永远不要想它能够 简单的用&来搞定。也许,以失去美丽的代价,来换取质量上的保证,这也是C+对于函数指针的一种无奈吧期待C+0x版本。它通过可变模板参数,能够让mem_fun的参数达到无限个 BTW: C+Builder扩展了一个关键字 closure ,允许成员函数指针如同普通函数指针一样使用。也许C+0x能考虑一下函数对象不是函数指针。但是,在程序代码中,它的调用方式与函数指针一样,后面加个括号就可以了。这是入门级的随笔,说的是函数对象的定义,使用,以及与函数指针,成员函数指针的关系。 沐枫小筑函数对象实质上是一个实现了operator()-括号操作符-的类。例如:class Addpublic: int operator()(int a, int b) return a + b; ;Add add; / 定义函数对象cout add(3,2);/ 5函数指针版本就是:int AddFunc(int a, int b) return a + b;typedef int (*Add) (int a, int b);Add add = &AddFunc;cout add(3,2);/ 5呵呵,除了定义方式不一样,使用方式可是一样的。都是:cout add(3,2);既然函数对象与函数指针在使用方式上没什么区别,那为什么要用函数对象呢?很简单,函数对象可以携带附加数据,而指针就不行了。下面就举个使用附加数据的例子:class lesspublic: less(int num):n(num) bool operator()(int value) return value n; private: int n;使用的时候: less isLess(10); cout isLess(9) isLess(12); / 输出 1 0这个例子好象太儿戏了,换一个:const int SIZE = 5;int arraySIZE = 50, 30, 9, 7, 20;/ 找到小于数组array中小于10的第一个数的位置int * pa = std:find_if(array, array + SIZE, less(10);/ pa 指向 9 的位置/ 找到小于数组array中小于40的第一个数的位置int * pb = std:find_if(array, array + SIZE, less(40);/ pb 指向 30 的位置这里可以看出函数对象的方便了吧?可以把附加数据保存在函数对象中,是函数对象的优势所在。它的弱势也很明显,它虽然用起来象函数指针,但毕竟不是真正的函数指针。在使用函数指针的场合中,它就无能为力了。例如,你不能将函数对象传给qsort函数!因为它只接受函数指针。要想让一个函数既能接受函数指针,也能接受函数对象,最方便的方法就是用模板。如:templateint count_n(int* array, int size, FUNC func) int count = 0; for(int i = 0; i size; +i) if(func(arrayi) count +

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论