• 【C进阶】指针笔试题解析


    做题之前我们再来回顾一下

    对于数组名的理解:除了以下两种情况,数组名表示的都是数组首元素的地址

    (1)sizeof(数组名):这里的数组名表示整个数组

    (2)&(数组名) :这里的数组名也表示整个数组

    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. }

    【答案】:

    2,5

    【解析】:

    难点:&a+1的含义

    &a,数组名和&单独结合,此时的a代表整个数组的地址

    +1也就跳过整个数组的地址(&a的类型为数组指针:int (*)【5】)

    a指向的是数组首元素

    +1也就是指向第二个元素

    【图解】:

    a80e9f41f04d4e54bf46cbd24d21ab0c.jpeg


     2.假设p 的值为0x100000。 如下表表达式的值分别为多少?

    //已知,结构体Test类型的变量大小是20个字节(32位环境下)

    1. struct Test
    2. {
    3. int Num;
    4. char* pcName;
    5. short sDate;
    6. char cha[2];
    7. short sBa[4];
    8. }*p;
    9. int main()
    10. {
    11. printf("%p\n", p + 0x1);
    12. printf("%p\n", (unsigned long)p + 0x1);
    13. printf("%p\n", (unsigned int*)p + 0x1);
    14. return 0;
    15. }

    【答案】:

    10000014

    10000001

    10000004

    【解析】:

    结构体的最后*p表示:定义了一个struct类型的指针变量p

    %p表示:以地址的形式打印

    eg:32位打印出来也就是00 00 00 00(前面的0不会省,就是打印出8位

    %x表示:以十六进制的形式打印(和%p不一样,最前面的0会省略

    (1)结构体指针+1,表示跳过整个结构体,因为结构体的大小为20个字节,那么就是地址+20

    ,0x十六进制表示0x10000014(14表示的十六进制也就是十进制的20)

    %p形式打印也就是10 00 00 14

    (2)指针类型变为unsigned long正常数值类型(要注意这里不再是指针类型)

    正常数值+1也就是10 00 00 01

    %p形式打印也就是10 00 00 01

    (3)struct类型的指针变为unsigned int*类型的指针

    int类型指针+1,表示跳过一个int类型,也就是跳过4个字节

    %p形式打印也就是10 00 00 04


     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. }

    【答案】:

    4,2000000

    【解析】:

    (1)&a,数组名和&单独结合,此时的a代表整个数组的地址

    +1也就跳过整个数组的地址(&a的类型为数组指针:int (*)【4】)

    再强转为int *类型,ptr1[-1]==*(ptr1-1),也就是4

    (2)难点:怎么理解(int*)((int)a + 1) (简单来说就是数值+1转为地址+1个字节)

    a单独代表数组首元素,强转为int类型,再+1也就是数值+1

    eg:假设a的地址是0x0012ff40(十进制也就是1244992)

    那么a+1也就是0x0012ff41(十进制1244993)

    我们发现就是多了一个字节(内存中一个地址对应一个字节)

    在vs编译器中,数值在内存中是小端存储,那么a数组从低地址到高地址存储就是

    01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00(注意不要写成了10 00 00 00)

    a变为数值+1也就是+一个字节,再把加一个字节后的a当作一个地址,那么a也就是跳过了一个字节,指向了01后面的00位置,即ptr2指向了01后面的00位置

    int *ptr2解引用也就访问4个字节,也就是00 00 00 02这4个字节

    转化为数值也就是0x02 00 00 00,那么以%x打印也就是2000000


     4.程序的结果是什么?

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

    【答案】:

    1

    【解析】:

    难点:数组定义的括号不是{ },而是()

    {   {   }, {  }  }中里面的{  }表示:数组初始化的行列元素

    {   (  ), (  )}中的( )表示:逗号表示式

    第一行逗号表达式计算完也就是int a[3][2] = { 1, 3, 5 };

    那么p【0】==*(p+0)也就是数组的第一个元素1


     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. }

    【答案】:

    FFFFFFFC,-4

    【解析】:

    一个元素为4的数组指针指向一个5行5列的二维数组,也就是将这个数组每4个元素放一块,看图解(实际上二维数组也是连续存放的,但是为了便于理解,还是画成了行和列)

    指针-指针得到的是两个指针之间的元素个数

    &p[4][2] - &a[4][2]也就是-4

    &d打印就直接打印数值-4

    对于%p打印地址,在内存中存储的补码就是地址(fffc)

    原码:1000 0000 0000 0000 0000 0000 0000 0100

    反码:1111 1111 1111 1111 1111 1111 1111 1011

    补码:1111 1111 1111 1111 1111 1111 1111 1100

    转为十六进制也就是F F F F F F C

    9175d5c7273a4695be760857d215088d.png


     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. }

    【答案】:

    10,5

    【解析】:

    (1)&aa是二维数组地址,&aa+1跳过整个二维数组,元素10后面的地址

    ptr1-1是元素10的地址,*(ptr1-1)是元素10

    (2)aa是二维数组名,是第一行一维数组地址

    aa+1跳过第一行数组,到第二行数组地址 

    *(aa + 1)是第二行的首元素地址

    ptr2 - 1是元素5的地址,*(ptr2 - 1)是元素5


     7.程序的结果是什么?

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

    【答案】:

    at

    【解析】:

    a是一个指针数组,里面存放了元素的地址

    pa指针变量指向了a数组的第一个元素(第一个字符串)

    二级指针pa++也就是跳过一个a【0】

    那么pa就指向了a【1】(也就是第二个字符串)

    二级指针char **pa怎么理解:

    *pa这里的*表示pa为指针

    char *代表pa指向的对象类型为char *类型


    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. }

    【答案】:

    POINT
    ER
    ST
    EW

    【解析】:

    88edb54f58f24d69a2698bdebb0a8de0.jpg

    (1)cpp+1指向cp的第二个元素,

    一次解引用找到c+2

    两次解引用找到的就是POINT

    (2)cpp再次++指向cp的第三个元素

    注意:这里cpp++是接着上次的位置继续++

    一次解引用找到c+1

    c+1再--得到c(数值--)

    c再第二次解引用找到ENTER的首地址

    再+3跳过3个字节指向E的地址

    最后从这个地址开始打印

    (3)*cpp[-2] ==**(cpp-2) (这里是相当于,但是cpp并没有-2)

    cpp-2指向cp的第一个元素,

    一次解引用找到c+3

    两次解引用找到的就是FIRST

    再+3,跳过3个字节指针指向S的地址

    最后从这个地址开始打印

    (4)cpp[-1][-1]==*(*(cpp-1)-1)

    cpp-1指向cp的第二个元素

    一次解引用找到c+2

    (c+2)-1得到c+1

    第二次解引用找到的就是NEW中N的地址

    再+1跳过一个字节,指向E

    最后从这个地址开始打印


    本次内容就到此啦,欢迎评论区或者私信交流,觉得笔者写的还可以,或者自己有些许收获的,麻烦铁汁们动动小手,给俺来个一键三连,万分感谢 ! 

    87e8203d24d44ae2a0fc486087c20bbd.jpeg

  • 相关阅读:
    【Java基础面试十】、何对Integer和Double类型判断相等?
    2022年天猫淘特淘宝双11超级红包跨店满多少减多少活动时间是从什么时候开始的可以领取淘宝天猫淘特2022双十一超级红包优惠券返利及跨店满减优惠活动?
    Android cannot resolve constructor intent解决
    Java 集合 - Map 接口
    一切都在变
    工具篇 | WSL使用入门教程以及基于WSL和natApp内网穿透实践 - 对比VMWare
    Linux CentOS 8.x 生成rsa公私密钥
    frp内网穿透服务搭建
    lua基础之debug
    Mathematica强制将函数的自变量由符号转为数值
  • 原文地址:https://blog.csdn.net/qq_73017178/article/details/133187984