目录
(注意:这里的代码均为32位机器上实现)
❤️笔试题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为数组名,指向数组首元素地址;&a的值虽然也是数组首元素的地址,但是&a指向的是整个数组;
二者本质的区别在于数据类型不同:
a的类型为 int* - 即为整形指针
&a 的类型为 int(*)[5] - 即为存放整形的数组指针
所以,我们在定义ptr的时候,要将(&a+1)强制类型转化为 int* 之后再赋值给 int* 类型的ptr;
ptr为int* 类型,-1之后只向后移动一个int*类型的大小。
因此,最终答案为:2,5
❤️笔试题2:
//已知该结构体的大小为20个字节
struct Test
{
int Num;
char* pcName;
short sDate;
char cha[2];
short sBa[4];
}* p;//p是一个结构体指针变量
//X86环境下演示:
//假设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;
}
小tips:%p -- 打印地址(不能省略0)
%x -- 以十六进制输出 (省略前面的0)
📄解析:
已知p的类型为结构体指针类型,+1 之后跳过一整个结构体的大小,该结构体的大小为20个字节,转换为十六进制为0x14
所以,第一个printf打印的数据为 :00100014;
第二个printf中将p强制类型转化为整型类型,+1 之后就是加上一个整形1,
以%p打印的结果为 00100001;
第三个printf中将p强制类型转化为整形指针类型,+1 之后跳过一个整型的大小,
打印的结果为 00100004;
❤️笔试题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);
//%p - 是打印地址
//%x - 是16进制的格式打印
return 0;
}
📄解析:
ptr1 的找法与笔试题1的ptr找法一致,这里就不赘述了。
ptr[-1] 等价于 *(ptr-1) 即向后移动一个int类型大小,打印结果为4;
ptr2 将 a 强制类型转化为int类型,+1 之后就是a原本的地址+1,比如:a的地址为0x100001, 转化为int类型并且+1之后变为0x100002;
之后再转化回int*类型,赋值给ptr2,这时,ptr2指向的就是上图所示的位置,
这里还需要注意的是编译器的存储类型为小端存储还是大端存储,结果是不同的!!!
不了解大小端存储的可以看看这篇文章:
小端存储:
读取到的内存输出即为2000000
大端存储:
读取到的内存输出即为00000100
❤️笔试题4:
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
int* p;
p = a[0];
printf("%d", p[0]);
return 0;
}
📄解析:
要注意的是a数组的定义并不是准确地初始化到每一行,要是想准确地初始化到每一行需要将每一行的数用大括号括起来
int a[3][2] = { {0, 1}, {2, 3}, {4, 5} };
而代码中用的是小括号,那就是逗号表达式了, 最终数组a中的元素就只有{1, 3, 5};
a[0] 是二维数组第一行的地址,p[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;
}
📄解析:
这道题是十分有意思的,a是一个5*5的二维数组,p是一个指向包含4个int类型的数组指针,将a赋值给p,那么p就指向a数组第一行的起始位置。
如图所示,因为p的类型为int (*)[4],所以每p+1就跳过4个int类型!
小tips:
指针-指针 得到的是两地址间的元素个数
&p[4][2] - &a[4][2] 中间有4个元素,所以得到的是 -4
用%p打印:
所以得到的是 fffffffc
用%d打印:
就是 -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 就跳过一整个数组,
aa 表示数组第一行的地址, +1 就跳过一行, *(aa+1) 等价于 aa[1],
因为两者均为数组指针,所以要强制类型转化为 int* ,分别赋值给 ptr1 、ptr2;
ptr1、ptr2均为int*类型,-1 之后向前移动一个int类型的大小,
所以,答案为 10,5
❤️笔试题7:
#include <stdio.h>
int main()
{
char* a[] = { "work","at","alibaba" };
char** pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
📄解析:
a数组为char*类型,存放了三个字符串的首字母的地址,char** pa = a,则pa指向的是 a数组的第一个元素的地址,即'w'的地址;pa+1 就指向数组的下一个元素,即'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;
}
📄解析:
这题与上一题相似,解法相同:
第一个printf:前置++,这时cpp指向cp[1],再两次解引用,得到的就是"POINT";
第二个printf: 这里要考虑优先级,再一次前置++,这时cpp指向cp[2],解引用后得到c[1]的地 址,再前置--,得到c[0]的地址,解引用后得到'E'的地址,再+3,向后移动三位找 到第二个'E'的地址,打印字符串,结果为 "ER";
第三个printf:*cpp[-2] 等价于 **(cpp-2),cpp-2之后指向cp[0],但是这里并没有改变cpp的值! 两次解引用后得到'F'的地址,再+3,向后移动三位找到'S'的地址,打印字符串,结 果为 "ST";
第四个printf:cpp[-1][-1] 等价于 *(*(cpp-1)-1),cpp-1 之后指向cp[1],这里也没有改变cpp的值
解引用后得到c[2]的地址,再-1 之后得到c[1]的地址,解引用后得到'N'的地址, 再+1,得到'E'的地址, 打印字符串,结果为"EW"
最后的话:
看完这些笔试题,我相信你一定会对这部分的知识理解地更加深刻的,后续还会发布大量优质文章,希望大家多多支持,我们一起努力!!!