今天和大家探讨一下下面代码中的指针进行P++操作的几种可能性及哪种方式更合理
int main()
{
int arr[] = {1,2,3,4,5,6,7,8,9,10};
int *p = arr; //定义一个整型指针变量p保存整形数组arr的首地址
*p = 10; //通过解引用访问arr[0]
p++;
*p = 20; //p进行加一操作后重新赋值
printf("十进制 %d,%d\n",arr[0],arr[1]);
printf("十六进制 %08x,%08x\n",arr[0],arr[1]);
return 0;
}
根据此处的情况加一操作有如下几种可能性:
1.加一个字节
2.加整个数组的长度(数组长=sizeof(arr)/sizeof(arr[0])) 直接否定,没有操作意义
3.加一个数组单元格(此处指的是4字节)
情况1:加一个字节
我们令arr[0]=2 ,arr[1]=10存储地址分别为100,104,一个元素占四个字节
接下来考虑如何存放数据的问题,我们引入中间进制,将100这个地址的单元格细化为八个单元格,相比之下采用十六进制存放数据更适合八个单元格的布局,下面就要分析存储顺序的问题:存储单元从左到右依次是低地址到高地址,低地址放大数据称为大端,低地址放小数据称为小端,我们的操作以小端为准。2和10写成16进制数分别为00 00 00 02和00 00 00 0a,将这两个数存放如下图:
扫描二维码关注公众号,回复:
4988246 查看本文章
没有进行加一操作前结构是合理的。
下面实现猜想的内容,将20保存后移动一个字节得到下图:
20的16进制为00 00 00 14
此时就存在问题了,再用下面用代码一探究竟
#include <stdio.h>
int main()
{
int arr[] = {1,2,3,4,5,6,7,8,9,10};
int *p = arr;
*p = 10;
p = (int *)((int)p+1); //让 p 只加一个字节
*p = 20;
printf("%08x,%08x\n",arr[0],arr[1]); //分别输出arr[0] 、arr[1] 的八位十六进制
return 0;
}
既然后移一个字节,输出结果为0000140a,00000000,前者用十六进制换算下来是5130
明显得到的数字换算之后和自己给的数据差距很大,如果真的p++是进行加一字节的操作可能会造成不可预料的后果。因此p++操作加一个数组单元格是最合理的,它可以准确的指向某一个确定的位置,迅速的完成解引用。通过这一番讨论不难看出指针和数字间的操作并不是一步到位,它有一个中间过程,期间的数据转换取决于指针的数据类型,数据类型不同自然单元格长度不同,这就是p++操作是移动单元格而不是其他操作的原因。
接下来谈谈指针的几种运算:
1.指针+数字
#include <stdio.h>
int main()
{
int*p=(int*)2000;//用int*进行强转,保证等号两边数据类型一致
p+4;
printf("%d",p+4);
printf("%d",p-4);
return 0;
}
结果:2016 1984
指针的加法需要进行一个微调整,调整权重(sizeof(数据类型)),
此处的数据类型为指针去到一个*,二级指针也不例外。比如将上述代码中的红色部分被改为
printf("%d",(double**)p+2);
输出值为2000+8=2008
指针的减法我们也采用调整权重的方法,要注意的就是对于二级指针到一级指针的转换问题。说到这可能有人问指针可不可以和指针相加,显然是没有意义的,首先说你想得到啥,关键是本质上就得不到任何实际上的结果。但是指针间的减法就不同,8它至少可以得到一个区间,然后我们去研究它的用处。在指针数组中我们单纯在字节层面上讨论似乎没多大意义,因为我们
常说一个单元格存什么而不是说几个字节存什么。自然指针的
减法运算直观的得到的是两个指针间单元格的数目。
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
int *p = &arr[1];
int *q = &arr[9];
printf("%d\n", p - q);
printf("%d\n", q- p);
printf("%d\n", (float*)q - (float*)p);//调整力度为字节数之差/权重
printf("%d\n", (double*)q - (double*)p);
printf("%d\n", (short*)q - (short*)p);
printf("%d\n", (long**)q - (long**)p);
return 0;
}
结果:-8 8 8 4 16 8