上一篇我们也介绍了指针的笔试题,这一篇我们趁热打铁继续讲解8道指针更有趣的笔试题,,让大家更加深刻了解指针,从而也拿下【C语言】指针这个难点!
本次解析是在x86(32位)平台下进行
文章目录
- 所需储备知识
- 笔试题1
- 笔试题2
- 笔试题3
- 笔试题4
- 笔试题5
- 笔试题6
- 笔试题7
- 笔试题8
- 总结:
所需储备知识
- sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
- &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
- 除此之外所有的数组名都表示首元素的地址。
- 指针关系运算
笔试题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跳过一个字节 .答案是->0x100001printf(“%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);
- 先加加cpp,此时cpp指向了c+2
- 在解引用找到c+2
- 在解引用找到ROINT
答案是->ROINT. 内存部图如下(1)
- 2:
printf(“%s\n”, * – – * ++cpp+3);
- 先++cpp ,此时cpp指向了c+1
- 在解引用找到了c+1
- 再减减c+1 变成了c
- 解引用C 找到了ENTER
- ENTER+3 指向了E
最后以%s打印 答案是->ER 。内存部图如下(2)
- 3:
printf(“%s\n”, *cpp[-2]+3);
cpp[-2] = * (cpp-2)
- cpp[-2] , 此时cpp找到了c+3并解引用(cpp并没有指向他)
- c+3在解引用找到了 FIRST
- FIRST+3 指向了S
最后以%s打印 答案是->ST 。内存部图如下(3)
- 4:
printf(“%s\n”, cpp[-1][-1]+1);
cpp[-1][-1] = *( *(cpp-1)-1)
- *(cpp-1) 此时指向了 c+2 (cpp并没有指向他)
- *( *(cpp-1)-1) 把c+2再减1 (变成c+1)在解引用找到了NEW
- NEW + 1 指向了E
最后以%s打印 答案是->EW 。内存部图如下(4)
完!
总结:
把二维数组当成一维数组来看待。题目就没有那么抽象
指针类型加减运算决定了,对指针解引用的时候有多大的权限(能操作几个字节)
如果不是单独放在sizeof()内部,一般的操作都会使指针降一阶
对于抽象的指针一定要画图理解,不然很难做出。