指针笔试题
笔试题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;
}
//程序的结果是什么?
*(a + 1)等同于a[1],第一个是2,a的类型是int [5],&a的类型就是int(*)[5],
是个数组指针。所以给int(*)[5]类型加一,相当于加了一个int [5]的长度。也就
是这个指针直接跳过了a全部的元素,直接指在了刚好越界的位置上,然后转换成了
int *后再减一,相当于从那个位置向前走了一个int,从刚好越界的位置回到了5的
址处,所以第二个是5
笔试题2
由于还没学习结构体,这里告知结构体的大小是20个字节
struct Test
{
int Num;
char *pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
int main()
{
p = (struct Test*)0x100000
printf("%p\n", p + 0x1);
printf("%p\n", (unsigned long)p + 0x1);
printf("%p\n", (unsigned int*)p + 0x1);
return 0;
}
本质上考察指针加1的知识点:
p = 0x100000
p + 0x1 为跳过一个结构体指针(20字节),所以为0x100014
(unsigned long)p + 0x1 为强制类型转换为整型(1048576),整型数字加1(1048577),写成16进制就是0x100001
(unsigned int*)p + 0x1 为强制类型转换为整型指针,加的就是一个指针的大小4/8,就是0x100004/8
而在X86环境下打印,地址是4个字节
答案就是:
0010014
0010001
0010004
笔试题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;
}
ptr1[ -1 ] == *(ptr1-1) 因为ptr1直接指在了刚好越界的位置上,然后转换成了
int *后再减一,相当于从那个位置向前走了一个int,从刚好越界的位置回到了4的
址处,所以是4(int)a + 1 假设a的首元素地址为0x0012ff40,将它强制转换成 int 整型,整型数字加1,就是0x0012ff41,相当于往后移了一个字节,再将它强制转换成 int* 指针类型,假设是小端存储,存储形式就是 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 ,而 *ptr2 解引用就是往后读取4个字节(因为是int *)大小(00 00 00 02)0x02000000,又因为是以%x形式打印,所以就是16进制。答案是2000000
笔试题4
#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;
}
答案是1
注意:(0, 1), (2, 3), (4, 5)是逗号表达式,不是每一行数组的内容,因为二维数组每一行也是一个数组,所以也要用{ },所以{ (0, 1), (2, 3), (4, 5) }表示的其实是{1,3,5},因为a[0] 是第一行的数组名,就是数组第一行的首元素地址(即a[0][0]),p[0] == *(p+0) == *p ,所以就是a[0][0]
笔试题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 相当于将int(*)[ 5 ]类型的指针赋给 int(*)[ 4 ]类型的指针,而p+1 == &p[ 1 ]跨过的是四个整型大小,a+1跨过的是5个整型大小,所以算出&p[4][2] - &a[4][2]为-4(相差4个整型大小),而前面是用%p打印,相当于把-4当作地址,-4在内存中是以补码的形式存储的,就是11111111 11111111 11111111 11111100,16进制就是0xff ff ff fc
所以答案是:0xff ff ff fc,-4
笔试题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;
}
&aa + 1 是整个二维数组的地址+1,就是刚好越界的位置,再强制转换成int *,所以*(ptr1 - 1)就是往前移一个int大小,就是指向9,再解引用
*(aa + 1) == aa[ 1 ] 中aa是二维数组首元素的地址,就是第一行的地址,再加1就是第二行的地址,解引用就变成第二行第一个元素的地址,再强制转换成int *,所以*(ptr2 - 1)就是往前移一个int大小,就是指向5,再解引用
笔试题7
#include <stdio.h>
int main()
{
char *a[] = {"work","at","alibaba"};
char**pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
char *a[] = {"work","at","alibaba"}是将"work","at","alibaba"三个字符串的首元素(w , a , a)的地址放入a数组里
char**pa = a;就是pa指针的地址指向a数组首元素的地址(w)
pa++;就是pa指针的地址指向a数组第二个元素的地址(a)
最后以%s打印,就是从a的地址开始打印字符串,结果就是at
笔试题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;
}
需要画图来看(画指向关系)
char *c[] = {"ENTER","NEW","POINT","FIRST"};是将"ENTER","NEW","POINT","FIRST"四个字符串的首元素(E , N , P , F)的地址放入c数组里
char**cp[] = {c+3,c+2,c+1,c};cp数组每个元素是char**,那么就是四个指针分别指向c的首元素地址+3(c的首元素地址+3又指向F地址),c的首元素地址+2(P),c的首元素地址+1(N),c的首元素地址(E)
char***cpp = cp;cpp就是三级指针,指向cp的首元素地址
printf("%s\n", **++cpp);cpp变成指向cp第二个元素地址的地址(发生变化了),解引用一次就是cp第二个元素(指向P的地址的地址),再解引用一次就是P的地址,最后以%s打印,就是从P的地址开始打印字符串,结果就是POINT
printf("%s\n", *--*++cpp+3);++cpp使cpp变成指向cp第三个元素地址的地址(发生变化了),再解引用得到cp第三个元素(指向N的地址的地址),再进行--,就是cp第三个元素变成指向E的地址的地址(发生变化了),就是存储的从c+1变成c,再解引用得到E的地址,再+3得到第二个E的地址,最后以%s打印,就是从第二个E的地址开始打印字符串,结果就是ER
printf("%s\n", *cpp[-2]+3); *cpp[-2]+3 == **(cpp-2)+3,cpp现在是指向cp第三个元素地址的地址,-2变成指向cp首元素地址的地址,解引用得到c+3,再解引用得到F的地址,再+3得到S的地址,最后以%s打印,就是从S的地址开始打印字符串,结果就是ST
printf("%s\n", cpp[-1][-1]+1);cpp[-1][-1] == *(*(cpp-1)-1),cpp现在是指向cp第三个元素地址的地址,-1变成指向cp第二个元素地址的地址,再解引用得到c+2,再-1得到c+1,再解引用得到N地址,再+1变成E的地址,最后以%s打印,就是从E的地址开始打印字符串,结果就是EW