数组指针与指针数组及指针与数组的辨析

指针数组:首先它是一个数组,数组的元素都是指针,数组占多少个字节由数组本身决定。它是“储存指针的数组”的简称。

数组指针:首先它是一个指针,它指向一个数组。在32 位系统下永远是占4 个字节,至于它指向的数组占多少字节,不知道。它是“指向数组的指针”的简称。

下面哪个是数组指针,哪个是指针数组呢:
int *p1[10];
int (*p2)[10];

这里需要明白一个符号之间的优先级问题:

int *p1[10] : “[]”的优先级比“*”要高。p1 先与“[]”结合,构成一个数组的定义,数组名为p1,int *修饰的是数组的内容,即数组的每个元素。那现在我们清楚,这是一个数组,其包含10 个指向int 类型数据的指针,即指针数组

int (*p2)[10]: 在这里“()”的优先级比“[]”高,“*”号和p2 构成一个指针的定义,指针变量名为p2,int 修饰的是数组的内容,即数组的每个元素。数组在这里并没有名字,是个匿名数组。那现在我们清楚p2 是一个指针,它指向一个包含10 个int 类型数据的数组,即数组指针。下面这幅图可以帮助我们很好的理解他们:

                 


2.理解数组名和数组指针变量

看下面的代码:

扫描二维码关注公众号,回复: 3731768 查看本文章
int main()
{
   char a[5]={'A','B','C','D'};
   char (*p3)[5] = &a;
   char (*p4)[5] = a;
   return 0;
}
&a 是整个数组的首地址,而a是数组首元素的首地址,其值相同但意义不同,这在上一篇博文中已经讲到过。p3 这个定义的“=”号两边的数据类型完全一致,都是指向char型数组的指针,而p4 这个定义的“=”号两边的数据类型就不一致了。左边的类型是指向整个数组的指针,右边的数据类型是指向单个字符的指针。在Visual studio 2010编译器中无法编译成功。

再看下面这段代码:

int main()
{
	int a[5] = {0,1,2,3,4};
	int (*p)[5] = &a;
	cout<<&a<<endl;
	cout<<&a+1<<endl;
	cout<<a+1<<endl;
	cout<<p<<endl;
	cout<<p+1<<endl;
	return 0;
}
这里面的a,&a,&a+1,a+1,p,p+1究竟是指向哪里呢?

上面代码的输出结果为:

&a:      001EFC9C

&a+1:  001EFCB0

a+1:    001EFCA0

p:        001eFC9C

p+1:    001EFCB0

分析结果可得出:

a: 是一个数组名,类型是指向int型整数的指针,不是变量,a的值是指针常量,即不能有a++或者a=p这些操作。a指向这块连续空间的首地址。

&a: &a是指向这个一位数组的首地址。

&a+1: 前面我们讲过,指针加上一个数,并不是真是的加上这个数,而是加上这个数乘以指针类型多占的空间,即 sizeof(&a),而&a占据了20byte(5*sizeof(int)),所以&a+1得到了如上的结果。

a+1:a在直接做加法运算时转化成了指针变量,而不再时数组名,此时a代表的含义是指向数组首元素的地址,因此a+1,实际是在a地址的基础上加了sizeof(a),而这里的a的类型是指向int型的指针,大小是sizeof(int)。

p: 是一个数组指针变量,有点类似于数组名的概念,不同的是它是变量,可以执行p++;p=a等操作。

p+1:这就很好理解了,p是指向数组的指针,因此p+1其实就是p的地址加上sizeof(&a),等同于&a+1。


再看看下面这段代码:

int main ()
{
	int a[2][2] = {1,2,3,4};
	int (*p)[2];
	p = a;
	cout<<&a<<endl;               // 001CFCC0
	cout<<&a[0]<<endl;            // 001CFCC0
        cout<<a[0]+1<<endl;           // 001CFCC4
	cout<<&a[1]<<endl;            // 001CFCC8
	cout<<p<<endl;                // 001CFCC0
	cout<<(a+1)<<endl;            // 001CFCC8
	cout<<(&a+1)<<endl;           // 001CFCD0 
	cout<<*(a+1)<<endl;           // 001CFCC8
        cout<<**(a+1)<<endl;          // 3
	cout<<(p+1)<<endl;            // 001CFCC8
	cout<<*(p+1)<<endl;           // 001CFCC8
        cout<<**(p+1)<<endl;          // 3

	return 0;
}
首先我们认识到a时一个二维数组,其实也就是一个特殊的额一位数组,只不过其每一位上的元素又是一个数组。

还要注意的是这里p跟上面那段代码里一样,是一个指向int型数组的指针,但是不一样的是,对p进行初始化的语句似乎有所改变,上面那段代码是这样的p = &a,而这里直接是 p = a,也可以这样 p = &a[0];其实这种改变很容易理解,一切看类型,p是指向一位数组的指针,而在上一段代码中,a就是表示的一位数组的数组名,因此p = &a是合适的,而在这段代码中,a是二维数组,直接取地址p = &a是不妥的,类型上不匹配,而p = a,此处的a代表的是二维数组的第一个元素,也就是一位数组,这样就合适了,同理p = &a[0] 其实是显示的指出p指向的是二维数组的第一个元素。

有了上面的解释,这里代码的输出应该很好理解了。

a是一个数组名,类型是指向一维数组的指针,不是变量,a的值是指针常量,即不能有a++或者a=p这些操作。a指向这块连续空间的首地址,值是&a[0][0]。

a[0]是一维数组名,类型是指向整型的指针,值是&a[0][0],这个值是一个常量。

a[1]是一维数组名,类型是指向整型的指针,值是&a[1][0],这个值是一个常量。

p是一个数组指针变量,指向一维数组的指针变量,值是&a[0][0]。可以执行p++;p=a等操作。

a+1表示指向下一行元素,也可以理解为指向下一个一维数组。

*(a+1)是取出第一行的首地址。

a[0]+1是指向第0行第1个元素,也可以理解为指向一维数组a[0]的第一个元素。

p+1同a+1

*(p+1)同*(a+1)

虽然a跟a[0]值是一样,但类型不一样,表示的意义不一样。


3 数组名与数组指针变量的区别

 

从以上分析中得出数组名是指针,类型是指向元素类型的指针,但值是指针常量,声明数组时编译器会为声明所指定的元素数量保留内存空间。数组指针是指向数组的指针,声明指针变量时编译器只为指针本身保留内存空间。


再看下面这段代码

int main()
{
	int a[2][2] = {1,2,3,4};
	int (*p)[2];
	p = a;
	cout<<sizeof(a)<<endl;       // 16
	cout<<sizeof(p)<<endl;       // 4
	cout<<sizeof(a+1)<<endl;     // 4
	cout<<sizeof(p+1)<<endl;     // 4
	cout<<sizeof(a+0)<<endl;     // 4
	cout<<sizeof(p+0)<<endl;     // 4
        cout<<sizeof(a[0])<<endl;    // 8
}
1. 当sizeof用于数组名时,返回整个数组的大小(这里的大小指占用的字节数)。p是一个指针变量,这个变量占用4个字节。而a是数组名,所以sizeof a返回数组a中的全部元素占用的字节数,因此输出16。

2. 从结果中看出,a在做+运算时是转化成了指针变量,此时a+i的类型是一个指针变量,而不是一个数组名。但a[i]是一个一维数组的数组名,sizeof(a[0])的值是8。

再看下面这段代码

void f(int a[][2])  
{  
	cout<<sizeof a<<endl; 
}  
void main()  
{  
	int a[2][2]={1,2,3,4}; 
	cout<<sizeof a<<endl;      // 16
	f(a);                      // 4
}
这是因为传参的时候数组名转化成指针变量,注意到函数f中f(int a[][2])这里并不需要指定二维数组的长度,此处可以改为int (*a)[2]。所以传过来的就是一个数组指针变量。


本篇博文部分内容出自以下博客,写的都很好:

http://c.biancheng.net/cpp/html/476.html

http://blog.csdn.net/touch_2011/article/details/6966980


个人水平有限,如有错误,敬请指正!



猜你喜欢

转载自blog.csdn.net/dby3579/article/details/52012367