指针在c/c++里属于难理解的知识点,在学习指针之前先来回顾一下变量的知识点,我们知道数据存储在计算机里有3个基本属性(1.数据存储在何处,2.存储的值是多少,3.存储的数据是什么类型)必须要跟踪。下面用一个整形和一个字符串型来做例子,代码如下:
// pointer.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include#include int _tmain(int argc, _TCHAR* argv[]) { using namespace std; int i=3; string str="zdsoft"; cout<<"i="< 代码编译后运行如下图(图1):
在使用变量时我们常常不会关心变量的地址是多少 ,但不要忽略它的存在。可以在变量前面使用取地址运算符(&),来获得变量的地址。代码如下:
// pointer.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include#include int _tmain(int argc, _TCHAR* argv[]) { using namespace std; int i=3; string str="zdsoft"; cout<<"i="< 代码编译后运行如下图(图1):
变量的地址是系统给的,不同的系统分配变量的地址的方式可能不一样,这个可以不用关心。
接下来我们正式解析指针,前面说了变量也有地址,在变量前面使用取地址运算符& 就可以得到变量的地址。
那么指针到底是什么呢?它和地址有什么关系。用一句通俗点的话应该怎么概括或定义指针。
初学者难理解指针,我感觉至少有部分原因是因为在文字上对它进行了一次封装。也许这样说比较好理解:指针不是针,它也是变量,只不过是一种特殊的变量,是一种只能存放地址的变量。
因此,指针名表示的是地址。大部分时候我们目的不是想只知道地址,而是要得到地址里放的值或者说数据。在指针前面使用*运算符可以得到该地址处里的数据,感觉上就像是需要中间人介绍一样,因此*运算符称为间接值运算符或解除引用运算符。
下面用代码来演示一下这个原理,具体代码如下:
// pointer.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include#include int _tmain(int argc, _TCHAR* argv[]) { using namespace std; int i=3; string str="zdsoft"; int* pInt; //声明一个指针 pInt=&i; //把i的地址赋值给pInt指针 cout<<"i="< 代码编译后运行如下图(图3):
通过上面的文字解析,再加上代码和代码运行结果图 ,相信大家能够对指针有个基本的认识了,以及指针和变量的不同之处。上面的代码还演示了如何声明一个指针。从上面图3中发现指针pInt里地址指向的值是变量i的值。(这里强调是“指向”,而不是把变量i的值复制过来)。为了证实这一点,只需要对变量的值进行修改后来看看指针里地址的值是否也跟着变了。接下来我们用代码来验证一下:
// pointer.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include#include int _tmain(int argc, _TCHAR* argv[]) { using namespace std; int i=3; string str="zdsoft"; int* pInt; //定义一个指针 pInt=&i; //把i的地址赋值给pInt指针 cout<<"i="< 代码编译后运行如下图(图4):
反之,修改*pInt,变量i的值也同样被改变。总结:*pInt等于i,*pInt和 i 是同一个东西的两种叫法 。
从上面指针的声明上可以看到指针也是有类型的。我们知道变量的类型就是变量里值的类型,可是前面说过指针是只能存放地址的变量。既然只能存放地址,而地址又都是整数型(这里一般用16进制表示),难道指针只有int类型?如果你这样想那肯定是理解思路的方向被误导了。在这一点上指针变量和普通变量的区别就出来了。正确的理解方向是这样的:指针的类型和存放在指针里的地址没有关系,而是和该地址里的值有对应的关系。即,int类型指针存放的地址,该地址里必须是int类型的数据。为了证实这一点,我们把上面代码里变量str的地址放到指针pInt里,看看会发生什么。代码如下图(图5):
看到没有,直接提示错误:不能将string*类型的值分配到int*类型的实体。
那么再声明个string类型指针,把变量str的地址放到里面看看,代码如下:
#include "stdafx.h" #include#include int _tmain(int argc, _TCHAR* argv[]) { using namespace std; int i=3; string str="zdsoft"; int* pInt; //声明一个类型为int的指针 string* pStr; //声明一个类型为string的指针 pInt=&i; //把变量i的地址赋值给pInt指针 pStr=&str;//把变量str的地址赋值给pStr指针 cout<<"变量i的地址="<<&i< 编译后运行结果如下图(图6):
顺便说一下声明指针的格式,我上面用的格式是C++程序员常用的格式 int* pVar,但是C程序员习惯上喜欢把星号和指针名挨在一起 int *pVar。其实这只是一种书写习惯,你甚至可以星号前后都不要空格 int*pVar。但是如果多个指针名声明时写在一行的话,必须在每个指针名前面都要加上一个*号,像这样: int * pVar1, * pVar2; 而如果写成了这样:int * pVar1, pVar2; 就成声明了一个指针(pVar1)和一个变量(pVar2)。
指针在声明时同样可以初始化,上面声明指针的代码可以写成这样:
int* pInt=&i; string* pStr=&str;此时指针pInt是变量i的地址,指针pStr是变量str的地址。
至此,如果你认真的看完了上面的文章,应该对指针有了初步的认识了。但是,如果你认为自己这就会用指针了,那就大错特错了,这只是初步了解。我们离全面了解还有很大一段距离。
使用指针时容易发生的危险,在C++中创建指针时,计算机只会分配用来存储地址的内存,但不会分配用来存储指针所指向数据的内存。为数据提供空间是一个独立的步骤,忽略这一步无疑是自找麻烦,比如下列代码:
// pointer.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include#include int _tmain(int argc, _TCHAR* argv[]) { using namespace std; int* pInt; *pInt=123; cout<<"pInt="< 编译时没有任何问题,但运行时程序就会崩溃。如下图(图7)
从上面代码看,pInt确实是一个指针,但是它指向哪里呢?没有将地址赋值给指针pInt,那么123将被放在哪里呢?由于指针pInt没有被初始化,它可能是任何值。不管值是什么,程序都将它解释为存储123的地址。如果pInt的值恰巧是程序代码的地址,那么程序代码将被改成乱码了,导致程序崩溃。这种错误可能导致最难跟踪的BUG。
总结:一定要在对指针使用解除引用运算符(*)之前,将指针初始化为一个确定的,适当的地址。这就是程序员的清规戒律。
未完,待续。。。



