指针详解系列4-指针与数组
数组名实际上就是第一个元素的地址,一维数组名的值是一个指针常量。
如果数组a是一个一维数组,那么第一个元素的地址就有两种表示方法:a或者&a[0].第二个元素也有两种表示方法:(a+1)或者&a[1],所以数组元素a[i]的地址既可以表示为&a[i]又可以表示为(a+i)(回顾一下指针的运算)。
a[i]和*(a+i)都可以访问数组元素。
如果定义了一个指针ptr,并让它指向某一个数组a的第一个元素(假定指针指向的类型和数组元素的类型相同),那么就可以像数组一样使用这个指针名字,ptr,a,&a[0],&ptr[0]均表示数组的第一个元素。
对数组名不能赋值,但是对指针名字可以赋值,下面这两个赋值语句是等价的:
ptr= &a[i];
ptr= a + i;
上面我们说明了用指针访问数组的用法,反过来我们也可以用数组的方式来访问指针。比如
i = ptr[0];
i = ptr[10];
*(ptr+i)的访问可以简化为ptr[i],这充分说明在处理一位数组的问题上,编译器就是把它作为指针的方式来处理的。
下面的例子简单的说明了指针和一位数组直接的互换关系:
void TestPointer3(void)
{
char a[] = "0123456789";
char *ptr;
DebugPrintf(" \r\n");
ptr = a;
DebugPrintf("a[1] = %c,ptr[1] = %c\r\n",a[1],ptr[1]);
DebugPrintf("a[2] = %c,ptr + 2 = %c\r\n",a[2],*(ptr+2));
}
输出结果如下:
如果是二维或者更多维的数组呢?
一个n维数组实际上就是一个n-1维数组的一维数。
比如a是一个二维数组:
- a[i][j]表示该数组的i行j列。
- a可以看作一个一维数组,只不过包含i个元素,每一个元素恰好包含j个数据而已
- a[i][0]就表示该数组的第i行的头一个元素的地址
- 而a就表示第一行的地址,等同于a[0][0],a[i]就可以表示为*(a+i),从而a[i][j]就可以表示为((a+i))[j],这个又可以看为一位数组的引用(数组类型是(a+i)),进一步可以表示为*(((a+i))+j),即(a+i)+j.
下面的四个表达式完全等价:
a[i][j]
((a+i))[j]
*(a[i]+j)
(((a+i))+j)
测试代码如下:
void TestPointer3(void)
{
char a[][3] = { {0,1,2},
{4,5,6},
{7,8,9}};
char c;
DebugPrintf(" 二维数组和指针:\r\n");
c = a[1][2];
DebugPrintf("a[1][2] = %d\r\n",c);
c = (*(a+1))[2]; //a+1表示第二行的首地址
DebugPrintf("(*(a+1))[2] = %d\r\n",c);
c = *(a[1]+2);
DebugPrintf("*(a[1]+2) = %d\r\n",c);
c = *((&(a+1))+2);
DebugPrintf("*((*(a+1))+2) = %d\r\n",c);
}
输出结果表明确实一致;
如果ptr是一个指向一位数组的指针,这个一位数组大小与a的第二维相同,并且具有相同的数据类型,下面的定义是等价的:
int a[10][50];
int (*ptr)[50]; //声明一个指向数组类型的指针
然后我们就可以这样初始化ptr:
int (*ptr)[50] = a;
如果函数参数需要传入一个一维数组或者二维数组,可以如下声明:
void Func(int p[])等价于void Func(int *p)
void Func(int p[][10])等价于void Func(int (*p)[10])
你可以声明一个指针数组,和其他的数值的唯一差异,就是数组的元素是指针。
比如:char *pchar[10];
特别建议:
上面的内容展示了常见的和一些非常见的数值和指针的使用方法,其用途是方便你深入理解数组和指针的关系及其本质,但是,我提出以下来自实战的忠告:
- 不要使用二维以上的数组,那将使你的程序变得晦涩难懂,可读性和维护性急剧下降。
- 不要使用那些不常见的方式来来访问数组,可能到最后你自己都会被绕晕的。
- 数组的访问,在对性能没有特别的要求情况下,使用下标方式访问会更加易读易懂。
原创文章,欢迎转载,请注明来源,未经书面允许,请勿用于商业用途。