在上一节的学习中,我们已经了解了sizeof和strlen的使用方法,也做了一维数组的相关题目,今天我们就来挑战二维数组和指针运算题目吧。
话不多说,我们来看代码
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]));
老规矩,我们先看sizeof(a),他表示的是整个数组 那么它的大小应该是48.
接下来看sizeof(a[0][0]),他表示的是第一个元素,所以它的大小是4。
接下来我们看sizeof(a[0]),首先我们要明白a[0],表示的是什么,其实他表示的就是一个一维数组的数组名。接下来,我给大家画图理解一下。
所以说,这里就相当于数组名单独放在sizeof里面,也就是第一行的大小。那么就是16.
接下来我们看 sizeof(a[0]+1),上面我们说过,a[0]其实就是数组名,那么他就表示首元素的地址,是地址,他就应该是四或者八个字节。
接下来我们看sizeof(*(a[0]+1)),他表示的是第二个元素,那么大小是4.
接下来我们看sizeof(a+1),a表示的是第一行的地址,加一直接跳到第二行,但他同样是地址,那么答案是4或者8.
接下来我们看sizeof(*(a+1)),和上面一样,a+1表示第二行的地址,其实就相当于&a[1],在加上*,其实就相当于数组名,那么同理也是16.
接下来我们看sizeof(&a[0]+1),很明显可以看出他是一个地址,是地址就是4或者8.
接下来我们看sizeof(*(&a[0]+1)),&a[0]+1他表示的第二行的地址,解引用之后相当于数组名,那么大小是16.
接下来我们看sizeof(*a),他表示的是第一行的地址解引用,相当于数组名,答案也是16.
接下来我们看sizeof(a[3]),很显然,他是不存在的,我们是通过他的类型来判断出来的,也可以假象有第四行,那么它的大小是16.
先来一道简单的题目开开胃。
#include
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int *ptr = (int *)(&a + 1);
printf( "%d,%d", *(a + 1), *(ptr - 1));
return 0;
}
遇到这样的问题,如果想不懂,那就一定要画图,画图!!!
通过画图我们应该可以清楚的知道答案是多少了吧,那就是2 和 5.
接下来我们加大一点难度。
#include
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
int *p;
p = a[0];
printf( "%d", p[0]);
return 0;
}
我想,很多同学会非常自信地说是0.真是如此嘛?
尽然是1.不卖关子了,请大家仔细看看二维数组是怎么初始化的?它用的是括号!!!
其实,相当于他只输入了1,3,5,三个数字。那么p[0]就应该是1。
接下来我们再看看一道题目。
#include
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;
}
首先,我们定义了一个二维数组。之后又定义了一个数组指针,这个数组的大小是四个元素,p=a是将a的地址赋给p,但p这个指针所指向的地址是以四个元素为一组的整体的地址,那么这里就会发安生强制类型转换,所以p+1将会跳过四个元素也就是16个字节的大小,a+1会跳过20个自己的的大小。
为了方便大家理解,我画了一个图帮助大家。我们前几节讲过 ,指针相减表示的是两地之间的元素个数,有正负之分,因为地址是越来越高的,所以减出来应该是-4,那么%d打印出来就是-4,%p会以为打印的是地址,那么它会将-4的补码直接当作地址打印出来
下面我们来看一道相对复杂的题目。
#include
int main()
{
char *a[] = {"work","at","alibaba"};
char**pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
我们一起来分析,首先a是一个chae*类型的指针数组,说明数组里面放的都是地址,也就是w字符的地址,a的地址以及alibaba里a的地址。
pa是一个用来存放char*类型的指针,是一个二级指针,a赋值给他的意思是把这个指针数组的首元素的地址传给他。之后,pa++,跳过一个空间,拿到第二个元素的地址,通过解引用,拿到了at里面a的地址,打印出来应该是at。
接下来,我们来看最麻烦的一道题。
#include
int main()
{
char *c[] = {"ENTER","NWE","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:
对应的图片应该是这样的,先++之后指向了cp的第二个元素的位置,通过两次解引用 ,拿到了E的地址,所以打印出来的应该是ENTER.
*--*++cpp+3:我们来看这个,我们要知道+的优先等级低于++和*我们先来看*++cpp之后的关系图。
解引用之后,指向了c的第二个元素,之后我们来看 *--之后的结果,--之后指向了c的第一个原为位置,在解引用拿到了E的地址,+3之后跳过三个字节,所以打印出来的是ER。
*cpp[-2]+3:这里我就不画图了,如果大家看不懂,那我们把他换一种形式。*(*(cpp-2))+3.大家看着上面的图自己分析一下,不难知道cpp-2在解引用拿到的是F的地址,+3之后打印出来应该是ST。
cpp[-1][-1]+1:和上面类似,如果看不懂可以自己转换一下,他所表示的是N的地址+1所打印出来的便是WE。
好了,今天的分享就到这里了,相信大家对指针有了比较深入的理解。