1.1 数组指针
指针数组和数组指针常常让人感到混淆。
从词性的角度理解。指针数组是指针修饰数组,强调的是数组。数组指针是数组修饰指针,强调的是指针。
从C语法定义来看:二者定义的语法不同。如果int (*p)[5] 不加括号,就变成了指针数组int *p[5]。
int main() {
int array[5] = { 1, 2, 3, 4, 5 };
int* arr[5]; // 指针数组。数组arr中存储的都是int型的指针
int (*p)[5]; // 数组指针p。指针p指向一个长度为5个int的数组首地址
p = (int(*)[5])arr; // 数组指针p指向arr的首地址
printf("%d %d\n", p, *p);
return 0;
}
输出:
11533108 11533108
从汇编代码看:注意默认是cdel调用约定,printf的参数是从右往左压栈。可以看到p和*p的值都是来自[ebp-44h]。因此对于cup而言,二者是没有区别的。
我认为p和*p是编译器用于某种区分的。
printf("%d %d\n", p, *p);
006450D8 8B 45 BC mov eax,dword ptr [ebp-44h]
006450DB 50 push eax
006450DC 8B 4D BC mov ecx,dword ptr [ebp-44h]
006450DF 51 push ecx
006450E0 68 DC 7B 64 00 push 647BDCh
006450E5 E8 C7 C2 FF FF call 006413B1
006450EA 83 C4 0C add esp,0Ch
验证我的假设
int main() {
int arr[5] = { 1, 2, 3, 4, 5 };
int(*p)[2] = (int(*) [2])arr;
printf("%d %d\n", p, *p);
printf("%d %d %d\n", p, p+1, *(*(p+1)));
printf("%d %d %d\n", *p, (*p)+1, *(((*p)+1)+0));
return 0;
}
输出:
5241652 5241652
5241652 5241660 3
5241652 5241656 2
printf("%d %d %d\n", p, p + 1, *(*(p + 1)));
00D450ED B8 04 00 00 00 mov eax,4
00D450F2 6B C8 00 imul ecx,eax,0
00D450F5 8B 55 D8 mov edx,dword ptr [ebp-28h]
00D450F8 8B 44 0A 08 mov eax,dword ptr [edx+ecx+8] ; +8
00D450FC 50 push eax
00D450FD 8B 4D D8 mov ecx,dword ptr [ebp-28h]
00D45100 83 C1 08 add ecx,8 ; p + 1 --> +8
00D45103 51 push ecx
00D45104 8B 55 D8 mov edx,dword ptr [ebp-28h]
00D45107 52 push edx
00D45108 68 E4 7B D4 00 push 0D47BE4h
00D4510D E8 9F C2 FF FF call 00D413B1
00D45112 83 C4 10 add esp,10h
printf("%d %d %d\n", *p, (*p)+1, *(((*p)+1)+0));
00D45115 8B 45 D8 mov eax,dword ptr [ebp-28h]
00D45118 8B 48 04 mov ecx,dword ptr [eax+4] ; +4
00D4511B 51 push ecx
00D4511C 8B 55 D8 mov edx,dword ptr [ebp-28h]
00D4511F 83 C2 04 add edx,4 ; (*p)+1 --> +4
00D45122 52 push edx
00D45123 8B 45 D8 mov eax,dword ptr [ebp-28h]
00D45126 50 push eax
00D45127 68 E4 7B D4 00 push 0D47BE4h
00D4512C E8 80 C2 FF FF call 00D413B1
00D45131 83 C4 10 add esp,10h
实验结果证明:
-
p和*p存储的值相等
-
p+1和*p+1的值不相等。
p+1:5241652 - 5241660 = 8 --> 2个int
*p+1:5241652 - 5241656 = 4 --> 1个int
证明p和*p的数据宽度不同。p是2个int宽度,*p是1个int宽度。表明:p指向的是arr首地址,是一个具有2个int宽度的数组的首地址。*p指向arr数组第一个元素的地址,其宽度是1个int宽度。
-
*(*(p+1))。p+1使得p向高地址移动1*2个int宽度。*(p+1)将移动宽度变为1个int宽度。*(*(p+1))取出指向地址的值:3。*(*(p+1)) == *(*(p+1)+0) == *(p+1)[0]
*(((*p)+1)+0)。*p将移动宽度变为1个int宽度。(*p)+1使得p向高地址移动1*1个int宽度。((*p)+1)+0使得p向高地址移动0*1个int宽度。*(((*p)+1)+0)取出指向地址的值:2。*(((*p)+1)+0) == *((*p)+1+0) == ((*p)+1)[0]
结论:
数组指针中,p和*p存储的值相等,是编译器用于区分不同数据宽度的一种标记方式。即p指向的是数组首地址,加减运算是按照数组宽度计算。*p指向数组的第一个元素的地址,加减运算是按照数组元素的宽度计算。
鄙人陋见,批评指正!