前言
上期关于指针的文章,我们用多个试题对指针与数组的关系进行了全面多方位的讲解。这期文章我们将用八道经典的指针笔试题来全面多方位来了解指针。
第一题
答案是多少呢?为什么是这个答案呢?
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是数组首元素的地址,而&a是取出的整个地址。所以a+1就是第二个元素的地址,解引用就是2。&a+1就是跳过整个数组,在5元素的后面,强制类型为int就意味着它后面地址加减的单位都是int类型。ptr就是&a+1的地址,所以ptr-1就是向前减一个int类型,就指向了5的地址,解引用就是5。
第二题
假设p 的值为0x100000。 如下表表达式的值分别为多少?
已知,结构体Test类型的变量大小是20个字节
struct Test { int Num; char *pcName; short sDate; char cha[2]; short sBa[4]; }*p; int main() { printf("%p\n", p + 0x1); printf("%p\n", (unsigned long)p + 0x1); printf("%p\n", (unsigned int*)p + 0x1); return 0; } struct Test { int Num; char *pcName; short sDate; char cha[2]; short sBa[4]; }*p; int main() { printf("%p\n", p + 0x1); printf("%p\n", (unsigned long)p + 0x1); printf("%p\n", (unsigned int*)p + 0x1); return 0; } struct Test { int Num; char *pcName; short sDate; char cha[2]; short sBa[4]; }*p; int main() { printf("%p\n", p + 0x1); printf("%p\n", (unsigned long)p + 0x1); printf("%p\n", (unsigned int*)p + 0x1); return 0; }
分析
通过提示,我们知道他是一个结构体指针类型,大小为20个字节,地址为0x100000。0x1就是p+1就是加一个20大小的结构体类型,就是0x100000+20=0x100014。unsigned long p 就是将p转化为整型,整型加1就是加1.结果就是0x100000+1=0x100001 unsigned int* p就是将p转化为指针类型,所以p+1就是加了一个指针大小0x100000+4=0x100004
第三题
答案是多少?为什么是这样呢?
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*(&a+1)就是将&a+1的类型转化为int*的类型,ptr[-1]可以理解为*(ptr-1),ptr就是&a+1,所以ptr-1就是减一个int*类型大小,所以就在元素4的地址,解引用就是4 int a就是将地址a转化为整型,整型加1就是加1 ,我们又知道,在内存中一个地址就是一个字节,这里加1,所以a+1指向的内容就是00到02 0x02000000
第四题
答案是什么呢?为什么呢?
#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; }
解析
通过观察:我们可以发现,a数组里面是(),所以是逗号表达式,保留右边的值,所以()里的值是1,2,3未满就补0,后面都是0,所以数组中布局就是画图后的样子。
p是a[0]的地址,而a[0]是代表的第一行,我们可以把a[0]理解为第一行的数组名,所以a[0]是首元素的地址,解引用就是1
第五题
答案是多少呢?为什么是这样呢?
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; }
解析
a数组是一个五行五列的二维数组 a是起始元素的地址 将a的地址赋给p 且q是一个数组指针 p的类型是int[4]。
所以p每加一跳过4个元素 所以p[4]就相当于*(p+4) p的地址就是画图后的地址 两地址相减结果就是中间元素的个数 所以&p[4][2]-&a[4][2]=-4
我们知道电脑中存的都是补码原码10000000000000000000000000000100
反码11111111111111111111111111111011
补码11111111111111111111111111111100
以%P打印就直接把补码当做地址打印了
以%d打印还是-4
第六题
答案是多少呢?为什么是这样呢?
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是首元素的地址,就是第一行的地址 &aa取出的是整个二维数组的地址
ptr1=&aa+1 &aa+1被转化为int*类型 所以ptr-1,就是减去一个int*类型大小,这时ptr指向10元素的地址,解引用为10
aa+1就是第一行的地址加1到第二行的地址,我们发现aa+1的类型也是int*,前面的int*就没用了。就可以理解为:int*ptr=*(aa+1),我们又可以把*(aa+1)理解为aa[1],aa[1]为第二行数组名,就是首元素地址,又因为ptr==*(aa+1)==aa[1],所以ptr-1就是指向元素5,解引用就是5
第七题
答案是多少呢?为什么是这样呢?
#include <stdio.h> int main() { char *a[] = {"work","at","alibaba"}; char**pa = a; pa++; printf("%s\n", *pa); return 0; }
解析
我们要知道数组a里面存的char*类型的元素,是work,at,alibaba首元素的地址
用二级指针存放a的地址,pa==a,pa++==a++,a是首元素的地址,pa++==a++就是第二个元素的地址。
*pa就是at首字母的地址,%s就是通过首字符地址打印这一串字符
第八题
答案是多少呢?为什么是这样呢?
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; }
解析
c数组存放的是字符串首元素的地址,cp数组存放的是这些首元素的地址的地址,cpp存放的是cp的地址。这里我们还要注意一个点:++和--是直接改变了它们的值,会影响后面的计算的。