关于指针、数组、常量等相关联的内容,常常有一些拗口又难以区分的概念,比如指针数组、数组指针、二重指针、二维数组、常指针、指向常量的指针、指针函数、函数指针、“函数指针类型”。本文给出了我自己对这些概念的理解,如有谬误之处,还望指正。
1、指针数组、数组指针、二重指针、二维数组
下面先看一段指针数组、二重指针、二维数组、数组指针的例子:
char chArray1[] = "Hello";
char chArray2[] = "world!";
//指针数组,PointerArray是个数组,它其中每个元素都是个指针
char *PointerArray[] = {chArray1, chArray2};
cout << PointerArray[0] << endl; //输出Hello
cout << PointerArray[1] << endl; //输出world!
//二重指针,可以指向指针数组
char **P2Pointer = PointerArray;
cout << P2Pointer[0] << endl; //输出Hello
cout << P2Pointer[1] << endl; //输出world!
//二维数组
char A2Array[][7] = {"Hello", "world!"};
cout << A2Array[0] << endl; //输出Hello
cout << A2Array[1] << endl; //输出world!
//数组指针,ArrayPointer本身是个指针,它可以指向数组(甚至是二维数组)
char (*ArrayPointer)[7];
ArrayPointer = &chArray2;
cout << *ArrayPointer << endl; //输出world!
ArrayPointer = A2Array;
cout << *ArrayPointer << endl; //输出Hello
++ArrayPointer;
cout << *ArrayPointer << endl; //输出world!
对于二重指针和二维数组的区分是很容易的,指针数组也相对好理解些,让人费解的是数组指针。下面着重讲一下数组指针的概念:
所谓数组指针,就是指向数组的指针。简单的形如:
char chArrary[7] = “Hello!”;
char *pch = chArrary;
这里pch指向数组chArrary首地址,这个很好理解。当然我们也会碰到下面的例子:
char chArray[7] = “Hello!”;
char (*chArrayPointer)[7];
chArrayPointer = &chArray; //数组指针chArrayPointer指向了chArray(“数组类型”变量)
这里chArrayPointer同样是一个数组指针,它指向了一个含7个char元素的类型的数组。
为了方便理解上面第二种情况的数组指针,我们引入一个“数组类型”的概念(这是从http://topic.csdn.net/t/20020607/12/785969.html借鉴过来的)。我们可以把数组本身当做一种数据类型来看待,例如int iArray[7]中的int [7]可以看做是一种“数组类型”(为了方便叙述和理解,我们用type [n]表示一种“数组类型”),我们称它为“含有7个int元素的数组类型”。之所以要强调元素个数和元素类型,因为我们是把int iArray[7]、int iArray[8]中的int [7]和int [8]当做两种不同的“数组类型”看待的,因为它们元素个数不一样。同样,int iArray[7]、char chArray[7]中的int [7]与char [7]也看着两种不同的“数组类型”,因为它们元素类型不一样。
另外,我们把数组名作为一个具体的“数组类型变量”。与一般定义变量的方式不同,这里我们是把变量名放在类型名和[n]之间的。有了这两个概念,从而就可以很容易地理解数组指针了。再看一遍上面的例子,应该容易理解点了吧?
数组指针既然是指针,可以指向其对应“数组类型”变量,当然也是可以指向其对应“数组类型”数组首地址的。这就是为什么可以把二维数组的数组名直接赋值给数组指针的原因了,还是举一个例子:
char A2Array[][7] = {“Hello”, “world!”};
char (*chArrayPointer)[7];
chArrayPointer = A2Array;
这里我们把二维数组A2Array当成一个“数组类型”的一维数组,该“一维数组”包含两个元素A2Array A2Array[0]和A2Array[1],而A2Array与&A2Array[0]相同,即该“一维数组”的首地址。从而当我们使用++ chArrayPointer时,chArrayPointer便指向该“一维数组”的第二个元素,即A2Array[1](”world!”)。
总结一下数组指针与指针数组的区别:
(1)、数组指针是数组的指针,指针数组是指针的数组;
(2)、数组指针是一个指针它指向一个数组,指针数组是一组指针的集合。
2、常指针、指向常量的指针
这两个概念还是很好区分的,常指针的指针本身为一常量,它一旦指向某个地址,就不能再指向别的地址,但可以通过它修改它所指向地址中内容的值;指向常量的指针,它所指向地址的内容为常量,不能通过它来改变它所指向地址的内容,但指针本身不是常量,所以可以让它指向别的地址,即可以改变它的指向地址。下面还是举一段例子:
char chArray1[] = "Hello";
char chArray2[] = "world!";
char *pch = chArray1;
pch[0] = 'M'; //Ok
const char *pcch = chArray1; //相当于char const *pcch
//pcch[0] = 'G'; //错误! pch为指向常量的指针,虽然给它赋了非常量数组的值,但是也不能通过它来修改数组中的值
char * const cpch = chArray1;
cpch[0] = 'N'; //Ok
//cpch = chArray2; //错误! pch为常指针,常指针不能改变指向,即赋初值后不能指向别的地址
这里再顺便提一下使用typedef和#define定义的指针类型加上const修饰词,定义指针的区别,举例如下:
char chArray1[] = "Hello";
char chArray2[] = "world!";
typedef char* pTdChar;
#define pDefChar char *
const pTdChar ptdch = chArray1;//相当于pTdChar const ptdch = chArray1;或char *const ptdch = chArray1;
//ptdch = chArray2; //错误! ptdch为常指针
const pDefChar pdefch; //相当于const char *pdefch;或char const *pdefch;
pdefch = chArray1;
pdefch = chArray2; //pdefch的指向可以变
pDefChar const cpdefch= chArray1;//相当于char *const cpdefch = chArray1;
//cpdefch = chArray2; //错误! cpdefch为常指针
3、指针函数、函数指针、“函数指针类型”
指针型函数:这个比较好理解,其实就是函数返回值为一个指针,一般格式如下:
数据类型 *函数名(形参表)
{
函数体
}
函数指针:指向函数的指针,可以用来指向函数的入口地址,从而便可以在程序中使用它来调用它所指向的函数。还是看一段简单的程序
int max(int iv1, int iv2)
{
return (iv1 > iv2 ? iv1 : iv2);
}
int main(int argc, char* argv[])
{
int (*pMaxFun)(int, int); //定义一个函数指针pMaxFun
pMaxFun = max; //让pMaxFun指向max函数的入口地址
cout << (*pMaxFun)(2, 3) << endl; //输出3
return 0;
}
这里定义了函数指针,顺便提一下“函数指针类型”(我不清楚c++中是否真有这个概念,但在网上看到有人这样用,并且觉得很好理解,所以这里也采用“函数指针类型”的这个概念)。先看下面的一个例子:
int max(int iv1, int iv2)
{
return (iv1 > iv2 ? iv1 : iv2);
}
int main(int argc, char* argv[])
{
typedef int (*pFunType)(int, int); // pFunType就是传说中的“函数指针类型”
pFunType pFunMax = max; //注意这两句与上面例子中的区别
cout << (*pFunMax)(2, 3) << endl; //输出3
return 0;
}
通过“函数指针类型”这一概念,我们还可以同理去理解1中所说的“数组类型”了,当然这里还是有所不一样的,举一个类似的例子:
typedef int iArrayType[5]; //定义了一个“数组类型”int [5]的别名为iArrayType
iArrayType iArray = {1, 2, 3, 4, 5};
for (int i=0; i<5; i++)
{
cout << iArray[i] << " ";
}
cout << endl;