目录
Group 1
Group 2
Group 3
Group 4
Group 5
Group 6
Group 7
Group 8
听说有孩子为了下饭已经把之前的题目看得烂熟于心惹,所以这里再给出几组指针和数组笔试题,保证有新鲜的题目下饭~
废话不多说,上菜
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
现在看一下编译器跑出的结果:
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滴)
让我们来看一下编译器输出的结果吧:
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
我们最后来看一下编译器输出的结果:
#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
看编译器输出的结果:
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
我们来看一下编译器跑出的结果:
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
看一下程序运行的结果:
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”
康康程序运行的结果:
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,本次下饭试题就先到这里啦
觉得不戳请点赞关注一下嘞,喜上加喜可是再好不过啦~