- 论坛徽章:
- 2
|
回复 #11 wuexp 的帖子
假设i386上。
int* p = 0; 的布局是:
address of p: [0] , [0] , [0], [0]
int a[2] = {0}; 的布局是:
address of a :
[0],[0],[0],[0]; [0],[0],[0],[0] ;
指针占据4字节。
数组名,可以认为数组名不占空间,也可以认为数组名拥有整个数组的空间。
编译器不进行优化的情况下,通过指针访问元素: p[ i ]
需要首先从指针的地址address of p上的4个字节,得到指针的值 —— 数组的地址。
再根据指针的类型计算偏移 void* pe = (char*)p + sizeof(*p) * i。
然后访问: *(int*)pe
通过数组名访问元素: a[ i ]
少一个访问过程,address of a就是数组的地址。
下面的过程和指针相同。
所谓的数组退化为指针, 就是将address of a 的值,保存到一个指针变量中。
综上:
a, &a[0]的值是相同的, 都是 address of a。
要解释为什么 &a的值也是address of a 嘛……
int i = 0; 布局:
address of i : [0],[0],[0],[0];
&i 得到的就是 address of i。
T v; 布局 :
address of v : [??],[??], ...
&v 得到的也是address of v
对变量(或者object)取值,得到的地址,也可以理解为一个大小为1的数组的首元素的地址。
int a[2]; 可以理解为一个变量,一个数组变量。
对a取值, 得到的是这个变量的地址 —— address of a。 所以和 a, &a[0]是相同的。
这个地址又可以理解为一个大小为1的数组的首元素地址,该数组元素类型是int [2], 如果+1, 偏移就是sizeof( int[2] )。
这样可能会清晰一些:
typedef int iarr2[2];
iarr2 a;
iarr2* p = &a;
p[ i ] 是 (iarr2*)( (char*)p + sizeof(iarr2) * i )
其实我是通过汇编代码才真正了解指针和数组名的区别的。
要想不通过汇编就理解这2者的区别, 多少有点过于抽象……
写过一个 指针/数组 & 声明/定义 4种组合都不会错的程序 —— 不涉及汇编 —— 应该对非汇编理解这个问题有帮助。
不过不知道丢哪去了…… 我找找看…… |
|