• 【C语言】指针的进阶(四)—— 企业笔试题解析


    笔试题1:

    1. int main()
    2. {
    3. int a[5] = { 1, 2, 3, 4, 5 };
    4. int* ptr = (int*)(&a + 1);
    5. printf("%d,%d", *(a + 1), *(ptr - 1));
    6. return 0;
    7. }

    【答案】在x86环境下运行

    bf08dbd262ea403fba61cb3d48cceedc.png

    【解析】

    &a是取出整个数组的地址,&a就表示整个数组,因此 &a + 1就是跳过一整个数组指向数组后方,因为这是一个数组的地址,不能够直接赋值给整型指针,所以需要将该地址强制转换为整型指针,才能给指针ptr接收,此时指针ptr就指向了数组后方。

    因此*(a+1)就等于a[1]自然是2,而*(ptr-1)就是向前挪动一个整型指向5

    0587d66d938341e38418967fe08c23ed.png


     

     笔试题2:

    1. struct Test
    2. {
    3. int Num;
    4. char* pcName;
    5. short sDate;
    6. char cha[2];
    7. short sBa[4];
    8. }*p;
    9. //假设p 的值为0x100000。 如下表表达式的值分别为多少?
    10. //已知,结构体Test类型的变量大小是20个字节
    11. //0x开头的数组是16进制的数字
    12. int main()
    13. {
    14. p = (struct Test*)0x100000;
    15. printf("%p\n", p + 0x1);
    16. printf("%p\n", (unsigned long)p + 0x1);
    17. printf("%p\n", (unsigned int*)p + 0x1);
    18. return 0;
    19. }

    【答案】在x86环境下运行

    1747cc85ab514a468a1b821f9734ae72.png

    【解析】

    %p:把要打印的数以地址的形式来打印。

    • p是结构体指针,p + 1 就是跳过一个结构体的大小即20个字节,又因为%p是以16进制地址形式打印,10进制的20等于16进制的14,因此结果为00100014。
    • p是结构体指针,被强制类型转换成unsigned long,整数+1就是+1,因此结果为00100001。
    • p是结构体指针,被强制类型转换成unsigned int*,在x86环境下,只要是指针大小就为4,那么指针+1就是跳过4个字节,因此结果为00100004。

     

    笔试题3:

    1. int main()
    2. {
    3. int a[4] = { 1, 2, 3, 4 };
    4. int* ptr1 = (int*)(&a + 1);
    5. int* ptr2 = (int*)((int)a + 1);
    6. printf("%x,%x", ptr1[-1], *ptr2);
    7. return 0;
    8. }

    【答案】在x86环境下运行,小端存储

    9e6c946adce74f80bc920c7d7e6074b2.png

    【解析】

    该题涉及到大小端存储的问题,往期博客中有进行探讨,这里就不多赘述,如有兴趣或者不懂的可前往:点击前往

    &a是取出整个数组的地址,&a就表示整个数组,因此 &a + 1就是跳过一整个数组指向数组后方,因为这是一个数组的地址,不能够直接赋值给整型指针,所以需要将该地址强制转换为整型指针,才能给指针ptr接收,此时指针ptr1就指向了数组后方。

    ptr1[-1]等价于*(ptr1-1),表示ptr1向前挪动一个整型指向4,结果就为4

    a被强制转换成int,即首元素地址被转换成int,整型+1就是+1。假设a的地址是0x100000,此时+1就等于0x100001,然后再强制类型转换为整型指针并赋值给ptr2。

    *ptr2整型指针访问四个字节(即红框所框处),此时为内存存放的图解,需要转成真实值即0x02000000。

    e06e99ac553d4962bd5c88c4808f44be.png


     

    笔试题4:

    1. int main()
    2. {
    3. int a[3][2] = { (0, 1), (2, 3), (4, 5) };
    4. int* p;
    5. p = a[0];
    6. printf("%d", p[0]);
    7. return 0;
    8. }

    【答案】

    27284d0809034786bd770b8160012e62.png

    【解析】

    此题为细节题,过程不难。需要注意的是题中的是小括号,( , )即逗号表达式。a[3][2]中实际存储的是{1,3,5}。将a[0]赋值给p指针,即p指向数组第一行地址,再通过p[0]找到第一行第一个元素即1。

    fdb13d132c7a400c95d77a75f7978b87.png


     

    笔试题5:

    1. int main()
    2. {
    3. int a[5][5];
    4. int(*p)[4];
    5. p = a;
    6. printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
    7. return 0;
    8. }

    【答案】 

    bc7173a0cab046f3962b963bdbeee0aa.png

    【解析】

    a的类型是int (*) [5] ——— 即a指针指向一个大小为5的数组

    p的类型是int (*) [4] ——— 即p指针指向一个大小为4的数组

    当把a所指的地址赋值给指针p之后,a p指向同一地址,但是由于a认为自己指向的是5个元素,而p认为自己指向的是4个元素,这就会导致它们就算下标相同时访问到的内容也是不一样的,如图所示。

    e44a901257634dfd93cb8b5a2d2dac22.png

    随着数组下标的增长,地址是由低到高的变化,并且指针和指针相减的绝对值得到的是指针之间的元素个数,而当取出p[4][2]和a[4][2]的地址之后相减,就是小地址减去大地址,得到一个负数,就是-4。

    %p是打印地址,会认为内存中存储的补码就是地址,所以就是打印-4的补码

    原码:10000000 00000000 00000000 00000100

    反码:11111111  11111111  11111111   11111011

    补码:11111111  11111111  11111111   11111100

    以%p地址的形式打印补码转成16进制表示:FF FF FF FC

    而-4以%d形式打印就是-4

    对于原码反码补码的内容,往期博客有所提及,感兴趣的可以前往:立即前往


     

    笔试题6:

    1. int main()
    2. {
    3. int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    4. int* ptr1 = (int*)(&aa + 1);
    5. int* ptr2 = (int*)(*(aa + 1));
    6. printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));
    7. return 0;
    8. }

    【答案】

    b0946fae13c94456b4c58b6ea0c7b0b5.png

    【解析】

    • &aa表示整个数组的地址,&aa+1跳过一整个二维数组,指向数组之后,再强制类型转换成整型指针赋值给ptr1。此时*(ptr1-1)就等于ptr1向前挪动一个整型并解引用找到10。
    • aa不是单独放在sizeof()内,因此aa表示首元素地址,aa又是二维数组,因此aa的首元素地址就是一个大小为5的数组,*(aa+1)等价于aa[1],表示aa跳过一个大小为5的数组指向下一个大小为5的数组再解引用找到该数组再赋值给整型指针ptr2。此时*(ptr2-1)就等于prt2向前挪动一个整型并解引用找到5。

    a40878d02f114c0dbc7857eb3d8e20e0.png


     

     笔试题7:阿里巴巴笔试题

    1. int main()
    2. {
    3. char* a[] = { "work","at","alibaba" };
    4. char** pa = a;
    5. pa++;
    6. printf("%s\n", *pa);
    7. return 0;
    8. }

    【答案】

    b26809836bb84466b456f6701806961d.png

    【解析】

    a是一个字符指针数组,每个元素的类型都为char*。

    pa是一个指向char*字符指针的指针,即二级指针。

    pa++即是向前跳一个char*的大小,即指向a[1]的地址,*pa就是找到a[1]指向的元素at的首地址。

    c554e8a79b32413cad6f3b85439b1c7d.png


     

     笔试题8:

    1. int main()
    2. {
    3. char* c[] = { "ENTER","NEW","POINT","FIRST" };
    4. char** cp[] = { c + 3,c + 2,c + 1,c };
    5. char*** cpp = cp;
    6. printf("%s\n", **++cpp);
    7. printf("%s\n", *-- * ++cpp + 3);
    8. printf("%s\n", *cpp[-2] + 3);
    9. printf("%s\n", cpp[-1][-1] + 1);
    10. return 0;
    11. }

    【答案】

    0269ce58931f48ba9731019198b1a6bf.png

    【解析】

    本题是该篇博客中最难的一题,因为涉及到三级指针,并且还使用指针自增1自减1的方法去永久改变指针指向,会导致如果一步错,就会步步错的结局,因此需要非常仔细的去画图理解。

    534e6f6a5d3b47a0b9b9f2d5f6c82311.png

    cpp存放char**类型的数据。 【** ++cpp】,cpp先自增1,跳过一个char**类型指向下一个元素的地址,如下图。

    此时再对cpp进行解引用,找到cp[1],再解引用,找到c[2]即P的地址。此时%s打印出来就是POINT

    bc3efd36e3d94f8aac45ab9cb45665be.png

     【* -- * ++cpp + 3】,加号优先级是最低的,所以最后算。cpp先自增1跳过一个char**大小指向下一个元素的地址,如下图。

    此时再对cpp进行解引用,找到的是cp[2]即指向了c+1这块空间,再自减1则指向了c这块空间,再解引用找到了c[0]即ENTER中第一个E的地址,此时+3跳过三个char,最后指向ENTER中第二个E的地址,此时用%s打印出来就是ER

    6ba71070920e48089caae31e1c44e167.png

    *cpp[-2] + 3】等价于【* *(cpp-2)+3】。与前面的cpp自增自减不同,cpp-2只是表达式并不会改变cpp的指向。

    cpp[-2]指向cp[0],再解引用找到c[3],c[3]存放的是FIRST中F的地址,此时+3跳过三个字母指向FIRST中S的地址,再用%s打印出来就是ST

    cd95f7d2863041219ba6141144ec6954.png

    【 cpp[-1][-1] + 1】等价于【*(*(cpp-1) -1) + 1】。

    *(cpp-1)找到c+2,c+2再-1就是c+1的地址,再进行解引用找到NEW中N的地址,再+1找到E的地址,此时用%s打印出来就是EW

    cc7c4ca938da4feaa6d85f80f54e44d4.png

     


     到此,这几道企业笔试题就讲解完成,希望能对你们有所帮助,也欢迎大家在评论区进行讨论。

    如果觉得作者写的不错,求给博主一个大大的点赞支持一下,你们的支持是我更新的最大动力!

    如果觉得作者写的不错,求给博主一个大大的点赞支持一下,你们的支持是我更新的最大动力!

    如果觉得作者写的不错,求给博主一个大大的点赞支持一下,你们的支持是我更新的最大动力!

     

  • 相关阅读:
    方舟开服需要知道的那些事
    Spring Cloud Gateway快速入门(一)——网关简介
    jenkins使用 mvnd 加速maven 项目打包
    如何介绍自己的项目经验?
    投稿开奖丨轻量应用服务器征文活动(5月)奖励公布
    Docker常用命令
    Flutter 应用程序中的 Quick Actions
    富文本编辑器(wangEdit)+(jquery.wordexport)实现web版在线编辑导出
    浏览器交互:Cookies、事件、浏览历史
    数字藏品系统开发,基于区块链智能合约技术
  • 原文地址:https://blog.csdn.net/zzzzzhxxx/article/details/133161104