0


懂了嘎嘎乱杀,但我赌你会懵——指针进阶终极版

目录

传统艺能😎

小编是双非本科大一菜鸟不赘述,欢迎大佬指点江山(QQ:1319365055)
此前博客点我!点我!请搜索博主 【知晓天空之蓝】
乔乔的gitee代码库(打灰人 )欢迎访问,点我!

过渡区🤣

现在是北京时间17:00,又摸了三天鱼,琐事很多,早上7点起床也愣是磨到9点才开始安排学习,效率嘎嘎不行,说实话学校更适合学习是有道理的……
在这里插入图片描述

正片开始👀

细化指针这一部分内容,现在着重把一些指针的运用情景搬出来康康,如果对指针盘的非常熟练了,或者指针还出于入门阶段的铁子请绕道(晕头警告)

直接给大家盘个套餐:

一维数组👏

int a[]={1,2,3,4,5};printf("%d\n",sizeof(a));printf("%d\n",sizeof(a+0));printf("%d\n",sizeof(*a));printf("%d\n",sizeof(a+1));printf("%d\n",sizeof(a[1]));printf("%d\n",sizeof(&a));printf("%d\n",sizeof(*&a));printf("%d\n",sizeof(&a+1));printf("%d\n",sizeof(&a[0]));printf("%d\n",sizeof(&a[0]+1));

问题很简单,这组 printf 的值是多少?小朋友你是否有些许害怕,但是没有关系我们逐个击破,这是后续内容的基础

记住你的答案,来看看编译器是怎么解析的:
在这里插入图片描述
首先我们应该知道数组名代表的是数组首元素的地址,但是要知道我们有两种例外:
1.sizeof (),()内是数组名时,代表的就是整个数组,计算的就是数组的大小,单位是字节;
2. & 数组名时,表示的也是整个数组,取出的就是整个数组地址;
除了以上两种情况,其余的所有数组名都表示首元素地址!

所以1.sizeof(a)就是直接将数组名放进去,算出的就应该是 sizeof(int)5 = 20
2.sizeof(a+0)此时不只有数组名,因此 a 代表首元素地址,a+0 等价于 a,是地址大小,在32位/64位平台下对应 4/8 字节大小
3.a ,不只有数组名 a 代表首元素地址,解引用得到首元素,sizeof(a)= sizeof(int)= 4
4.a+1 老规矩还是首元素地址+1,就是第二个元素地址,是地址大小为 4/8 字节
5.a[ 1 ],大小为4
6.&a 为整个数组地址,但是地址终归是 4/8 字节
7. * &a ,&a 是类型为 int(
)[4] 的数组指针,解引用数组地址为整个数组,大小 20
8. &a +1取出整个数组地址,一个数组指针的单位就是整个数组,+1 就会跳过整个数组,大小还是为第二个数组地址,大小 4/8
9. &a[0],首元素地址,4/8
10. &a[0] + 1,第二个元素地址,4/8

字符数组👏

char a[]={'a','b','c','d','e','f'};printf("%d\n",sizeof(a));printf("%d\n",sizeof(a+0));printf("%d\n",sizeof(*a));printf("%d\n",sizeof(a[1]));printf("%d\n",sizeof(&a));printf("%d\n",sizeof(&a+1));printf("%d\n",sizeof(&a[0]+1));

和上面同理,再来看看编译器怎么解答的:
在这里插入图片描述
1.a 是数组名,首元素地址计算整个数组大小为 6
2.a + 0,首元素地址 + 0 还是首元素地址,4/8
3.*a,首元素大小 ,1
4.1
5.&数组名为整个数组地址,4/8
6.跳过一个数组,下一个数组地址,4/8
7.第二个元素地址,4/8

这么简单?nonono,这才刚刚开始

char a[]={'a','b','c','d','e','f'};printf("%d\n",strlen(a));printf("%d\n",strlen(a+0));printf("%d\n",strlen(*a));printf("%d\n",strlen(a[1]));printf("%d\n",strlen(&a));printf("%d\n",strlen(&a+1));printf("%d\n",strlen(&a[0]+1));

我还是先把运行结果啪出来吧:
在这里插入图片描述
诶?怪诶,打印7下出来2个?nnd给我玩阴的是吧。这里首先强调一下,sizeof 是操作符,海纳百川来啥算啥,‘ \0 ’也会照收,而 strlen 是傲娇的库函数,傲娇在于strlen 针对的是 \0 之前的字符串长度(个数),而且不包含 ‘ \0 ’。
————————————————————————————————

1.a 数组名,没有在 sizeof 内部,为首元素地址,我们知道 strlen 在遇到 \0 之前是不会停下来的,字符数组我们没有给到 \0就是没有,因此什么时候遇到他我们不知道,起码会比当前数组大,不知道嘛时候停就是个随机值
在这里插入图片描述
2.同理,随机值
3.首元素地址解引用为首元素 ‘a’,strlen 需要的是一个地址,此时会从把‘a’ 默认为一个地址,a的ASCII码值为 97,就会从内存中地址为 97 的地方开始往后数字符个数,但是注意 97 不属于我原本分配的内容,属于非法访问内存,直接报错崩溃垮掉
4.同理,报错
5.数组的地址,虽然和 strlen 参数类型有所差异,但还是从第一个位置向后数,那就是随机值啦
6.跳过一个数组的地址,依然是随机值
7.第二个元素地址,依然依然是随机值
————————————————————————————————

那么就又双可以联想到字符串了,引入指针变量进行讨论:

char* p ="abcdef";printf("%d\n",sizeof(p));printf("%d\n",sizeof(p+1));printf("%d\n",sizeof(*p));printf("%d\n",sizeof(p[0]));printf("%d\n",sizeof(&p));printf("%d\n",sizeof(&p+1));printf("%d\n",sizeof(&p[0]+1));printf("%d\n",strlen(p));printf("%d\n",strlen(p+1));printf("%d\n",strlen(*p));printf("%d\n",strlen(p[0]));printf("%d\n",strlen(&p));printf("%d\n",strlen(&p+1));printf("%d\n",strlen(&p[0]+1));

直接看结果吧,这下就感觉出有点意义不明了,结果对应哪个数据都不知道了,所以这时候自主分析的价值就出来了
在这里插入图片描述
———————————————————————————————

  1. p是一个指针,sizeof 算指针的大小,4/8字节
  2. p+1,就是 p 的值加1,字符指针p是一个地址,数值上+1是 b 的地址,所以大小 4/8字节
  3. p 为char* 的指针,解引用访问首元素,大小为 1
  4. 小问号你是否有很多朋友,指针为啥还可以 [0] ?其实 p[0] 等价于 *(p+0),p是首元素地址,p+0亦是,解引用出来就是首元素,1
  5. p是地址,&p是对地址取地址,也就是二级指针,是地址那就是 4/8
  6. 跳过一个char*的地址即跳过了一个p的地址,本质上还是地址,4/8
  7. p[0]为首元素,取地址+1 为第二个元素的地址,4/8 ——————————————————————————————— 1.在 “abcdef”中是隐含了一个‘ \0 ’的,所以传入p算出大小为 6 2.同理,从第二个元素开始,5 3.解引用为a,97,报错 4,等价于*p,就是首元素,报错 5,取数组地址的地址往后数字符串,为随机值 6.+1跳过一个地址的地址往后数,依然是随机值 7.第二个元素的地址往后数,随机值,等价于 p+1 ———————————————————————————————

二维数组👏

int a[3][4]={0};printf("%d\n",sizeof(a));printf("%d\n",sizeof(a[0][0]));printf("%d\n",sizeof(a[0]));printf("%d\n",sizeof(a[0]+1));printf("%d\n",sizeof(*a[0]+1));printf("%d\n",sizeof(a+1));printf("%d\n",sizeof(*(a+1)));printf("%d\n",sizeof(&a[0]+1));printf("%d\n",sizeof(*(&a[0]+1)));printf("%d\n",sizeof(*a));printf("%d\n",sizeof(a[3]));

看一下运行结果:
在这里插入图片描述
————————————————————————————————
1.a数组名,为整个数组,124 = 48
2.数组首元素,1
4 = 4
3.数组第一行元素,44 = 16
4.a[0]此时为首元素地址,即a[0]|0] 地址,+1为 a[0]|1] 地址,4/8
5.等价于a[0] [1],,1
4 = 4
6.数组名不是单独存放为第一行地址,+1表示第二行地址,4/8
7.解引用为第二行元素,44 = 16
8.a[0]为第一行地址,&a[0]+1为第二行地址,4/8
9.解引用为第二行元素,4
4 = 16
10.解引用首元素地址为第一行,44 = 16
11.淦!好怪!数组只有3行却搞出个 a[3],这不越界了吗?其实编译器会很聪明的自行推算,按照已给数组排列就是 4
4 = 16
————————————————————————————————

整点硬菜👏

热身完了,来整点题目,让你混乱的大脑脉动回来(雪上加霜)
1.

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

ptr 是int类型指针,&a是数组地址,+1跳过一个数组,指向数组末位的地址,强转为 int* 类型,*(a+1)首元素地址+1再解引用得到第二个元素 ,ptr - 1指向 5 的地址,所以答案为 2,5。

//假设stu 大小为 20 字节structstu{char* name;int age;float score;}*p;intmain(){(structstu*)p =0x100000;printf("%p\n",p +0x1);printf("%p\n",(unsignedlong)p +0x1);printf("%p\n",(unsignedint*)p +0x1);return0;}

p是一个指针类型指向结构体,代表结构体的地址, p + 0x1 就是地址数值上进行 + 0x1 操作,p = 0x100000 ,我们知道 int* +1 跳过4个字节,char* + 1跳过1 个字节,我们这里是结构体类型,就跳过 20 个字节,20 化成16进制相加就是 0x100014

p 强转为 unsigned long 其实就是整型,p 就是一个纯纯的数字了,那0x100000 既然已经是“数字”了,那就有了可以直接加减的属性,就是 0x100001

p强转成无符号整型指针类型, 一个指针类型是 4 个字节,结果就是 0x100004

到这里是不是有内味儿了,那咱继续

intmain(){int a[4]={1,2,3,4};int* ptr =(int*)(&a+1);int* ptr2 =(int*)((int)a+1);printf("%x %x",ptr[-1],*ptr2);return0;}

此题请仔细思考,大坑警告!

ptr 为整型指针,&a为整个数组地址,+1跳过整个数组来到末位的地址,再强转成 int * 类型(这里大可不必,因为它本来就是一个 int * 指针)ptr[-1] 等价于 * (ptr - 1),再代入 ptr 就代表最末地址 -1 来到 4 的地址,ptr[-1 ]就是 4。

ptr2中 a为首元地址,这里注意优先级问题,先强转成 int 就是个纯纯的数字可以直接进行 +1 操作,即地址进行了数值+1,格局高不高就要看下一步,接下来需要考虑 大小端问题,因为我们是小端存储,按照低位字节在低地址处,所以 1,2,3,4 在内存中是这样的:
————————————————————————————————
…… | 01 | 00 | 00 | 00 | 02 | 00 | 00 | 00 | 03 | 00 | ……
————————————————————————————————
假设 01 地址是 0x01,int 强转后 a +1 偏移一个字节,指向的就是 01 后面一个 00 的位置,所以 ptr2 就指向这个位置,我们解引用拿到他的值就是整型大小的值就是 00 00 00 02,再以小端的形式拿出来就是 20 00 00 00。

intmain(){int a[3][2]={(0,1),(2,3),(4,5)};int* p;
p = a[0];printf("%d",p[0]);return0;}

p[0] 等价于(p+0),p = a[0], 就是a[0] ,a[0] 是首元素地址,那么问题来了:是 0 的地址吗?

请仔细看看我们二维数组的逗号表达式,其实整个数组就只有 3 个元素:1,3 5;他们在数组中排列出来是:

1 | 3
5 | 0
0 | 0

所以解引用出来就是 1。

今天就到这儿吧,摸了家人们


本文转载自: https://blog.csdn.net/qq_61500888/article/details/122584321
版权归原作者 乔乔家的龙龙 所有, 如有侵权,请联系我们删除。

“懂了嘎嘎乱杀,但我赌你会懵——指针进阶终极版”的评论:

还没有评论