现在的位置: 首页 > 综合 > 正文

我靠,指针!

2011年07月04日 ⁄ 综合 ⁄ 共 3111字 ⁄ 字号 评论关闭

由于最近一直在用C#.net的缘故,几乎再也不会做什么指针的运算了。其实最初学C++的时候,指针就没怎么学明白,后来也不怎么用了,干脆就把它放下了。昨天有一门研究生讨论课,终于又和指针打上交道了,就因为语言又回到了C++。

指针,我觉得已经算是计算机语言在搞内存的时候,最成功的东西了。但是指针的东西,搞不好还真是着实让人糊涂啊。

我们声明指针的时候,普遍也就是两种写法吧,无非是空格与*的位置的问题。比如我声明一个指向int的指针,我可以写成int* ptr,也可以写成int *ptr,这两种写法编译器都认,而且没有任何区别,但是对于人理解起来麻烦可就大了。以人最直观的印象,int* ptr是声明了一个叫做ptr的变量,它的类型是int*,而对于int *ptr,是声明了一个*ptr的变量,它的类型是int。其实不错,如果把声明和赋值写全,第二种看起来更加的合理,即int *ptr=new int(10); 现在用.net用多了,看到后面这个东西,好,就表示一个值为10的Int32的struct,但是在C++里,如果用了new这个东西,返回的就是指针了,虽然指针存放的东西——就是地址,就是个int变量,但是如果我们写成char *ptr呢,总不能说我们返回了一个地址存成char类型吧。所以,个人认为,第一种写法是最为直观而且便于理解的,即int*(和int[]一样)是一种类型,它声明了一个叫做ptr的变量,这个变量作为一个int*类型的指针变量,必须指向一个int,即它存放的值必须是一个存放int类型的地址。

唉,说着都觉得别扭,这还没完,因为这还仅仅是声明,声明时用到*还只是个开始,后面还有指针运算符会用它,再加上个&求地址运算符,好玩的还多着呢。刚才说到,int* ptr=new int(10); 是声明了一个指针叫做ptr,它指向了一块内存空间,而这块内存空间存的是一个int——10。好吧,那么如果我们cout<<ptr,得到的是什么?不错,得到的是地址,谁的地址?是存10的那块内存的首字节地址。那么如果我们cout<<*ptr,得到的什么?不错,得到的是10。因为*作为指针运算符,会把*后面跟的那个指针变量所指向的那块内存里面存的东西取出来,然后再跟据声明的指向类型转化成正确的类型。那么如果我们cout<<&ptr,得到的是什么?不错,得到的是地址,这回是存ptr的地址了,因为求地址运算符没那么啰嗦,它就直接把它后面跟的那个变量的存放地址取出来了。

好吧,也许你会说,这些东西都知道了。可能是吧,可是还没完,后面还有东西呢。指针好玩的东西,可不光在这一点上,比如说指针的指针就更好玩。比如,我声明一个int** pptr,然后给它赋值,在下面这几个东西里面,哪个能够编译过去?new int(10)? 10? ptr? &ptr?只有&ptr能编得过去。这是因为int** pptr这句话,声明了一个指针,这个指针必须指向一个声明为int*的指针,也就是说它是指针的指针,如果把话说全了,就应该是:我声明了一个叫做pptr的指针变量,它必须指向一块存放另一个指针变量的地址,而那个指针变量,必须指向一块存放int值的地址。好吧,我们来看这个int** pptr=&ptr; 如果我们cout<<pptr,得到的是什么?不错,是地址,谁的地址?是ptr的,这句话和cout<<&ptr得到的是同一个结果,这就是前面那句赋值运算做的事情,它把&ptr得到的地址值就存到了pptr里面,只不过这个pptr是个int**类型而已。还有,如果我们cout<<*pptr,得到的是什么?还是地址,这回是存10的那块地址了,因为*这个指针运算符做了一个事,就是把pptr里面存的那个地址里面存的东西给取出来了,这句话的输出,和cout<<ptr得到的结果是一样的。那么,cout<<**pptr是什么?这回是10了,因为*这个东西的运算顺序是从右向左的,**pptr就是*(*pptr),刚才说了*pptr和ptr是一个东西,那个**pptr和*ptr就是一个东西,所以输出的就是10。

好吧,我就不再往下嵌套了,再来了int***其实完全没有意义了,上面弄明白了,后面无非就是递归,开始成为文字游戏了,没有意思。不过,到这也还没完,指针这个东西,和值传递、引用传递大有关联,比如我写几个方法如下:

 1 void f(int* ptr)
 2 {
 3      ptr = new int(10);
 4 }
 5 
 6 void f(int i)
 7 {
 8      i = 10;
 9 }
10 
11 void f(int** pptr)
12 {
13      *ptr = new int(10);
14 }

我在主函数里面写int x;然后我想调用f这个方法给x进行赋值,哪个能够有用?首先我们看第一个,我调用f(x);然后x按值传递过去了,因为int型不赋值就是0,所以这句话和f(0)没有任何区别,于是乎f(int i)里面i这个形式参数变成了10,而那个传递过去的0,什么也没干,出了作用域就被pop出去了,所以调用过后,x还是0。再看那个f(int* ptr),看看这段代码,结果如何。

1 int* ptr = new int(0);
2 int x = *ptr;
3 f(ptr);

这段代码是说,我初始化了一个ptr指针,让它指向0,然后我把它指向的值给了x,然后我调用了f(ptr),那之后x是几?有人说你这是废话,x肯定是0,你在调f(ptr)之前就把它之前的值给了x,ptr以后再怎么变,x也不会变了。不错,是这么回事,如果代码这样呢?

Code

这回我没弄错吧,我是再调过了f之后才给x赋值的,这回x该得10了吧。很不幸,这个x还是0。因为虽然我值的是指针而不是int的值,但是指针也是值,在f的内部只是把这个指针的值给拷贝了一份过去,也就是说在f里面,有一个新的指针,它和外面的这个ptr一样,指向了那个0的地址,然后面f的内部,我们把这个指针指向了10,而外面那个ptr还是指向0,因此这个x,仍然是0。
那么我们还看最后一个,代码如下:

int* ptr = new int(0);
f(
&ptr);
int x = *ptr;

这回我是把ptr的地址作为一个int**传过去了,在f里面,我首先求了*pptr,也就是说我把ptr得到了,这回可不是拷贝的了,这是真正的ptr,在传值的时候,拷贝的是&ptr,也就是ptr的地址,但是经过一次*求指针运算,我已经得到了ptr,然后我让ptr=new(10);于是外面那个x变成了10。

指针的学问多,我记得当年考研时候的专业题,还有数组指针和指针数组。比如说我写int*[] 和int[]*,这两个东西分别代表什么呢?其实你如果把int*和int[]全部理解为一种类型,这个也没那么麻烦。int*[]是什么,首先[]在后面,它是一个数组,而这个数组里面存的都是int*类型的变量,所以它是一个指针数组;int[]*是什么,首先*在后面,它是一个指针,它指向的是一个int[]的数组类型,因此它是一个数组指针。

我能想到的东西好像就这么多了吧,想想还是.net好,什么时候想按引用传了,管它呢加个ref就完事了,多省心啊,哪像C++这样老得指来指去的,烦人死了。不过我觉得这种带着抽象和逻辑的东西,用来练脑子还不错,就像玩组合数学一样。。。

总之,希望我写的东西,对学习指针的新手能够有所帮助吧。

抱歉!评论已关闭.