C语言-数组指针
## 1.1 数组指针指针数组和数组指针常常让人感到混淆。
从词性的角度理解。指针数组是指针修饰数组,强调的是数组。数组指针是数组修饰指针,强调的是指针。
从C语法定义来看:二者定义的语法不同。如果int (\*p) 不加括号,就变成了指针数组int \*p。
```c
int main() {
int array = { 1, 2, 3, 4, 5 };
int* arr;// 指针数组。数组arr中存储的都是int型的指针
int (*p);// 数组指针p。指针p指向一个长度为5个int的数组首地址
p = (int(*))arr;// 数组指针p指向arr的首地址
printf("%d %d\n", p, *p);
return 0;
}
输出:
11533108 11533108
```
从汇编代码看:注意默认是cdel调用约定,printf的参数是从右往左压栈。可以看到p和\*p的值都是来自。因此对于cup而言,二者是没有区别的。
我认为p和\*p是编译器用于某种区分的。
```assembly
printf("%d %d\n", p, *p);
006450D8 8B 45 BC mov eax,dword ptr
006450DB 50 push eax
006450DC 8B 4D BC mov ecx,dword ptr
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
```
验证我的假设
```c
int main() {
int arr = { 1, 2, 3, 4, 5 };
int(*p) = (int(*) )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
```
```assembly
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
00D450F8 8B 44 0A 08 mov eax,dword ptr ; +8
00D450FC 50 push eax
00D450FD 8B 4D D8 mov ecx,dword ptr
00D45100 83 C1 08 add ecx,8;p + 1 --> +8
00D45103 51 push ecx
00D45104 8B 55 D8 mov edx,dword ptr
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
00D45118 8B 48 04 mov ecx,dword ptr ; +4
00D4511B 51 push ecx
00D4511C 8B 55 D8 mov edx,dword ptr
00D4511F 83 C2 04 add edx,4; (*p)+1 --> +4
00D45122 52 push edx
00D45123 8B 45 D8 mov eax,dword ptr
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)
\*(((\*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)
结论:
数组指针中,p和\*p存储的值相等,是编译器用于区分不同数据宽度的一种标记方式。即p指向的是数组首地址,加减运算是按照数组宽度计算。\*p指向数组的第一个元素的地址,加减运算是按照数组元素的宽度计算。
鄙人陋见,批评指正! 刚学完这里,小白还啥都不懂,刚好看看跟着学习一下{:301_978:} borohai 发表于 2023-5-6 17:18
刚学完这里,小白还啥都不懂,刚好看看跟着学习一下
感谢支持!你觉得这篇文章写的通俗吗,有没有什么地方晦涩难懂。 ATrueMan 发表于 2023-5-8 10:39
感谢支持!你觉得这篇文章写的通俗吗,有没有什么地方晦涩难懂。
{:301_983:}主要的是我刚学,很多不懂,汇编都看不懂,最多认识了指针数组与指针数组,还不会用,但是你的那个p与*p那个太高级了,我看不明白 本帖最后由 熊猫拍板砖 于 2023-5-9 16:11 编辑
int (*p),这个一般用在二维数组上,一维数组直接int *p就行了
你上面的操作有点迷之操作,既然操作一维数组,为啥非要把一维数组强制转换成一个二维数组指针去操作,有点化简为繁了。
```c
#include "stdio.h"
int main(void)
{
int arr={1,2,3,4,5};
int (*p);
int *p1;
// 这里把 arr 赋值给 p
p=arr; // 这里虽然不会报错,但是会告警 Incompatible pointer types assigning to 'int (*)' from 'int'; take the address with &
// 意思是 将'int'分配给'int (*) '时,指针类型不兼容;请使用&获取地址
// 这里把arr 赋值给 指针p1,就没有什么问题
p1=arr;
// 分别对 p1 和 p 进行指针操作
// 对p1进行指针操作
for (int i=0;i<5;i++)
{
printf("p1 = %d, &p1 = %p\n",*(p1),p1);
p1++;
}
// 对 p 进行指针操作
for (int i=0;i<5;i++)
{
printf("p = %d,&p = %p\n",*((*p)+i),(*p)+i);
}
return 0;
}
结果:
p1 = 1, &p1 = 0x7ff7bdb1b830
p1 = 2, &p1 = 0x7ff7bdb1b834
p1 = 3, &p1 = 0x7ff7bdb1b838
p1 = 4, &p1 = 0x7ff7bdb1b83c
p1 = 5, &p1 = 0x7ff7bdb1b840
p = 1,&p = 0x7ff7bdb1b830
p = 2,&p = 0x7ff7bdb1b834
p = 3,&p = 0x7ff7bdb1b838
p = 4,&p = 0x7ff7bdb1b83c
p = 5,&p = 0x7ff7bdb1b840
```
通过上面的例子可以看到,如果你要是对一维数组进行操作,最好不要使用二维数组的指针,那样是非常麻烦的
因为多维数组中,比如 int arr; 这里最外维的指针+1,相当于内存地址+16字节长度,而最内层维度,指针+1也就是增加4字节长度
这里推荐两本书,第一本,有没有基础都可以看的 《C Primer Plus》,第二本是《深入理解C指针》,这本是有C基础的来看的 熊猫拍板砖 发表于 2023-5-9 15:19
int (*p),这个一般用在二维数组上,一维数组直接int *p就行了
你上面的操作有点迷之操作,既然操作一 ...
感谢你的观点!你的解释非常正确!从二维数组的角度理解 int (\*p); p就是指向的二维数组中第一个维度的地址。
这篇文章主要是想解释下一般指针和数组指针对于\*的处理。\*p的值为什么是一个地址却不是一个值,而 *p1的值为什么是一个值。
此外,arr与&arr它们的值相同,但是表示的数据宽度却不一样。&arr:数组指针。
我认为你和我的看法没有本质的区别,只是看待的出发点不同。从正向的角度理解,有许多专有名词能够帮助我们快速理解。从逆向的角度理解,都是操作一段内存,只是数据宽度不同。
希望我们的理解能给大家带来帮助!
```c
int main() {
int arr = { 1, 2, 3, 4, 5 };
printf("arr:%x \t&arr:%x \tarr+1:%x \t&arr+1:%x\n", arr, &arr, arr+1, &arr+1);
return 0;
}
输出:
arr:cff9c8 &arr:cff9c8 arr+1:cff9cc &arr+1:cff9dc
``` 你这结论描述的很奇怪,容易让人更乱。
首先,“编译器用于区分不同数据宽度的一种标记方式”,这东西有个更加适合的名词叫“类型”。这也是内存存储的本质,即你后面结论的“宽度”实际上就是每个类型所占用的宽度。
其次,不加括号实际上不是因为“定义语法”不同,实际上这个是因为[]运算符早于*运算符运算,因此不加括号会先执行p,再执行*p。
最后,这俩东西实际上没什么混淆的,也和强调没有什么关系。“指针数组”的描述就和整型数组,浮点型数组一致,前面代表集合的类型,后面代表它是一个集合。而“数组指针”也和整型指针,浮点型指针一致,前面代表这个指针的类型,后面代表它是一个指针。这和强调什么并没有关系,只是一种简单的子类型定义而已。 lijialong1313 发表于 2023-5-12 11:11
你这结论描述的很奇怪,容易让人更乱。
首先,“编译器用于区分不同数据宽度的一种标记方式”,这东西有 ...
感谢你的讲解
页:
[1]