指针辨析(下)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

指针 对应的总是空间内存,但是你知道他是什么类型,对应什么空间,步长是多少,解引用之后是什么吗?废话不多说,直接上例题

步长和解引用

问最终显示是什么?

struct Test {
	int Num; char* pcName; short sDate; char cha[2]; short sBa[4];
}*p; 
//假设p 的值为0x100000。 如下表表达式的值分别为多少? //已知,结构体Test类型的变量大小是20个字节 
int main() {
	printf("%p\n", p + 0x1);
	printf("%p\n", (unsigned long)p + 0x1); 
	printf("%p\n", (unsigned int*)p + 0x1); 
	return 0;
}

答案是
0x100014
0x100001
0x100004

分析: 指针的类型决定了步长和访问的长度
如果 struct Test 长度是 20 ,说明 他的指针一次跳过 20 个字节
则0x 100000 + 20 = 0x100014
p 的类型是 long 整形 整形的加减和步长没有关系,就是数据加减
0x100000 +1
p 的类型是 int* 一次越过 4 个字节 0x100000 + 4

数组类型

一维数组

还是问最终显示多少?

int main() {
	int a[5] = { 1, 2, 3, 4, 5 }; 
	int* ptr = (int*)(&a + 1); 
	printf("%d,%d", *(a + 1), *(ptr - 1));
	 return 0;
}

在这里插入图片描述
结合图来看
a 是一个数组,&a 取出整个数组的地址,+1 跳过整个数组,随后 把值付给了 ptr
随后打印
a + 1 是 数组第二个元素的地址,解引用 打印为 2
ptr-1 的位置指向了第5个元素 解引用 得到5


int main() {
	int a[4] = { 1, 2, 3, 4 };
	int* ptr1 = (int*)(&a + 1);
	int* ptr2 = (int*)((int)a + 1);
	printf("%x,%x", ptr1[-1], *ptr2);
	return 0;
}

在这里插入图片描述
首先,先把内存空间画出来,得到最上面的图
ptr1 是越过了整个数组之后 强制类型转换为 (int
这个时候 (int
) 只能访问四个字节,ptr[-1] 相当于后退一个元素解引用,得到数字 4
但是
ptr2 的来源是 首元素地址强制类型转换为整形加减 , 一次只加一个字节
地址指向下一个字节的时候,就要将储存原理画出来,电脑是小端存储,得到下面那个图
第一个元素是 1 ,小端存储 是 0001 0000 0000 0000
第二个元素是2 ,小端存储 是 0002 0000 0000 0000
地址移动一位,int* 访问得到
0000 0000 0000 0002
最终结果要反过来打印得到
0x2000000000


int main() {
	int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int* ptr1 = (int*)(&aa + 1); 
	int* ptr2 = (int*)(*(aa + 1)); 
	printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));
	return 0;
}

在这里插入图片描述
&aa 是整个数组的地址
此时数组是一个 二维数组, &aa+1会直接跳过整个二维数组指向最后
然后被强制类型转换,从数组指针变成整形指针,访问字节和步长都变了,付给了 ptr1
随后 ptr1 - 1 就是 退 一个 int 的长度,然后访问到 10

aa+1 就是数组第二个元素的地址 ,因为 aa 是二维数组, aa+1 是一个一维数组的地址
然后解引用访问到 一维数组 ,强制类型转换为 整形指针付给了 ptr2
ptr2 - 1 就会 退一个 int 长度,访问到 5这个值


int main() {
int a[5][5]; 
int(*p)[4];
 p = a; 
 printf( "%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]); 
 return 0;
}

在这里插入图片描述
本来 aa 是一个 二维数组,数组中有五个一维数组,每个一维数组中有五个 int 值
我们可以得到上方的每五个格子为一个单位的切分方式
&a[4][2] 就是 得到 第五个数组的第三个元素的地址

之后 p = a 因为 p 的类型是 数组指针,每个数组指向 四个元素
这就将数组 四个四个一组切分,得到上面的图下方的锯齿切分
&p[4][2] 就是 第五个数组的第三个元素的地址

两个地址之间差了四个元素,地址相减是元素个数,p 小 , a 大, p - a = -4
第一个 %p打印 直接打印数组的内存码,也就是 -4 的补码

字符串数组

最大的要点就是字符串数组里面放的是数组指针

int main() {
	char *c[] = {"ENTER","NEW","POINT","FIRST"}; 
	char**cp[] = {c+3,c+2,c+1,c}; 
	char***cpp = cp; printf("%s\n", **++cpp); 
	printf("%s\n", *--*++cpp+3);
	printf("%s\n", *cpp[-2]+3);
	printf("%s\n", cpp[-1][-1]+1);
 	return 0;
}

在初始化后,我们可能得到这张图
在这里插入图片描述
我们看看 **++cpp
在这里插入图片描述

printf("%s\n", *--*++cpp+3);

++cpp , cpp 指向 c+2 , *++ cpp 得到 c+2 的指针
**++cpp 访问到 c+2 ,里面储存 point 的首元素地址,打印得到 point


在这里插入图片描述

printf("%s\n", *cpp[-2]+3);

++cpp cpp继续下移
++cpp 得到 c+1 的位置
然后 –
(++cpp) ,就是 c+1-1 = c
(++cpp), 访问到 c ,也就是首元素,就是 ENTER 首元素的地址
(++cpp)+3 ,首元素地址加三 指向ER 打印最后得到 ER


在这里插入图片描述

printf("%s\n", *cpp[-2]+3);

cpp[-2] 相当于 *(cpp - 2) ,访问到 c+3, *cpp[-2] 访问到 FIRST 首元素地址
*cpp[-2]+3 访问到 ST,然后打印出来


printf("%s\n", cpp[-1][-1]+1);

在这里插入图片描述
*(cpp - 1 ) 等价于 cpp[-1], 访问到 c+2
随后 cpp[-1][-1] 等价于 *(c+2 - 1) 我在图上直接改了 访问到 NEW 的首元素地址
随后 +1 得到 EW 打印

总结

  1. 画出内存图
  2. 注意指针类型,步长和访问权限,访问后是什么类型
  • 8
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值