指针笔试题
1. 练习1
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int* ptr = (int*)(&a + 1);
printf("%d,%d", *(a + 1), *(ptr - 1)); 2 5
return 0;
}
分析:
如图所示,&a + 1为数组最后一个元素的邻接位置地址(跳过了整个数组),所以ptr-1为数组最后一个元素,对其解引用为5;
a为数组首元素地址,a+1为数组第二个元素地址,对其解引用为2;
2. 练习2
//此结构体的大小是20个字节(结构体内存对齐)
struct Test
{
int Num;
char *pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
int main()
{
printf("%p\n", p + 0x1); // 0x100014
printf("%p\n", (unsigned long)p + 0x1);//0x100001
printf("%p\n", (unsigned int*)p + 0x1);//0x100004
return 0;
}
分析:
由于p为结构体指针(大小为20字节),所以p+0x1相当于0x1000000+14(20的16进制为14),即0x100014;
对于(unsigned long)p + 0x1,p已经被强制转换为unsigned long,p+0x1相当于0x1000000+1,即0x100001;
对于(unsigned int*)p + 0x1,p已经被强制转换为unsigned int*,p+0x1相当于0x1000000+4(指针大小4字节),即0x100004;
3. 练习3
int main()
{
int a[4] = { 1, 2, 3, 4 };
int *ptr1 = (int *)(&a + 1);//4
int *ptr2 = (int *)((int)a + 1);//20 00 00 00
printf( "%x,%x", ptr1[-1], *ptr2);
return 0;
}
分析:
对于ptr1[-1],如下图所示,&a得到数组首元素地址,&a + 1得到数组最后一个元素的邻接元素地址,ptr[-1]相当于指针左移一位,即图示③地址,即为4;
对于*ptr2,如下图所示,a数组地址,将其强转为int后+1,实际指向为下图②所指位置,又因为将其强转为int *,打印时打印四个字节,即下图③,又因为电脑为小端字节序存储,打印时逆序打印,即20 00 00 00;
4. 练习4
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]为第一行数组首地址,将其赋给p,即p为第一行元素首地址,p[0]即第一行第一个元素1;
5. 练习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;
}
分析:
a为数组地址,p=a,所以p也为首元素地址,如上图①所示,a[4][2]位置如上图③所示;
由于数组a为5*5矩阵,但是数组指针p指向的数组大小为4,所以p[4][2]位置如上图②所示;
所以 &p[4][2] - &a[4][2] (指针减指针为中间元素个数),又因为 &p[4][2]在前 &a[4][2]在后,所以按照%d的形式打印结果为:-4,按照%p形式打印,结果为-4在内存中的表示:FFFFFFFC
-4–>内存地址解析:
原码:10000000 00000000 00000000 00000100
反码:11111111 11111111 11111111 11111011
补码:11111111 11111111 11111111 11111100
1111 1111 1111 1111 1111 1111 1111 1100
F F F F F F F C
6. 练习6
int main()
{
int a[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int* ptr1 = (int*)(&a + 1); //10
int* ptr2 = (int*)(*(a + 1));//5
printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));
return 0;
}
分析:
&a 表示数组地址,&a + 1表示数组最后一个元素邻接位置地址,如图②所示;
由于a为二维数组,所以其表示数组第一行元素地址,a+1表示第二行元素地址,所以a+1如上图①所示。
7. 练习7
int main()
{
char* a[] = { "work","at","alibaba" };
char** pa = a;
pa++;
printf("%s\n", *pa);//at
return 0;
}
分析:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9YxHlpKM-1621435660776)(https://i.loli.net/2021/05/19/ns1D8abSmUlYodP.png)]
如上图所示,指针数组a保存的为"work",“at”,“alibaba” 三个字符串的首地址,pa为二级指针,指向指针数组a的首地址,如图①所示,当pa++时,指针向后移动一个位置,到达如图②所示位置,即指向at首元素的地址的指针,由于打印时按照%s打印,所以打印出“at”;
8. 练习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);//POINT
printf("%s\n", *-- * ++cpp + 3);//ER
printf("%s\n", *cpp[-2] + 3);//ST
printf("%s\n", cpp[-1][-1] + 1);//EW
return 0;
}
分析:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fXRqr3xh-1621435660777)(https://i.loli.net/2021/05/19/MmNEc3R6FLZXxlh.png)]
如上图所示,指针数组c保存的为"ENTER",“NEW”,“POINT”,“FIRST” 五个字符串的首地址,如上图①所示;
cp为二级指针数组,指向指针数组c的每个元素(类型为char *)的地址,如上图②所示;
cpp为三级指针,指向二级指针数组的首地址,如上图③所示;
8.1 解析一
对于****++cpp**,三级指针cpp自增后(如上图④所示),再对其进行两次解引用,最终得到POINT的首地址,打印后得到“POINT”;
8.2 解析二
对于***-- * ++cpp + 3**,
- 由于自增自减运算符优先级高于+ -,所以先对cpp进行++运算,(由于经过上一步自增,cpp已经指向下图①位置)如下图②所示;
-
接着对其地址值解引用(* ++cpp),得到c+1,但又因为对其自减(-- (* ++cpp)),得到c,起始状态下,其指向下图①所指位置(c+1),经过运算后,其指向下图②所指位置(c),接着对其进行解引用操作*(-- (* ++cpp)),得到元素E的地址;
-
现在指针已经指向元素E的地址,如下图①所示,将其+3后指针移动到如下如②所示位置,将其按照%s打印后,结果为 ER
8.3 解析三
对于*cpp[-2] + 3
- cpp[-2],首先将指针后移两个位置,如下图②所示,并得到(c+3)的地址,如下图③所示;
- *cpp[-2] ,再将其解引用得到FIRST首元素F的地址,如图④所示;
- *cpp[-2] + 3,将其+3相当于指针后移三位,即指向S,如图⑤,将其按照%s打印后,结果为 ST
8.4 解析四
对于cpp[-1][-1] + 1
- cpp[-1]为将三级指针cpp位置前移一位的结果,其地址入下图①,结果如下图②所示;
- cpp[-1][-1]如下图④所示,得到N的地址;
- cpp[-1][-1] + 1,将N的地址右移一位,得到E的地址,如下图⑤所示,将其按照%s打印后,结果为 EW
17-1621435660780)]
8.4 解析四
对于cpp[-1][-1] + 1
- cpp[-1]为将三级指针cpp位置前移一位的结果,其地址入下图①,结果如下图②所示;
- cpp[-1][-1]如下图④所示,得到N的地址;
- cpp[-1][-1] + 1,将N的地址右移一位,得到E的地址,如下图⑤所示,将其按照%s打印后,结果为 EW