指针是C语言的一大特色,它就像一把双刃剑:使用得当能够给程序带来极大的灵活性和高效性;反之,程序就会变得难以调试,漏洞百出!
众所周知,指针实质就是地址!一个变量的地址即称为此变量的“指针”。如果有这样一种变量:它的存储单元里存放的是其它变量的地址!我们就称之为“指针变量”。(请注意两者之间的区别:两个完全不同的概念!)
我们都知道,数组名和函数名就是它们的入口地址。同理,一个变量名其实也是此变量的所在地址!C语言中有一种运算符为“&”:取址运算符。因为数组名与函数名本身代表的就是地址,通常不会对并且也不能对它们进行取址操作或其它运算操作(其实对于函数名的直接引用与对它取址是等价的)。这也是它们被称为“常量”的原因!但对于一个变量来讲,情况就不一样了。要想获得它的地址,就必须进行“&”运算,尽管它本身表示的也是地址值!而对变量直接进行引用得到却是它所在的内存单元的数据内容!“指针变量”作为一种变量当然也不能例外!只不过它与其它普通变量的差别是,它的内容是其它变量(包括“指针变量”)的地址,在WIN32上,它的大小恒为32位,4BYTE。而普通变量则不会有大小上的限制!对指针变量所指向的地址的数据内容的获取则是通过操作符“*”。在理解上我们将“提领操作符*”视为类型的一部分,并且这种数据类型是一种变量地址类型(均对每一个“*”而言)!
只要明白了以上常识,“指针”将不会再是程序设计中的“拦路虎”!
从内存的存储映象的角度来讲,C的规则数组(不包括通过数据结构设计的多维数组)不存在多维,也就是说所有的数组本质上都是一维的,而一级指针就等价于一维数组!关键的不同在于多维数组与一维数组语义上的差别!而我们理解多维数组通常将之形象地描述成“矩阵”形式。更为精确的理解是多维数组的每个元素就是一个数组,如此递归下去直至最后每个元素是一个简单的变量类型,最终得到的就是一个特殊的一维数组!
看如下一个例程:
#include<stdio.h>
#include<stdlib.h>
voidmain()
{
inta[][3]={{250,250,250},{250,250,250}};
int*p=(int*)a;-------------------@
}
注:语句@在.C文件中可以写成int* p=a;但会给出警告;若是在.CPP文件中写成int*p=a;是通不过编译的!所以规范起见,最好在任何时候都要进行强制类型转换!
通过进行调试,在WATHCH窗口中查看变量的内存地址情况如下:
从图上可以看出二维数组在内存单元中是线性增长的。倘若此时有一个二级指针int** q=NULL;如何通过q来操作二维数组a[2][3]呢?
通过q=a;如何呢?在.C文件中可以编译通过,但会给出警告。若是在.CPP文件中则不会编译通过!我相信很多人的第一反应是加上强制类型转换:q=(int**)a;如此以来,程序编译、链接畅通无阻,连警告也没有!但一运行就会出问题:这是当然的!下面进行详细分析。。。。。。
cout<<typeid(a).name()<<endl;
cout<<typeid(pp).name()<<endl;
输出结果为:int(*)[3](换行)
两者完全相同,与设想一致!