指针相关笔试题(附解析)—-史上最强C语言教程
目录
10. 指针相关的笔试题及解析
10.1 笔试题1
题目:
#include<stdio.h> int main() { int a[5] = { 1, 2, 3, 4, 5 }; int* ptr = (int*)(&a + 1); printf("%d,%d", *(a + 1), *(ptr - 1)); return 0; } //程序的结果是什么?
运行截图:
文字解析:
a是数组首元素的地址,类型为int *,数组首元素的地址+1,跳过一个整型元素,即数组第二个元素的地址,对其进行解引用就是a[1],所以输出结果为2。
&a是数组的地址,数组地址+1就是跳过一个数组大小的地址,此时类型为int (*)[5],对其进行强制类型转换后,类型变为了(int*),-1就回退一个整型元素的大小,此时指针指向的元素是5。
画图解析:
10.2 笔试题2
代码:
程序的结果是什么? #include<stdio.h> struct Test { int Num; char* pcName; short sDate; char cha[2]; short sBa[4]; }*p; //假设p 的值为0x100000。 如下表表达式的值分别为多少? //已知,结构体Test类型的变量大小是20个字节 int main() { p = 0x100000; printf("%pn", p + 0x1); printf("%pn", (unsigned long)p + 0x1); printf("%pn", (unsigned int*)p + 0x1); return 0; }
运行截图:
代码解析:
p的类型为(struct Test*),所以+1后跳过12个字节,在进行(unsigned long)强制类型转换后,+1后跳过1个字节,在进行(unsigned int*)强制类型转换后,加1跳过一个整型的大小,即4个字节。
10.3 笔试题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; }
运行截图:
画图解析:
代码解析:
&a得到的是数组a的地址,+1即跳过一个数组的大小,对其进行强制类型转换后类型变成了int*,ptr[-1]=ptr+(-1),因为此时的类型是int*,所以回退的是一个整型的空间,因为其类型是int*,一次能够访问4个字节,所以对其进行输出后的结果是4。
对a进行强制类型转换后a的类型就变成了int,+1就是简单的加1,跳过一个字节,跳过一个字节在上图中体现的就是跳过两位数组,此时指向了00,因为其类型是int*,一次能够访问4个字节,所以取出的数组就是00 00 00 02,因为其是小端存储,并且是按照16进制形式进行打印,所以打印结果是20 00 00 00。
10.4 笔试题4
代码:
#include<stdio.h> #include <stdio.h> 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},即最终数组初始化的结果就像下方画的图一样!
a[0]是第一行的数组名,而此处的数组名代表的是第一行第一个元素的地址 ,即a的地址,p[0]代表的是*(p+0),p+0之后并未发生改变,仍然是第一行第一个元素的地址,且此时的类型也是int*,对其进行解引用之后就是数组第一行第一个元素,即1。
注意:
1、()中的内容是逗号表达式!
2、p[0] = *(p+0)。
10.5 笔试题5
代码:
int main() { int a[5][5]; int(*p)[4]; p = a; printf("%p,%dn", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]); return 0; }
运行截图:
代码解析:
10.6 笔试题6
代码:
#include<stdio.h> 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; }
运行截图:
代码解析:
&aa+1就是跳过一个二维数组,指向10的后面,此时的类型是int (*)[2][5],强制类型转换后类型变成int*。此时使ptr1-1就是回退一个整型元素,指向元素10,解引用之后得到元素10。
aa是二维数组的数组名,数组名是数组首元素的地址,即第一行的地址,aa+1跳过一行,此时是数组第二行的地址,对其进行解引用之后就是第二行的数组名,再进行请值类型转换后类型变成了int *(当然,此处就算不进行强制类型转换类型也是int*,因为第二行数组名是一维数组,数组名代表首元素的地址,其类型自然就是int*)。此时使ptr2-1就是回退一个元素,指向元素5,解引用之后得到元素5。
10.7 笔试题7
代码:
#include <stdio.h> int main() { char* a[] = { "work","at","alibaba" }; char** pa = a; pa++; printf("%sn", *pa); return 0; }
运行截图:
代码分析:
如何理解char *a = { "work","at","alibaba" };这行代码呢?
其实这行代码等价于下面的这三行代码:
a[0] = "work";
a[1] = "at";
a[2] = "alibaba";
其中a[0]、a[1]、a[2]的类型都是字符指针类型,都是存储的后面字符串常量的首地址。
a代表的是数组首元素的地址,即a[0]的地址,pa的类型是char**,即二级指针,+1后跳过一个char *类型,即pa++后pa指向的是a[1],即存储的是a[1]的地址,对其进行解引用之后得到的就是字符串常量中"at"中a的地址。
10.8 笔试题8
代码:
#include<stdio.h> int main() { char* c[] = { "ENTER","NEW","POINT","FIRST" }; char** cp[] = { c + 3,c + 2,c + 1,c }; char*** cpp = cp; printf("%sn", **++cpp); printf("%sn", *-- * ++cpp + 3); printf("%sn", *cpp[-2] + 3); printf("%sn", cpp[-1][-1] + 1); return 0; }
运行截图:
代码分析:
对于**++cpp,这个表达式来说,*和++优先级相同,但是结合性是自右向左,所以先进行++操作,++后cpp指向cp[1],即图中的c+2,对其进行一次解引用之后可以得到cp[1]中存放的地址,二次解引用之后可以得到c[2]中存放的即"POINT"首元素即P的地址,所以打印结果应该是POINT。
对于*--*++cpp+3这个表达式来说,,首先是cpp自增1,cpp指向了图中的c+1(前一个表达式中已经自增过一次了),此时进行解引用之后得到了c+1,然后进行自减,此时就变成了c,然后再对其进行解引用之后得到了"ENTER"中字符E的地址 ,此时再进行加3,然后得到的就是E的地址,所以输出结果为ER。
对于*cpp[-2]+3这个表达式来说,这个表达式等价于**(cpp-2)+3,首先是cpp-2,因为之前cpp指向的是图中的c+1,-2之后指向的是c+3,对其进行解引用之后得到的是c[3]的地址,然后进行解引用之后就得到了FIRST的地址,然后对其进行+3,就得到了S的地址,所以打印结果为ST。
对于cpp[-1][-1]+1这个表达式来说,这个表达式等价于*(*(cpp-1)-1)+1,首先cpp-1,从指向c+1这个位置指向 了c+2这个位置,解引用得到的就是c+2,然后进行-1,变成了了c+1,然后解引用之后得到了c[1]的内容,其实是N的地址,N的地址+1得到的就是E的地址,所以输出的结果是EW。