数组里a和&a的区别

来源:互联网 发布:vr培训机构 知乎 编辑:程序博客网 时间:2024/05/17 01:08

#include<stdio.h>
int main(void)
{
 int a[5]={1,2,3,4,5};
 int *ptr=(int *)(&a+1);                      //&a代表整个数组的地址,+1应该加上sizeof(a)的长度,所以ptr指向a[5]位置处。
 printf("%d %d\n",*(a+1),*(ptr-1));   //a代表数组首元素的地址,+1应该加上sizeof(a[0]),所以a+1指向a[1]处。
 return 0;                                         //输出是  2   5
}


  对指针进行加1 操作,得到的是下一个元素的地址,而不是原有地址值直接加1。所以,一个类型为T的指针的移动,以sizeof(T)移动单位。因此,对上题来说,a 是一个一维数组,数组中有5 个元素;ptr  是一个int  型的指针。
  &a+1:取数组a的首地址,该地址的值加上sizeof(a)的值,即&a+5*sizeof(int),也就是下一个数组的首地址,显然当前指针已经越过了数组的界限。 
  (int *)(&a+1): 则是把上一步计算出来的地址,强制转换为int * 类型,赋值给ptr 。 
  *(a+1): a,&a 的值是一样的,但意思不一样,a 是数组首元素的首地址,也就是a[0]的首地址,&a 是数组的首地址,a+1 是数组下一元素的首地址,即a[1]的首地址,&a+1 是下一个数组的首地址。所以输出2。 
  *(ptr-1): 因为ptr是指向a[5],并且ptr是int * 类型,所以*(ptr-1)  是指向a[4],输出5。


==============================================================

a与&a,数组与指针的恩恩怨怨

#include "stdafx.h"
#include <conio.h>

int _tmain(int argc, _TCHAR* argv[])
{
char a[12] = {'A','B','C','D','E','F','G','H','\0'};
char * p = a;
printf("The content of a    %s\n", a);
printf("The content of &a    %s\n", &a);
printf("The content of p    %s\n", p);
printf("**************************************************\n");
printf("The value of a     %x\n", a);
printf("The value of &a     %x\n", &a);
printf("The value of p     %x\n", p);
printf("**************************************************\n");
printf("%c\n", *(a));
printf("%c\n", a[1]);
printf("**************************************************\n");
printf("%d\n", sizeof(a));
printf("%d\n", sizeof(&a));
printf("%d\n", sizeof(p));
printf("%d\n", sizeof(*p));
printf("**************************************************\n");
char (*q)[12] = &a;
char (*i) = a;           //类型一致,OK。左边为“char *”,右边也为“char *”
//char (*q) = &a;       //error C2440: “初始化”: 无法从“char (*)[12]”转换为“char *”
//char (*q)[12] = a;   //error C2440: “初始化”: 无法从“char [12]”转换为“char (*)[12]
i = a + 1;
q = &a +1;

printf("%s\n", i);
printf("%s\n", q);
getch();
return 0;
}


上图,有图有真相!

图1(最终输出结果)



图2(a与&a的区别)



图3 ((i=a+1)、(q=&a+1)之前)



图4:((i=a+1)、(q=&a+1)之后))



总结
1.a、&a、p都指向数组首元素的首地址,这点可以从输出他们的值是一样的看出。

2.指针和数组的2种访问形式:以指针的形式访问和以下标的形式访问数组(以指针的形式访问和以下标的形式访问指针)。
3.数值名a有2层含义:

(1).作为数组名,代表数组,类型为char [12](图1)。

(2).作为数组首元素的首地址(a+1,跨度为数组元素的类型的长度),类型为char *。(从图1和图2的对比可以看出)。
4.&a的类型为char [12]*,代表整个数组(&a+1,跨度为整个数组的长度)。(从图1和图2的对比可以看出)

5.i和q的输出结果的说明:i在字符'H'后即是'\0',所以输出结束;q数组越界,所以乱码。

6.对"图1"的说明:sizeof(*p),p的声明是"char * p",p所指向的内容是一个字符(即p[0]),所以sizeof(*p)当然是1了。&a的类型为char[12]*,是一指针,长度当然为4。




==============================================================

数组名,数组首地址,a,&a,&a[0]

(1)指针数组: 是数组,但数组中的每个元素都是指针。
int *p[5];     //如p[2]是指针,可*p[ 2]=3;

(2)指向数组的指针: 是个指针,但它指向的是一个数组。
int a[5];
int (*p)[5];  //与前一行比较,*p相当于a,即p=&a。 就像:int m;int *pm; //*pm就相当于m,pm=&m。
p= &a;       //可与前一行合并为int (*p)[5]=&a。

a代表这个数组,它是一个指针,指向第一个元素。
这里一定要记住,a是数组名,&a代表的不是取a这个变量的地址,而是取数组元素的地址。
---------------------------------------
a     的类型是 int[5]          数组
&a   的类型是 int(*)[5]      指针:指向int[5]数组的指针
&a[0] 的类型是 int *         指针:指向int类型的指针

sizeof(a)=20;
sizeof(*a)=4;      //因为有取值符*,表示把a当成一个指针(int*),而a指向数组的首地址。
                          //即a=&(a[0]),即sizeof(*a)=sizeof(*&(a[0]))=sizeof(a[0])=sizeof(int)=4。
sizeof(*&a)=20; //因为p=&a,所以=sizeof(*p),而*p=a,所以=sizeof(a)=20;
---------------------------------------
int a[5]={1,2,3,4,5};
int *ptr=(int *)(&a+1);
printf("%d,%d",*(a+1),*(ptr-1));  //输出:2,5

指针加1要根据指针类型加上一定的值,不同类型的指针+1之后增加的大小不同,指针只是一个内存地址,但指针指向地址的长度可能不同。

如char *pc与int *pi,sizeof(pc)=sizeof(pi)=4,但为什么输出时,cout<<*pc只从内存处取一个字符,而cout<*pi则从内存处取4个字符,这是由指针类型(char,int)决定的。

对A* p;p+1=(A*)(p的地址值+sizeof(A)),如pc+1=pc+sizeof(char)=(char*)(pc的地址值+1个字节),而pi+1=pi+sizeof(int)=(int*)(pi的地址值+4个字节)。

对代码中的&a+1,&a是数组指针,其类型为int (*)[5],因为p=&a,也即是p的类型,所以&a+1=&a+sizeof(A),其中A为int[5]:(把A=int[5]代入A* p,即相当于int(*p)[5])。所以&a+1=&a的地址值+5*4字节,即变为数组a的结束地址的下一个地址(即&a[5]),&a+1仍是int (*)[5]类型,经(int *)(&a+1)强制转化为int*,赋值给ptr。后面的ptr-1=ptr-sizeof(int)=ptr的地址值-4个字节,即变为数组a的最后一个元素的地址&a[4],*(ptr-1)即为a[4],故输出为5。
而a+1=a+sizeof(int):(此处a退化为int*,故对于A* p,A为int) =a的地址值+4个字节,即是&a[1],则*(a+1)即a[1],故输出2。

---------------------------------------

又如:
double t[]={1, 2, 578, 111,90} ;
double *pt= &t[3];//指向值为111
int *ptInt= reinterpret_cast< int * > (pt);//reinterpret_cast强制类型转换。
char *ptCh= reinterpret_cast< char * > (pt);
cout<< *( pt- 1)<< "\t"<< *(reinterpret_cast<double*>(ptInt-2))<<"\t"<<
*(reinterpret_cast<double*>(ptCh-8));//最后输出结果全为578。
---------------------------------------
void Fun( int *p)与void Fun( int p[])是一样的,即函数列表中的数组此时退化成了指针。





==============================================================

数组名a&a的区别


   这里我们先看看数组名代表的是什么,这个概念可能大家有所误解,认为数组名代表的就是数组的地址,当然,数组名代表的是一个地址,但是关键是,通过这个地址,我们关注的是它能取得多大空间的数据的值,例如对于一个char类型的地址,我们能够取得一个字节的值,对于一个int型的地址,我们能够取得4个字节的值。

   这里我们以整形数组为讲解:int a[4];

   a是一个地址,我们知道a其实本来的面目应该是:a+0,只不过这个0我们就省了,*(a+0)是什么呢,这个代表的就是取得第一个元素的内容,也就是a[0],所以我们可以知道a它代表的应该是第一个(下标为0)元素的地址。

   这里我们先从另外一个方面来理解什么是数组,其实我们可以把数组假设为一个基本类型变量,它的类型为 int [4].也就是变量a这个对象代表的就是4int那么大小的一个区间,而且不可分割。它和一般变量不同的地方在于,它的操作受限,不能像一般变量那样,直接通过赋值符号一次性将整个区间的值赋值给另外一个相同的数组变量。而只能通过下标来一个个元素的赋值。

   通过上面的分析,&a就好理解了,他就是变量a的地址,这个变量a的作用范围是四个int空间的数据,也就是如果将&a赋给某个变量pp的类型应该是:int (*)[4];也就是指向包含4int数据的数组的指针。

   再来看看a+1&a+1的区别,a+1表示的是数组第一个元素地址,&a+1表示的是跨过a数组的下一个地址。通过图来表示可能更加清晰可理解:


   其实&aa的值倒是完全一样的,都是下标为0的元素的地址,关键在于它们的作用域不同,在网上看到一个比喻觉得挺恰当的,长沙在长沙市来说是中心,同时在湖南省来说,也是湖南省的政治中心,长沙即作为市的中心,也作为省的中心,但是在不同的范围内,表达的意思是不一样的。

 



0 0