1. 指针笔试题
1.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 main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int *ptr = (int *)(&a + 1);
//&数组名+1跳过整个数组后指向下一个数组的起始地址
//数组的地址应该存放到数组指针
//这里把它强制类型转换位int*类型
printf( "%d,%d", *(a + 1), *(ptr - 1));//2,5
//a+1解引用是第二个元素,ptr-1解引用等价于a[4]即第五个元素
return 0;
}
1.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;
}
解析:
struct Test
{
int Num;
char *pcName;
short sDate;
char cha[2];
short sBa[4];
}*p; //p为结构体指针
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
int main()
{
printf("%p\n", p + 0x1);
//结构体指针+1跳过一个结构体大小
//已知test类型变量的大小是20个字节+1跳过20个字节
//而0x是十六进制数,因此把20转换为16进制数
//结果为:0x1000014
printf("%p\n", (unsigned long)p + 0x1);
//把地址强制类型转换为无符号整形为:
//1,048,576 再+1,1,048,5767,整形变量+1就是+1
//最后的结果就是0x100001
printf("%p\n", (unsigned int*)p + 0x1);
//把结构体指针p强制类型转换为无符号整形指针类型
//整形指针+1跳过4个字节
//因此结果为:0x100004
return 0;
}
1.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;
}
画图解析:
1.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 main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
//很坑,这里是逗号表达式,表达式的结果是最后一个元素的值
//因此大括号内部的值为:1,3,5
//而这个数组是三行两列:
//第一行:1,3
//第二行:5,0
//第三行:0,0
//不够补0
int *p;
p = a[0];
//a[0]是第一行的数组名
//数组名即首元素地址,也就是&a[0][0]
printf( "%d", p[0]);
//最后解引用得到1
return 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; }
画图解析:
1.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 main()
{
int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int *ptr1 = (int *)(&aa + 1);
//取出整个数组的地址+1跳过整个数组
//指向下一个数组的起始地址
//把它强制类型转换为整形指针
int *ptr2 = (int *)(*(aa + 1));
//aa是首元素地址,也就是第一行的地址
//+1是第二行的地址,解引用访问第二行的数组
//而数组名代表首元素的地址
printf( "%d,%d", *(ptr1 - 1), *(ptr2 - 1));
//ptr1是整形指针-1向前跳过一个整形
//指向的是该二维数组的最后一个元素,解引用就是10
//ptr2指向的是第二行数组的首元素地址
//-1指向第一行数组的最后一个元素地址
//解引用得到5
return 0;
}
1.7 小题
int main()
{
char* a[] = {"work","at","alibaba"};
char* *pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
解析:
int main()
{
char* a[] = {"work","at","alibaba"};
//数组a的每一个元素都是指针来把三个字符串的首地址存起来
char* *pa = a;
//a代表首元素地址,a的首元素是一个char*类型的指针
//存放一级指针就需要一个二级指针变量
pa++;
//+1跳过一个char*
printf("%s\n", *pa);
//这时pa指向"at"的起始地址,打印at
return 0;
}
1.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;
}
画图解析:
总结
这些题目的本质就是探究数据在内存中的存储以及数据在内存中的地址间的各种操作运算,深入理解代码的前提就是对内存的布局非常清楚,看代码就知道对应的内存布局。