上篇文章我们介绍了一系列字符数组和指针在sizeof和strlen函数中的不同应用,下面我们再来通过几道笔试题加深指针在数组中的应用吧。
笔试题1:
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int* ptr = (int*)(&a + 1);//ptr指向的地址在a[4]的后面
printf("%d,%d", *(a + 1), *(ptr - 1));
//a+1 这里的a表示数组名,也就是数组的地址,也就是数组首元素的地址,再+1,指向a[1]
//*(a+1) 值为2
//ptr - 1 指向a[4]
return 0;
}
答案:
2,5
解析:
指针ptr指向的地址是a[4]也就是数字5的后面的地址,而ptr-1,也就是ptr指向的地址向低地址移动一个int大小,这时指向了a[3]的地址
笔试题2:
struct Test
{
int Num;
char* pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?(在x86环境下编译)
//已知,结构体Test类型的变量大小是20个字节
//0x开头的数字是16进制的数字
//
int main()
{
p = (struct Test*)0x100000;
printf("%p\n", p + 0x1);//跳过20Byte 0x100000 --> 0x100014
printf("%p\n", (unsigned long)p + 0x1);//整型+1就是+1 0x100000 --> 0x100001
printf("%p\n", (unsigned int*)p + 0x1);//跳过4Byte 0x100000 --> 0x100004
return 0;
}
答案:
解析:
1.p指针为结构体指针,而结构体大小是20字节,那么p加1,也就是跳过20个字节,得到的地址用十六进制表示就是0x100014
2.而将p强制转换类型为unsigned long也就是无符号长整型,整型加1得到的地址就是0x100001
3.将p强制转换类型为unsigned int*,整形指针加1,加了4字节,得到的地址就是0x100004
笔试题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.&a+1取到的是整个数组的地址,再向后移动一个数组的大小,因此ptr指向的是a[3]后面的地址,因此ptr[-1]指向的就是a[3]的地址
2.a本来是代表整个数组的地址也就是数组首元素的地址,而将它强制转换成int类型,再加+1,也就是地址加了1,假设a的地址是0x0012ff40,那么(int)a+1的地址就是0x0012ff41
因此ptr2指向的就是a的地址往后移动1字节,而为什么会得到这么大的十六进制数,则是由于VS编译器采用的是小端存储方式,不了解的可以去查一下大小端存储
笔试题4:
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
int* p;
p = a[0];
printf("%d", p[0]);
return 0;
}
答案:
1
解析:
二维数组的初始化里面为逗号表达式,是从左到右依次计算,但是只取最后一个表达式的值,因此二维数组的值如下,而p[0]就等于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;
}
答案:
解析:
我们认为的二维数组是按照上图所示排列的,实际上在内存中是按照下图排列的。
p是一个一维数组指针,指向的一维数组应该只有四个元素,而这里让p指向二维数组a,在后续如果用p+1,则跳过4个int变量的大小,因此p[4][2]指向的地址如下图所示
而地址-地址,得到的就是数组的元素个数,也就是4,在这里是低地址-高地址,所以是-4
而-4在内存中是以补码的形式存在的,先转化为二进制为
10000000000000000000000000000100 //原码
11111111111111111111111111111100 //补码
转换为十六进制为FFFFFFFC
笔试题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;
}
答案:
解析:
1.&aa+1取出二维数组的地址,然后往后走aa数组的大小的地址,指向的就是10的后面,再用强制类型转换为int*,最终指针ptr1-1是ptr往前走一个int大小,此时指向的就是aa[1][3]的地址
2.*(aa+1)指向的是第二个一维数组的地址,ptr2指向就是a[1]的地址
笔试题7:
int main()
{
char* a[] = { "work","at","alibaba" };
char** pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
答案:
解析:
char* a[]是指针数组,存放三个指针,分别指向一个字符串
pa是二级指针,指向指针数组的指针,也称数组指针
而pa++,也就是pa从指向数组a[]的首元素地址,跳转到第二个元素的地址,而第二个元素指向的字符串为"at",对二级指针解应用也就是a[1]地址,用%s输出字符串,打印出来的就是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;
}
答案:
解析:
数组c是指针数组,有4个元素,分别指向一个字符串 - 一级指针
数组cp是指针数组,有4个元素,分别指向c数组元素的一个地址 - 二级指针
cpp是指针,指向数组cp的地址 - 三级指针
- **++cpp
++cpp指向的是cp[1]的地址,在对其二次解应用,也就是POINT\0的首地址
- *-- * ++cpp + 3
--*++cpp指向的是c[1]的地址,解应用后也就是字符串的首地址,再+3指向的就是‘W’的地址
- *cpp[-2] + 3
- cpp[-1][-1] + 1