本篇博客讲解指针经典笔试题,但是不会太详细,只会大概讲解一下,如果好好理解的话包能看懂的,那么就开始学习吧!
题目1:
int main()
{
int a[5] = { 1,2,3,4,5 };
int* ptr = (int*)(&a + 1);
printf("%d %d", *(a + 1), *(ptr - 1));//*(a+1)? *(ptr-1)?
return 0;
}
答案解析:打印的结果是2和5,首先a是数组名,*(a+1)等价于a[1],所以就是解引用第二个元素的地址,结果为2。prt得到的是&a+1数组a最后一个元素的后面的地址。所以-1就拿到了元素5。
题目2:
//在x86环境下
//假设结构体的大小是20个字节
//程序输出的结构是啥?
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;
}
答案解析:假设结构体的大小是20个字节。那指针变量p就是一个+1跳过20个字节的指针。
p+0x1跳过20个字节,结果为:0x100014,4个16^0也就是4个1和1个16^1也就是一个16,16+4为20.就是跳过的那20个字节。
(unsigned long)p + 0x1是将p存储的地址信息0x100000强转为一个整型,整型+1就是+1,所以结果为:0x100001。
(unsigned int*)p + 0x1是将p存储的地址转换为int*类型的地址,int*类型的地址+1跳过4个字节。所以结果为:0x100004
题目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;
}
答案解析:ptr1还是跳到数组最后一个元素的地址,所以ptr[-1]就是*(ptr-1)所以结果为:4
ptr是将a首元素的地址转换成int+1在转换为int*类型。只跳过了一个字节。因为整型的值是4个字节,每个字节都有地址。但是只跳过了一个字节。解引用时访问4个字节拿到的是16进制00 00 02 00,打印后的结果是:2000000.
本题有关大小端字节序,如果想要了解作者后期会写一篇数据在内存中是如何存储的。里面就有介绍大小端字节序
题目4:
int main()
{
int a[3][2] = { (0,1),(2,3),(4,5) };
int* p = a[0];
printf("%d", p[0]);
return 0;
}
答案解析:打印结果为:1,为什么?不应该是0吗?0不是最前面的元素吗?
首先注意里面的数组是由小圆括号括起来的,小圆括号括起来的叫逗号表达式。逗号表达式是从左向右依次计算,整个表达式的结果是最后一个表达式的结果。所以真正初始化的元素就是1,3,5后面自动初始化为0。所以结果为1。
题目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;
}
答案解析:a是二维数组,每个元素一维数组有5个int类型的元素,所以+1跳过一个元素一维数组5个int类型的元素的大小。将a的首元素地址赋值给数组指针。数组指针明确表示接收的是4个int*类型元素的一维数组。+1跳过4个int*类型的大小。所以给出以下图:
它们之间相差4个int*类型元素的大小,指针-指针就是求出两个指针之间的元素个数。但是低地址-高地址得到的就是负数,也就是负数的元素个数。所以结果为-4。-4%d打印和%p打印是截然不同的。%p打印会将-4的二进制补码当作地址转换成16进制就是ff ff ff fc,%d打印的就是-4的。
题目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;
}
答案解析:10,5
为什么是10和5? 首先&aa拿到的是整个数组的地址,+1跳过整个数组。拿到数组最后一个元素的下一个地址后强转为int*初始化给ptr1,ptr1[-1]就拿到了10。aa+1跳过一个一维数组元素来到第二个一维数组元素解引用拿到了第二个一维数组的首元素地址初始化给ptr2,ptr2[-1]拿到的就是5。
题目7:
int main()
{
char* a[] = { "work","at","alibaba" };
char** pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
答案解析:打印结果为at,因为a是数组指针,是存储地址的连续空间,存储地址的变量是二级指针,所以可以用二级指针pa来接收a的首元素地址,pa++来到第二个元素的地址。再*pa解引用拿到存储的地址,通过这个地址打印字符串at。
题目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;
}
答案解析:打印结果为POINT、ER、ST、EW
**++cpp:首先三级指针cpp拿到了二级指针数组的首元素地址,++的优先级高先++cpp来到了第二个元素的地址,解引用拿到了c+2的地址。再解引用拿到c+2的地址处元素"POINT"
*--*++cpp+3:首先++cpp,cpp指向了cp的第三个元素的地址,解引用拿到c+1的位置,--(c+1)就是c的首元素解引用后+3拿到了"ER"
*cpp[-2]+3:首先cpp[-2],得到cp的首元素c+3,解引用拿到c+3空间存储的地址,再+3最后拿到"ST"。
cpp[-1][-1]+1:首先cpp[-1]拿到了c+2的位置,再看cpp[-1][-1]拿到了c+1位置存储的地址,再+1就拿到了"EW"
以上就是笔试题解析的全部内容了,本篇博客也就结束了,再见。