首页
社区
课程
招聘
[原创]C语言-数组指针
发表于: 2023-5-5 23:01 6157

[原创]C语言-数组指针

2023-5-5 23:01
6157

数组指针

指针数组和数组指针常常让人感到混淆。

 

从词性的角度理解。指针数组是指针修饰数组,强调的是数组。数组指针是数组修饰指针,强调的是指针。

 

从C语法定义来看:二者定义的语法不同。如果int (*p)[5] 不加括号,就变成了指针数组int *p[5]。

1
2
3
4
5
6
7
8
9
10
11
12
int main() {
    int array[5] = { 1, 2, 3, 4, 5 };
    int* arr[5];  // 指针数组。数组arr中存储的都是int型的指针
    int (*p)[5];  // 数组指针p。指针p指向一个长度为5int的数组首地址
    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是编译器用于某种区分的。

1
2
3
4
5
6
7
8
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

验证我的假设

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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

实验结果证明:

  1. p和*p存储的值相等

  2. 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宽度。

  3. *(*(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指向数组的第一个元素的地址,加减运算是按照数组元素的宽度计算。

 

鄙人陋见,批评指正!


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 3
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//