在博客园看了篇文章,写的真好,仰慕,大家可以直接过去看:https://www.cnblogs.com/zhiheng/p/6683334.html
数组与指针
长期以来,在C/C++中,数组名和指向数组首元素的指针常量到底是以一种什么关系,一直困扰着很多人。很多地方,甚至是一些教科书中都在说,“数组名就是一个指向数组首元素的指针常量”。但事实是,这是一种错误的说法!我们可以在很多场合中把数组名看作一个指向数组首元素的指针常量,但绝不能将这两者当成同一个东西。
真实的关系
数组是数组,指针是指针,这是两种不同的类型。
数组既可以表示一种数据类型,也可以表示这种类型的一个对象(非面向对象之对象,下同),表示对象时也可以称之为数组变量,和其他类型变量一样,数组变量有地址也有值。
数组的地址就是数组所占据内存空间的第一块存储单元的编号,而数组的值是由数组所有元素的值构成。
数组名既不是指针,也不是指针变量,而是数组变量的名字,与数组名相对应的是指针变量的变量名,都是符号。
int a[10];
int *const p = a;
std::cout << sizeof(a); // 40
std::cout << sizeof(p); // 8
作为数组,a拥有可以存放10个int型数据的空间,可以将一个int型的值存储到a中任意一个元素中。但作为一个指针的p,只能存储一个地址。
sizeof
操作符可以获取到一种数据类型所占据的内存大小,指针类型在x64位机器上的大小是8,而数组类型的大小是所有元素大小之和,上例中即为10个int型的大小,是40。
误解从何而来
可以将数组名赋值给一个指针,而赋值后的指针是指向数组首元素的,这让数组名看起来确像一个指针。
直接输出数组名会得到数组首元素的地址,这让人们误以为“数组名的值就是数组首元素地址“,符合指针的定义。
数组名可以像指针一样运算,对数组的索引和指针的运算看起来也是相同的。
#include <stdio.h>
int main(){
int a[] = {1,2,3};
int * p = a;
printf("a:\%#x, p:%#x, &a[0]:%#x\n", a, p, &a[0]);
printf("*a:\%d, *p:%d, a[0]:%d, p[0]:%d\n", *a, *p, a[0], p[0]);
printf("*(a+1):\%d, *(p+1):%d, a[1]:%d, p[1]:%d\n", *(a+1), *(p+1), a[1], p[1]);
return 0;
}
输出:
a:0x5fcaf0, p:0x5fcaf0, &a[0]:0x5fcaf0
*a:1, *p:1, a[0]:1, p[0]:1
*(a+1):2, *(p+1):2, a[1]:2, p[1]:2
从 &a 与 &a[0] 说起
数组的地址和数组首元素的地址虽然值相同,但意义不同。
值相同是因为,一个变量无论在在内存中占据多大空间,它的地址总是该空间第一个内存单元的地址。而数组的元素依次连续分布在整块数组空间中,数组空间的第一个内存单元被数组首元素占据,必然也同时是数组首元素所占空间的第一块空间单元,所以数组的地址与数组首元素的地址相同。
意义不同是因为,数组地址代表了整块数组所占据的内存空间,而数组首元素的地址只代表了首元素所占据的空间。
&a 表示取数组的地址,其结果是一个指向该数组的指针,它可以赋值给另一个同类型的指针。
&a[0]表示取数组首元素的地址,其结果是指向该数组首元素的指针,可以赋值给另一个同类型的指针。
注意:指向数组的指针和指向数组首元素的指针是两种不同类型的指针。
#include <stdio.h>
int main(){
int a[]={1,2,3};
int (* pa)[3];
int * pi;
pa = &a;
pi = &a[0];
printf("&a=%#x, &a[0]=%#x\n",&a, &a[0]);
printf("pa=%#x, sizeof(a)=%d, pa+1=%#x\n", pa, sizeof(a), pa+1);
printf("pi=%#x, sizeof(a[0])=%d, pi+1=%#x\n", pi, sizeof(a[0]), pi+1);
return 0;
}
编译后运行,输如下:
&a=0x5fcaf0, &a[0]=0x5fcaf0
pa=0x5fcaf0, sizeof(a)=12, pa+1=0x5fcafc
pi=0x5fcaf0, sizeof(a[0])=4, pi+1=0x5fcaf4
我们发现,取数组地址(&a)得到的指针pa和取数组首元素(&a[0])得到的指针pi是两种不同类型的指针,pa是一个指向有三个int型元素的数组的指针,pi是一个指向int型对象的指针。虽然pi和pa的值相同,但所指的内存空间不同,pi所指的空间处于pa所指空间的内部,而且是内部最靠前的部分。pi和pa所指内存块的大小显然是不同的,因此我们看到pa+1并不等于pi+1。
由指针运算规则可知,pa+1的值就是pa所指空间的下一个空间的地址,所以pa+1的值就是pa的地址向后偏移一段后的地址,这个偏移量就是pa所指的数组a的大小,即12个内存单元。同样,pi+1的值是pi向后偏移4个单位(int型的大小)后的地址。
ps: 看到有些地方说地址就是指针,我觉得这个说法不对。地址就是地址,它是数据对象的一个属性,表明它在内存中的位置。指针本身也有地址,总不能说“地址的地址”吧?此外,指针不光带有地址信息,它还附带有所指对象的类型信息,这就是为什么指针知道如何准确的指向下一个地址。