上一篇我们也介绍了指针的笔试题,这一篇我们趁热打铁继续讲解8道指针更有趣的笔试题,,让大家更加深刻了解指针,从而也拿下【C语言】指针这个难点!
本次解析是在x86(32位)平台下进行

文章目录

  • 所需储备知识
  • 笔试题1
  • 笔试题2
  • 笔试题3
  • 笔试题4
  • 笔试题5
  • 笔试题6
  • 笔试题7
  • 笔试题8
  • 总结:

所需储备知识

  1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
  2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
  3. 除此之外所有的数组名都表示首元素的地址。
  4. 指针关系运算

笔试题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;}//程序的结果是什么?

解析:

int *ptr = (int *)(&a + 1); (&a = int( *)[5]) &a取出了整个数组地址,&a+1跳过整个数组(ptr并没有解引用,所以不会造成越界访问)

*(a + 1) ,a是数组名,数组名代表首元素地址,首元素地址+1,跳到下一个元素2的地址,在解引用。 第一个输出结果答案是->2

*(ptr – 1),ptr此时指向了组最后一个元素地址的下一个地址, -1又回到了最后一个元素地址,解引用。第二个输出结果答案是->5


笔试题2

以下代码结果是什么!?

struct Test{ int Num; char *pcName; short sDate; char cha[2]; short sBa[4];}*p;假设p 的值为0x100000。 如下表表达式的值分别为多少?已知,结构体Test类型的变量大小是20个字节int main(){ printf("%p\n", p + 0x1); printf("%p\n", (unsigned long)p + 0x1); printf("%p\n", (unsigned int*)p + 0x1); return 0;}

解析:
0x1 就是十六进制 1

printf(“%p\n”, p + 0x1);

p是一个结构体指针类型,结构体类型+1 跳过一个结构体大小,单位是字节。
所以p+1跳过20个字节,答案是-> 0x100014 .(十六进制14 代表十进制20)

printf(“%p\n”, (unsigned long)p + 0x1);

p 强制类型转换成(无符号整形),整型+1就是+1.
所以整型p+1跳过一个字节 .答案是->0x100001

printf(“%p\n”, (unsigned int*)p + 0x1);

p 强制类型转换成(整型指针类型),整型int指针+1,跳过一个整型。
答案是->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;}

代码可能在x64位平台下编译不过 ,请换成x86(32)位平台

解析:

int *ptr1 = (int *)(&a + 1);
&a取出整个数组的地址,+1跳过整个数组

int *ptr2 = (int *)((int)a + 1);
a强制类型转换成 (整型),整型+1就是+1,跳过一个字节

printf( “%x,%x”, ptr1[-1], *ptr2);
图析:

所以ptr1[-1]答案->是 4
*ptr2 答案是-> 02 00 00 00


笔试题4

以下代码结果是什么!?

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

解析:

大家看清楚了! {}括号里面的()是逗号表达式,逗号表达式只算最后一个元素,所以int a【3】【2】 实际放的是 {1,3,5,0,0,0}

p = a[0] p是一个指针变量,a【0】 (a没有单独放在sizoef内部,代表的是二维数组首元素地址,二维数组首元素是第一行的地址, 那a是第一行了,a[0] 又是 第一行第一个元素a【0】【0】)。
所以 p[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;}

解析:

图析:


答案是->-4 FFFFFFFC


笔试题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;}

解析:

int *ptr1 = (int *)(&aa + 1);
&aa取出整个数组的地址,+1跳过aa数组,来到10元素后面的位置。

int * ptr2 = (int *)( *(aa + 1));
aa代表首元素地址(二维数组首元素地址是第一行地址),首元素地址+1就是第二个行的地址,在解引用。

*(ptr1 – 1)
ptr1-1 指向了元素10(&a[1][4])的地址, 解引用找到10,答案是->10

*(ptr2 – 1)
ptr2-1 指向了后面5(&a[0][4])的地址,在解引用。答案是->5


以下代码结果是什么!?

笔试题7

#include int main(){ char *a[] = {"work","at","alibaba"}; char**pa = a; pa++; printf("%s\n", *pa); return 0;}

解析:

*pa

其实该题把代码内存部图画出来就很容易解出来了,如下


笔试题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;}

该题比较复杂,要先画出内存部图才分析,不然很难做对

解析:

内该代码存部图(一步步画图分析)

  • 1

printf(“%s\n”, * * ++cpp);

  1. 先加加cpp,此时cpp指向了c+2
  2. 在解引用找到c+2
  3. 在解引用找到ROINT
    答案是->ROINT. 内存部图如下(1)
  • 2:

printf(“%s\n”, * – – * ++cpp+3);

  1. 先++cpp ,此时cpp指向了c+1
  2. 在解引用找到了c+1
  3. 再减减c+1 变成了c
  4. 解引用C 找到了ENTER
  5. ENTER+3 指向了E
    最后以%s打印 答案是->ER 。内存部图如下(2)
  • 3:

printf(“%s\n”, *cpp[-2]+3);

cpp[-2] = * (cpp-2)

  1. cpp[-2] , 此时cpp找到了c+3并解引用(cpp并没有指向他)
  2. c+3在解引用找到了 FIRST
  3. FIRST+3 指向了S
    最后以%s打印 答案是->ST 。内存部图如下(3)
  • 4:

printf(“%s\n”, cpp[-1][-1]+1);

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

  1. *(cpp-1) 此时指向了 c+2 (cpp并没有指向他)
  2. *( *(cpp-1)-1) 把c+2再减1 (变成c+1)在解引用找到了NEW
  3. NEW + 1 指向了E
    最后以%s打印 答案是->EW 。内存部图如下(4)

完!


总结:

  • 把二维数组当成一维数组来看待。题目就没有那么抽象

  • 指针类型加减运算决定了,对指针解引用的时候有多大的权限(能操作几个字节)

  • 如果不是单独放在sizeof()内部,一般的操作都会使指针降一阶

  • 对于抽象的指针一定要画图理解,不然很难做出。