给大家分享一句我很喜欢我话:
知不足而奋进,望远山而前行!!!
铁铁们,成功的路上必然是孤独且艰难的,但是我们不可以放弃,远山就在前方,但我们能力仍然不足,所有我们更要奋进前行!!!
今天我们更新了指针相关常见题型内容,
🎉 欢迎大家关注🔍点赞👍收藏⭐️留言📝
一、例子1
#include <stdio.h>
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int *ptr = (int *)(&a + 1);
printf( "%d,%d", *(a + 1), *(ptr - 1));
return 0;
}
我们先来看第一个例子,先创建一一维数组,然后第三行代码将创建一个指针ptr
,指向变量a
后面的内存位置。它通过使用 &a
获取变量a
的地址,然后将其强制转换为 int*
类型指针。接下来,+1
操作将指针指向下一个 int
类型的内存位置。
然后看这张图片,第一个输出的是*(a+1),a是一个数组名,它在这里代表的是第一个元素的地址,然后+1,也就是输出第二个元素,所以第一个输出2,然后看*(ptr+1),ptr我们已经说了,代表的是&a+1,这里的&a代表的是整个数组的地址,然后+1之后其实是跳过整个数组,这里我们呢再来说一下表示整个数组的两种情况:
表示整个数组的地址
1. &arr 表示的是 整个数组 的 地址
2.sizeof(arr)中的arr表示的 是整个 数组
(此外的都表示首元素地址,或首行地址等等)
然后我们继续说这个题,此时跳过整个数组之后,&a+1代表的位置已经在图片中表示出来了,所以此时ptr-1在解引用代表的就是5这个元素了,所以会输出2,5
二、例子2
struct Test
{
int Num;
char *pcName;
short sDate;
char cha[2];
short sBa[4];
}*p = (struct Test*)0x100000;
int main()
{
printf("%p\n", p + 0x1);
printf("%p\n", (unsigned long)p + 0x1);
printf("%p\n", (unsigned int*)p + 0x1);
return 0;
}
再看上述这串代码,这串代码的问题是,在x86的环境下,假设结构体的大小是20个字节,那么这串代码会输出什么,先来说一下x86环境是什么意思,其实就是在32位平台下
我们先来分析一下这串代码,我们创建了一个结构体,Test,然后在最后加了一个*,所以这是一个结构体指针,然后创建了一个结构体指针变量p,然后0x100000其实就是p里面存放的值,作为一个地址,然后我们前面说了结构体大小为20个字节,所以加1也就是跳过了20个字节,所以第一个应该是加20,但是0x100000是一个十六进制的数,因此将20转化为十六进制,结果就是0x100014。
然后我们看第二个,我们先把它强制类型转化成long,它就不再是指针了,然后整型值+1,其实就是+1,所以结果是0x100001,
最后我们来看第三个,第三个是强制类型转化为了int*类型,那么+1其实就是加4了,因为一个int*类型占据四个字节嘛,所以此时就是输出0x100004了
三、例子3
#include <stdio.h>
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
int *p;
p = a[0];
printf( "%d", p[0]);
return 0;
}
这串代码第一眼看上去我们可能认为是创建一个二维数组,然后三行五列,元素为括号里的元素,但是仔细一看,发现里边用的不是{},而是(),因此这个数组就会变成这样了{1,3,5};
然后接着往下走,创建一个指针p,然后令p=a[0],这里的a[0]就是第一行的数组名,数组名又表示首元素地址,然后输出p[0],也就是输出1.
下面我们运行代码看一下:
看来我们的推论是正确的。
四、例子4
#include <stdio.h>
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;
}
在x86平台上,你认为这串代码会输出什么呢,
先来分析一下这串代码, int(*p)[4],创建一个数组指针,然后p指向的是四个整形元素的
然后把a赋给p,
然后我们看输出的元素是指针-指针类型的,指针-指针得到的是指针之间的元素个数的绝对值。
我们再来看一下p=a这一行,这是什么意思呢?我们知道,a是二维数组,然后二维数组的数组名代表着首元素地址,也就是第一行的地址,
看一下这张图,绿色包含的这一块就是a所代表的地方。然后我们看&p[4][2],这个其实就是 *(*(p+4)+2),但是p有自己的类型,p指向的数组是四个整形元素的,所以每次+1往后跳四个元素,
所以&p[4][2]就指向紫色填充的地方。然后这两个指针相减,之间有四个元素,然后低地址-高地址,所以结果是负数,换成二进制就是1000000000000000000000000000100(原码)111111111111111111111111111111011(反码)111111111111111111111111111111100(补码)
然后因为打印是按十六进制打印,补码换成十六进制就是FFFFFFFC
然后看第二个,以%d形式打印,就是打印-4。
五、例子5
#include <stdio.h>
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;
}
先看ptr1,%aa是取出的整个二维数组的地址所以加1就是跳过整个二维数组,所以打印*(ptr-1)结果就是10了
再看ptr2,
ptr2是图片中的这样,所以*(ptr2-1)就是打印5了。
六、例子6
#include <stdio.h>
int main()
{
char *a[] = {"work","at","alibaba"};
char**pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
下面我们再来看一下这串代码,这串代码首先创建了一个数组a,然后每个元素都是一个char*类型
然后创建一个二级指针pa,存放char*类型的数组a的地址,a代表数组的首元素,然后pa++,所以pa就等于at
七、例子7
#include <stdio.h>
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,这个代码限制性++这一步,++后cpp指向的便是c+2这里,然后两次解引用,我们便得到P的地址,然后输出P这个数组的字符串,便会输出POINT
然后我们看*--*++cpp+3,此时我们要知道cpp指向的是c+2这里,因为优先级我呢提,所以我们先执行cpp前面的内容,++cpp,那么此时cpp便指向c+1这里,然后解引用,再--,便指向E这里,然后解引用,然后再+3,所以此时就会输出ER这个字符串。
然后我们再来看一下第三个,*cpp[-2]+3,这里cpp[-2]的意思其实就是*(cpp-2),然后由上面可得cpp此时位于c那里,然后-2就会位于c+3的位置,然后再解引用,此时会指向F这里,然后+3,就会输出了ST
最后我们来看一下cpp[-1][-1]+1,转换成*(*(cpp-1)-1)+1,我们知道此时cpp还是位于c+1的位置,因为cpp[-2]不会改变cpp的位置然后cpp-1,得到c+2,然后再-1,再次得到c+1指向的位置,也就是N,然后再+1,也就输出EW了
总结:
这就是我们今天讲解的全部题目了,希望大家好好看一下,把这些题都搞懂,那么指针的学习也就合格了。