目录
听说有孩子为了下饭已经把之前的题目看得烂熟于心惹,所以这里再给出几组指针和数组笔试题,保证有新鲜的题目下饭~
废话不多说,上菜
Group 1
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int *ptr = (int *)(&a + 1);
printf( "%d,%d", *(a + 1), *(ptr - 1));
return 0;
}
窝萌再来重复之前讲过的一个很重要的知识点:
数组名表示数组首元素的地址,但是有两个例外:
1.&数组名取出的是整个数组的地址
2.数组名单独放在sizeof内部时数组名表示的是整个数组
现在开始手撕
首先,&a取出的是整个数组的地址,+1后就跳过了整个数组,由于又强制转换成了int*,所以以后每次+-1都只能跳过一个int*类型的变量,所以在最后ptr-1时就指向了元素5,解引用后就得到了元素5,而a表示数组首元素的地址,+1后得到数组第二个元素的地址,解引用后就得到了数组第二个元素,因此输出的结果就是2,5
现在看一下编译器跑出的结果:
Group 2
struct Test
{
int Num;
char *pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
int main()
{
p = (struct Test*)0x100000;
printf("%p\n", p + 0x1);
printf("%p\n", (unsigned long)p + 0x1);
printf("%p\n", (unsigned int*)p + 0x1);
return 0; }
p是个结构体指针,p的地址是0x00100000,由于该结构体占20个字节,+1后就跳过20个字节,由于地址是用十六进制表示的,所以最后用十六进制表示的结果就是0x00100014
接下来p被强制类型转换成了无符号整形,因此+1就是加了整数1,所以最后结果就是0x00100001
然后p又被强制类型转换成了无符号int *类型的变量,所以+1就跳过4个字节,因此最后结果就是0x00100004
(ps:当用%p打印时是不会出现的0x滴)
让我们来看一下编译器输出的结果吧:
Group 3
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; }
首先,&a取出的是整个数组的地址,+1后就跳过了整个数组,再强制类型转换后,以后加减就是跳过一个整形类型,把a强制类型转换为int类型后地址就是个数值,+1就是大一个数值的整形,再强制类型转换后又指向了一个大一号的地址,由于在vs2019中数据在内存中是小端字节序存储,并且按照十六进制的形式,所以+1后就跳到了图中红色箭头指向的位置,因此ptr[-1] == *(ptr1)所以就得到了元素4,因此第一个输出的就是4,第二个解引用后就得到02000000,也就是2000000
我们最后来看一下编译器输出的结果:
Group 4
#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;
}
这里有个小坑嘞,现在我们把它填上
首先,题目中二维数组的初始化是有问题的,正确的初始化应该用{},而用()就是逗号表达式咯,所以最后的二维数组的初始化结果如图所示
a[0]表示该二维数组第一行首元素的地址,这里创建了指针变量p指向了第一行首元素, p[0] == *(p+0),也就是第一行的首元素1,所以最后的输出结果就是1
看编译器输出的结果:
Group 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;
}
首先这里创建了一个五行y五列的二维数组,由于p是一个有4个整形元素的数组指针类型,这里直接就给p数组第一行首元素的地址(也许程序会报错),p[4][2] == *(*(p+4)+2),由于p是个数组指针,p[4]就表示以p视角第四行元素的地址(等价于数组的地址),所以p[4][2]就表示取出在p视角下第四行的第二个元素,&p[4][2]就表示p视角下第四行第二个元素的地址,&a[4][2]是二维数组中第五行第三个元素的地址,由于指针-指针算出的是两个指针之间元素的个数,所以就是-4,由于第一个结果是用%p打印,-4在计算机中存储的二进制补码是11111111111111111111111111111100,地址是用16进制表示的,1111是一个f,所以最后的结果就是fffffffc,第二个结果用%d打印,%d打印的是有符号数,所以输出的结果就是-4
我们来看一下编译器跑出的结果:
Group 6
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取出的是整个二维数组的地址,+1就跳过了整个二维数组,然后强制类型转换为int *,此后+-1就只能跳过一个整形,*(aa+1) == aa[1],也就是第二行元素的地址,强制转换为int *后,ptr2就指向了第二行第一个元素,因此第一个输出的结果就是10,第二个输出的结果就是5
看一下程序运行的结果:
Group 7
int main()
{
char* a[] = { "work","at","alibaba" };
char** pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
当各个字符串作为表达式时,得到的是字符串首元素的地址,因此这里创建了指针数组a存储各个地址,此后又创建了二级指针pa指向了数组a首元素的地址,pa+1就跳过一个char *类型的变量,pa++后此时pa就指向了a数组第二个元素,解引用后就得到第二个字符串“at”首元素的地址,只要给出地址%s就可以顺着打印,因此最后输出的结果就是“at”
康康程序运行的结果:
Group 8
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;
}
当“ENTER ”作为表达式时得到的是第一个E的地址,同理,后边的字符串得到的都是第一个元素的地址,这里用一个指针数组c进行存储,然后创建了二级指针数组cpp,依次指向了c中第四个元素,第三个元素,第二个元素,第一个元素,接着又创建了三级指针变量cpp,由于此时cp表示数组首元素的地址,所以这里的cpp存的就是cp里首元素的地址
首先,++cpp后cpp就指向了cp中第二个元素,再两次解引用就得到了P元素的地址,所以第一个打印的结果就是“POINT”
根据操作符的优先级,++cpp后cpp就指向了cp中第三个元素,解引用后就得到c+1的地址,--之后就变成了c的地址,再解引用就得到了“ENTER”中第一个E的地址,+3后跳过三个char类型的变量,就得到了“ENTER中”T的地址,因此第二个打印的结果就是“TER”
第三个,cpp[-2]后又指向了cp中第一个元素,解引用后就得到c+3的地址,也就是“FIRST”中F的地址,再+3跳过三个char类型,就得到“FIRST”中S的地址,因此这里的输出结果就是“ST”
最后一个,cpp[-1][-1] == *(*(cpp-1)-1)首先cpp-1就得到cp中第二个元素的地址,解引用后就得到c+2,-1后就得到c+1,再解引用就得到“NEW”中N的地址,+1后得到“NEW”中E的地址,所以这里打印出的就是“EW”
我们康康结果是不是这样的嘞:
OK,本次下饭试题就先到这里啦
觉得不戳请点赞关注一下嘞,喜上加喜可是再好不过啦~