这是一篇我所见过的关于指针的最好的入门级文章,它可以使初学者在很短的时间内掌握复杂的指针操做。虽然,如今的Java、C#等语言已经取消了指针,但做为一个C++程序员,指针的直接操做内存,在数据操做方面有着速度快,节约内存等优势,还是不少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 <iostream.h>void main(){// 声明变量:int nNumber;int *pPointer;// 如今给它们赋值:nNumber = 15;pPointer = &nNumber;//打印出变量nNumber的值:cout<<"nNumber is equal to :"<< nNumber<<endl;// 如今经过指针改变nNumber的值:*pPointer = 25;//证实nNumber已经被上面的程序改变//从新打印出nNumber的值: cout<<"nNumber is equal to :"<<nNumber<<endl; } 通读一下这个程序,编译并运行它,务必明白它是怎样工做的。若是你完成了,准备好,开始下一小节。陷井! 试一下,你能找出下面这段程序的错误吗?#include <iostream.h>int *pPointer;void SomeFunction();{int nNumber;nNumber = 25;//让指针指向nNumber:pPointer = &nNumber;}void main(){SomeFunction(); //为pPointer赋值//为何这里失败了?为何没有获得25cout<<"Value of *pPointer: "<<*pPointer<<endl;} 这段程序先调用了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 <iostream.h>int *pPointer;void SomeFunction(){// 让指针指向一个新的整型pPointer = new int;*pPointer = 25;}void main(){SomeFunction(); // 为pPointer赋值cout<<"Value of *pPointer: "<<*pPointer<<endl; } 通读这个程序,编译并运行它,务必理解它是怎样工做的。当SomeFunction 调用时,它分配了一个内存,并让pPointer指向它。这一次,当函数返回时,新的内存区域被保留下来,因此pPointer始终指着有用的信息,这是由于了动态分配。可是你再仔细读读上面这个程序,虽然它获得了正确结果,可仍有一个严重的错误。分配了内存,别忘了回收 太复杂了,怎么会还有严重的错误!其实要改正并不难。问题是:你动态地分配了一个内存空间,可它毫不会被自动删除。也就是说,这块内存空间会一直存在,直到你告诉电脑你已经使用完了。可结果是,你并无告诉电脑你已再也不须要这块内存空间了,因此它会继续占据着内存空间形成浪费,甚至你的程序运行完毕,其它程序运行时它还存在。当这样的问题积累到必定程度,最终将致使系统崩溃。因此这是很重要的,在你用完它之后,请释放它的空间,如: delete pPointer; 这样就差很少了,你不得不当心。在这你终止了一个有效的指针(一个确实指向某个内存的指针)。 下面的程序,它不会浪费任何的内存:#include <iostream.h>int *pPointer;void SomeFunction(){// 让指针指向一个新的整型pPointer = new int;*pPointer = 25;}void main(){SomeFunction(); //为pPointer赋值cout<<"Value of *pPointer: "<<*pPointer<<endl;delete pPointer;} 只有一行与前一个程序不一样,但就是这最后一行十分地重要。若是你不删除它,你就会制造一块儿“内存漏洞”,而让内存逐渐地泄漏。 (译者:假如在程序中调用了两次SomeFunction,你又该如何修改这个程序呢?请读者本身思考)传递指针到函数 传递指针到函数是很是有用的,也很容易掌握。若是咱们写一个程序,让一个数加上5,看一看这个程序完整吗?: #include <iostream.h>void AddFive(int Number){Number = Number + 5;}void main(){int nMyNumber = 18;cout<<"My original number is "<<nMyNumber<<endl; AddFive(nMyNumber);cout<<"My new number is "<<nMyNumber<<endl; //获得告终果23吗?问题出在哪儿?} 问题出在函数AddFive里用到的Number是变量nMyNumber的一个副本而传递给函数,而不是变量自己。所以, " Number = Number + 5" 这一行是把变量的副本加了5,而原始的变量在主函数main()里依然没变。试着运行这个程序,本身去体会一下。 要解决这个问题,咱们就要传递一个指针到函数,因此咱们要修改一下函数让它能接受指针:把'void AddFive(int Number)' 改为 'void AddFive(int* Number)' 。下面就是改过的程序,注意函数调用时要用&号,以表示传递的是指针: #include <iostream.h>void AddFive(int* Number){*Number = *Number + 5;}void main(){int nMyNumber = 18;cout<<"My original number is "<<nMyNumber<<endl; AddFive(&nMyNumber);cout<<"My new number is "<<nMyNumber<<endl; } 试着本身去运行它,注意在函数AddFive的参数Number前加*号的重要性:它告诉编译器,咱们是把指针所指的变量加5。而不并指针本身加5。 最后,若是想让函数返回指针的话,你能够这么写: int * MyFunction(); 在这句里,MyFunction返回一个指向整型的指针。指向类的指针 指针在类中的操做要格外当心,你能够用以下的办法定义一个类: class MyClass{ public: int m_Number; char m_Character;}; 接着你就能够定义一个MyClass 类的变量了: MyClass thing; 你应该已经知道怎样去定义一个指针了吧: MyClass *thing; 接着你能够分配个内存空间给它: thing = new MyClass; 注意,问题出现了。你打算怎样使用这个指针呢,一般你可能会写'thing.m_Number',可是thing是类吗,不,它是一个指向类的指针,它自己并不包含一个叫m_Number的变量。因此咱们必须用另外一种方法:就是把'.'(点号)换成 -> ,来看下面的例子: class MyClass{public: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 int[6]; 程序会建立一个指针pArray,让它指向一个有六个元素的数组。另一种方法,不用动态分配: int *pArray;int MyArray[6];pArray = &MyArray[0]; 注意,&MyArray[0] 也能够简写成 MyArray ,都表示是数组的第一个元素地址。但若是写成pArray = &MyArray可能就会出问题,结果是 pArray 指向的是指向数组的指针(在一维数组中尽管与&MyArray[0]相等),而不是你想要的,在多维数组中很容易出错。在数组中使用指针 一旦你定义了一个指向数组的指针,你该怎样使用它呢?让咱们来看一个例子,一个指向整型数组的指针:#include <iostream.h>void main(){int Array[3];Array[0] = 10;Array[1] = 20;Array[2] = 30;int *pArray;pArray = &Array[0];cout<<"pArray points to the value %d\n"<<*pArray<<endl;} 若是让指针指向数组元素中的下一个,能够用pArray++.也能够用你应该能想到的pArray + 1,都会让指针指向数组的下一个元素。要注意的是你在移动指针时,程序并不检查你是否已经移动地超出了你定义的数组,也就是说你极可能经过上面的简单指针加操做而访问到数组之外的数据,而结果就是,可能会使系统崩溃,因此请格外当心。 固然有了pArray + 1,也能够有pArray - 1,这种操做在循环中很经常使用,特别是while循环中。 另外一个须要注意的是,若是你定义了一个指向整型数的指针:int* pNumberSet ,你能够把它看成是数组,如:pNumberSet[0] 和 *pNumberSet是相等的,pNumberSet[1]与*(pNumberSet + 1)也是相等的。 在这一节的最后提一个警告:若是你用 new 动态地分配了一个数组, int *pArray;pArray = new int[6]; 别忘了回收, 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 改变吗?cout<<myFristNumber<<endl;//结果是20仍是25? 当在类中操做时,引用的值必须在构造函数中设定,例:CMyClass::CMyClass(int &variable) : m_MyReferenceInCMyClass(variable){ // constructor code here}总结 这篇文章开始可能会较难掌握,因此最好是多读几遍。有些读者暂时还不能理解,在这儿我再作一个简要的总结: 指针是一个指向内存区域的变量,定义时在变量名前加上星号(*)(如:int *number)。 你能够获得任何一个变量的地址,只在变量名前加上&(如:pNumber = &my_number)。 你能够用'new' 关键字动态分配内存。指针的类型必须与它所指的变量类型同样(如:int *number 就不能指向 MyClass)。 你能够传递一个指针到函数。必须用'delete'删除你动态分配的内存。 你能够用&array[0]而让指针指向一个数组。 你必须用delete[]而不是delete来删除动态分配的数组。 文章到这儿就差很少结束了,但这些并不就是指针全部的东西,像指向指针的指针等我尚未介绍,由于这些东西对于一个初学指针的人来讲还太复杂了,我不能让读者一开始就被太复杂的东西而吓走了。好了,到这儿吧,试着运行我上面写的小程序,也多本身写写程序,你确定会进步不小的!