数组和指针

 

1.从内存和编译器角度来理解数组

1.1内存角度和编译器角度

(1)定义五个变量,普通定义int a,b,c,d,e;和数组定义int a[5];

1.都是定义了五个变量,第一种方法定义的变量的内存地址不一定是连续的,第二种方法定义的元素一定是地址连续的。
2.对于编译器来说,定义数组和定义其他变量的本质都是一样的。

1.2来理解一些符号 a,a[0],&a,&a[0]分别和左值右值的关系

分析
1.(1)a做左值。数组名a做左值时表示数组的所有地址,比如:int a[5];数组名a做左值时表示的就是4*5个内存地址。我们在操作数组的时候只能操作其中的一个元素,不能整体操作。比如 a = {1,2,3,4,5};整体操作是不行的。需要注意的是:它和数组定义初始化int a[5]={1,2,3,4,5}是不同的。

(2)a做右值。数组名a做右值时,表示数组首元素的首地址,第一个元素可能占好多地址,在这里只表示第一个字节的地址。其类型是数组元素的类型的指针。而非数组指针。

2.(1)a[0]做左值,表示第一个元素的地址,当a[0] = 1;时,是把1写入到元素a[0]对应的那个内存地址中去。
(2)a[0]做右值时,test = a[0]; 表示把a[0]的值写入test的地址中去。

3.(1)&a不能做左值,a的地址是随机分配的,但是一旦分配之后,在程序执行的时候,a的地址已经确定,所以成了常量,常量不能做左值。
(2)&a做右值,表示,整个数组的首地址。也就是一个指向数组的指针,所以&a做右值时的类型是数组指针。也就是int (*)[]类型。

4.(1)&a[0]不能做左值,道理和&a一样,变成了常量。
(2)&a[0]做右值时,表示数组首元素的首地址。

总结:
(1)a,&a,&a[0]在数值上是相等的,但是类型和含义完全不同。
(2)a和&a做右值的区别。a代表数组首元素的首地址,类型是int* 。&a表示数组的首地址,类型是int(*)[];
(3)a和&a[0]做右值时都是表示数组首元素的首地址。

2.从数组和指针的角度理解数组

1.通过代码看数组和指针的关系

#include<stdio.h>

void main(void)
{
    int a[5] = {1,2,3,4,5};
    int *p = a;         //将数组名a的值赋值给指针p
                        //数组名a做左值时表示数组首元素的首地址类型是int*
                        //所以这里 a 和 p 类型一致
    //通过数组下标访问元素
    printf("通过数组下标方式:第2个元素是 %d\n",a[1]);

    //通过指针访问数组元素
    printf("通过指针方式:第2个元素是 %d\n",*(p+1));


}

  分析:

(1)数组名a做右值时表示数组首元素的首地址,当p = a后,p也保存了数组首元素的首地址,当p+1时,p+1就指向了数组的第二个元素的首地址,以此类推。这里注意一点,p只是保留了首地址(一个字节),而第一个元素是int类型,占4个字节,这时候就靠指针p的数据类型的作用了,一看p是int*类型的指针,自动会根据p保留的第一个字节的地址去补齐剩下的那3个字节的地址,当找到4个字节地址后再去读取该值。

(1.1)比如 int a[5];int * p = a;当p自增1时,其实是自增(1+sizeof(指针类型)),举例:当int * p;p实际上是自增4个字节,char * p;p实际上是自增1个字节。

(1.2)指针在进行运算的时候,指针自增多少关键看指针类型。这时候再来看&a和a。&a做右值是数组指针,当&a自增时,实际上是增加了一个数组sizeof(int) * 5= 20个字节。而 a 自增实际上是自增了一个sizeof(int)大小的字节。

(3)看上面的代码,int * p = a; 若是改成int* p=&a;会怎样?一步一步分析:&a做右值时,代表数组的首地址,其数据类型是int (*)[5],这就得出了结果,&a和p类型不符,编译器报错。

(4)再来回顾一下,数组元素地址是连续的。所以指针访问数组才能自增去寻址。

总结
(1)a,a[0],&a,&a[0],做左值和做右值的区别
(2)指针自增的含义
(3)指针类型的含义
(4)数组和指针的相似处

下次说说指针和类型转换的内容

 

posted @ 2016-06-17 21:17  薛定谔的小灯泡  阅读(94)  评论(0编辑  收藏  举报