对指针进行初始化中的若干问题的思考与反思
我们在最开始定义一个指针的时候,最应该做的就是先将这个指针变量进行初始化,以防止它变成“野指针”。初始化的方式很简单,就是将0赋给这个指针变量:
int *p=0;
如果我们用上面这条语句对指针进行初始化,那么编译器就会自动为我们把指针p中的值设置成0x00000000。(注意,一共有8个0哈,原因就是因为0是int型变量。该变量的字节数就是8个~~~)但是,大家有没有想过,如果我们不这样初始化这个指针,而采用下面的方法来初始化这个指针,其结果又会有什么不同呢?
- 虽然初始化这个指针为0,那么*p的值是不是会不会输出0呢?
- 如果我们定义一个变量,那么能不能用这个变量的地址来初始化这个指针呢?
- 既然整型变量0可以定义这个指针,那我们能不能用另外的整型变量,比如说20来初始化这个指针呢?如果定义成功,是不是编译器也会自动将20转换成16进制的0x00000020呢?
下面,我们用一个程序来验证上面这些问题的可行性:
#include <iostream> using namespace std; int main() { //第一组为正常的赋值,将指针变量p初始化为0。 cout<<"第一组:"<<endl; int i=10; int *p=0; cout<<"i的值为"<<i<<endl; cout<<"&i的地址为"<<&i<<endl; cout<<"p的值为"<<p<<endl; cout<<"*p的值为"<<*p<<endl; //第二组中,我们定义一个指针r,并将其初始化为i的地址 cout<<"第二组:"<<endl; int *r=&i; cout<<"i的值为"<<i<<endl; cout<<"&i的地址为"<<&i<<endl; cout<<"r的值为"<<r<<endl; cout<<"*r的值为"<<*r<<endl; //第三组,我们第一了一个指针q,并将20作为初始化指针q的值。观察会不会输出0x00014这样的16进制数 int *q=20; cout<<"q的值为"<<q<<endl; cout<<"*q的值为"<<*q<<endl; }
我们信心满满的一点编译,啊!!!悲剧就出来了~~~~~~
首先,我们发现这个小程序居然,居然通不过编译!问题直指第16行。错误的原因是“无法从“int”转换为“int *”。这句话的意思其实就是*q和20的类型不匹配。*q的类型为一个指向整型变量的指针(int*),而20是整型变量,它的类型就是int。编译器当然就不会认识这个是什么了啊。想一想原因就可以知道,在这里,我们初始化指针变量q的原因就在于让q中的值能成为一个固定的值,而不是成为一个野指针。而在这里,我们的心是好的,就是让这个指针之中的值存成20。这样他就不会到处乱指了。这样的心是好的,但是我们忽略了一个最基本的问题:“指针是拿来存什么的?”答案可能大家都知道,“指针当然是拿来存地址的啊!”好了,这就是错误的根源了。既然指针是拿来存地址的,你拿来存放20有什么用呢,编译器当然就不会理会你了。所以,在这里,我们需要把这句话改一下,让这个20变成是一个地址,而不是一个值。即用下面这句话,强制把20转换成内存地址:
int *q=(int *)20
这样一来,我们就编译通过了。注意,这里面还有一个小知识点,我们这里面的20表示的是10进制的数,那么转换成16进制时,应该是0x00000014,切忌切忌不要认为是0x00000020了。所以上面那个问题都问错了,呵呵。
编译通过之后,我们一运行,又发现错误了,程序运行着运行着就“自动结束了”,真的是很囧啊!难道我们写点东西就那么多错!!!!!没办法,我们只有用debug了~~~
打开debug,一句一句的调试,发现在程序的第13行就运行不下去而来,截了个图:
错误的大意就是“无法读取”~~~好奇怪,有什么是无法读取的呢?其实细想一下你就会发现,我们在程序的第9 行初始化了这个指针p,那么这个时候指针p中的值应该为0x00000000。但是我们需要注意的是,这个只是初始化,我们并没有指定这个指针应该指向什么地方!这就是问题的症结所在了。我们既然没有指定这个指针p应该指定什么地方,那么在调用*p的时候编译器又怎么会知道这个*想要取得的内容究竟是什么呢?所以自然就是“无法读取”了啊!
我们在这里需要特别注意的是,这个指针的初始化并不和我们常见的那种对变量的初始化是一样的。比如说,我们想要初始化一个变量i,那么只需要写上int i=0,那么在以后的任何地方我们想要调用这个变量的时候,它的值都是i。但是对于指针变量就不一样了。我们定义一个指针p,并利用程序中第9行的代码进行初始化。那么这句话仅仅表示的是这个指针变量p中的值是0x00000000,并不代表这个“*p”这个东东的值为0。因为这里面的*表示间接运算符,它的作用是取得p这个指针所指向的内存地址中的值。这个值就不见得是0了。我们只有在下面加上一句,p=&i。利用这句话,我们就可以把指针p中的值修改为i的内存地址,这样在调用*p的时候,它就指向的是i的地址,并取得其中的值10。
没办法,这里,我们就只有把这一行注释起来了。好了,程序继续,但是!(我最讨厌但是了~~~)但是,在程序的第26行,程序又运行不下去了,错误原因和上面一模一样,都是“无法读取”。见下图:
想一想原因,其实和上面那个错误的原因一样。我们在这里都只是把值赋给了q,但是并没有说*q是什么,所以编译器自然就找不到了。解决方法和上面一样,注释起来,嘿嘿~~~~~~
好了,现在程序终于通了,程序的输出结果如下图所示:
好了,从上面我们就可以看到,其实,想要对指针进行初始化,也有三种方法:
- 利用int *p=0;这个语句老老实实的将指针初始化了;
- 把int *p=0和p=&i这两句话连成一句话: int *p=&i;这样,就既把指针变量p初始化了,又把它指向了某个值;
- 如果非要用常量来初始化一个指针。那么就只有利用强制类型转换,找一个内存地址,利用(ElementType *) 来把常量转换成内存中的一个地址。既采用:
ElementType *p=(ElementType *)[常量]
p.s. 这里的ElementType指的就是变量的类型哈。
好了,到这个地方,我们基本上分析完了对指针进行初始化操作中的若干问题,最后,我们再把文章一开始提出的三个问题回答下:
- *p根本就不会输出任何东西,因为他不知道要输出什么;(如上面程序中的第13行)
- 当然可以,但是需要注意的是,这样就给指针修改这个变量的值的自由哦;(如上面程序中的第17行。比如我们在17行后面加上一句,*r=20;那么此时*r和i的值就不再是10了,就变成了20.这里注意到不要因为这个原因悲剧了哈~~~~~~这里面的机理请见我上一篇博文http://www.cnblogs.com/uniqueliu/archive/2011/07/14/2106681.html,里面有详细的分析)
- 首先20转换成16进制应该是0x00000014。另外,我们想要用这个常量初始化这个指针,不是不可以,而是需要进行强制类型转换,把int转换成(int *)。
好了,全部搞定了,写了好多,呼呼~~收工了……