✨博客主页:小钱编程成长记
🎈博客专栏:进阶C语言
🎈相关博文:进阶C语言(一)、进阶C语言(二)、进阶C语言(三)、进阶指针(四)
进阶指针(五)—— 指针笔试题
- 笔试题1:
- 笔试题2:
- 笔试题3:
- 笔试题4:
- 笔试题5:
- 笔试题6:
- 笔试题7:
- 笔试题8:
- 总结
笔试题1:
#include <stdio.h>
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是一个数组的地址,类型是int( * )[5],+1时跳过一个数组的大小。int* ptr = (int*)(&a + 1);
:然后int( * )[5]类型的地址又被强转成了int*类型的地址,赋给ptr。*(a+1)
是首元素地址+1,然后再解引用。*(ptr-1)
是ptr-1,然后再解引用。
- 所以结果是2,5
笔试题2:
//由于还没学习结构体,这里告知结构体的大小是20个字节(在x86(32位)环境下)
#include <stdio.h>
struct Test
{
int Num;
char* pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
//假设p的值为0x100000(0x开头的数字是16进制的数字)。如下表达式的值分别为多少?
//已知,结构体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;
}
-
p+0x1
:p是结构体类型的指针,p+0x1跳过一个结构体的大小,结构体Test的大小是20字节,又因为一个地址对应内存中的一字节空间, 地址是以十六进制的形式打印的,所以p+0x1为0x100000+20 == 0x100000+0x16(十进制的20==十六进制的14)。 -
(unsigned long)p+0x1
:结构体类型的指针p被强转为unsigned long类型的值,由地址变成了常数,+0x1和+1效果一样。 -
(unsigned int*)p + 0x1
:结构体类型的指针p被强转为unsigned int*类型的指针,+0x1跳一个过unsigned int类型的大小。 -
%p
:是以地址的 形式(地址的长度和进制类型) 打印。
%x
:以十六进制的形式打印
%#x, "0x%x"
:以十六进制的形式打印,并显示出十六进制的数据标志。
-
结果为:0x100014,0x100001,0x100004
笔试题3:
#include <stdio.h>
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;
}
-
(int*)(&a+1)
:一个数组的地址,+1跳过一个数组的大小。最后再将int( * )[4]类型的地址强转为int*类型的地址。 -
(int*)((int)a+1)
:首元素地址a被强转为int类型的数据,然后再+1。最后再将int类型的(int)a+1强转为int*类型的地址。 -
ptr1[-1] == *(ptr1-1)。
4. 结果为:4,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;
}
逗号表达式的结果是最后一个表达式的结果。
2. 结果为:1
笔试题5:
#include <stdio.h>
int main()
{
int a[5][5];
int(*p)[4];//数组指针
p = a;//类型不同,不太合适,但非要这样写也行。a-->int(*)[5], p-->int(*)[4]
printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
return 0;
}
p = a
:类型不同,不太合适,但非要这样写也行。a–>int( * )[5], p–>int( * )[4]
2. 指针相减的差值的绝对值是指针间元素的个数。
2. %p
是打印地址,认为内存中存储的-4的补码就是地址。
3. %d
是以十进制的形式打印。
笔试题6:
#include <stdio.h>
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\n", *(ptr1 - 1), *(ptr2 - 1));
return 0;
}
-
(int*)(&aa+1)
:aa是二维数组的数组名,&aa+1是跳过一个二维数组的大小。最后int ( * )[2][5]类型的(&aa+1)强转成(int * )类型。 -
(int*)(*(aa+1))
:
第一种理解方式:aa+1是二维数组的首元素地址+1,也就是第一行数组的地址+1,跳过第一行数组的大小。因为aa的类型是int ( * )[5],是一个数组的地址,所以+1后,得到的aa+1的类型也是int ( * )[5],是一个数组的地址, 解引用时访问的空间大小是有5个int类型元素的数组的大小,得到第二行数组,也就是二维数组的第二个元素aa[1],但aa[1]又是第二行的数组名,除了两个例外,aa[1]表示第二行数组的首元素地址。最后被强转为int * 类型的地址。
第二种理解方式:因为 (aa+1) == aa[1] ,并且没有sizeof(),也没有&,所以aa[1]就是第二行数组的首元素地址==&aa[1][0]。最后再强转为int类型,因为第二行数组的首元素地址也是int类型的,所以强转前后类型并没有发生变化。 -
*(ptr1-1), *(ptr2-1)
:因为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;
}
-
char* a[] = {"work","at","alibaba"};
:字符串作为表达式时结果是首字符地址,所以数组中存放的都是字符串的首字符地址。 -
char* *pa = a;
:a是指针数组的首元素地址,指针数组的首元素是work的首字符地址,所以a是work的首字符地址的地址。用二级指针pa接收。 -
pa++
:因为pa存放的是char * 类型的数据的地址,所以 pa++ 时跳过一个char * 类型的大小。
-
*pa
:*pa指向at,是at的首字符地址。 -
%s
:打印字符串,从这个地址开始解引用打印,遇到\0停止(\0不打印)。 -
结果是at
笔试题8:
#include <stdio.h>
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"};
:数组c中放的是四个字符串的首字符地址。指针指向字符串,本质上指向的是字符串的首字符。 -
c+3
:c是指针数组的首元素地址,也就是ENTER的首字符地址的地址,存放在二级指针中。所以+3是跳过3个ENTER的首字符地址的大小。 -
char***cpp = cp;
:cp是二级指针数组cp的数组名,在这里表示首元素地址,表示c+3的地址。因为c+3的类型是char**,所以要用c+3的地址要存放在三级指针中。
-
cpp前置++或–后,cpp本身也会发生改变。
-
**++cpp
:前置++,先++,后使用,跳过1个char * * 类型的大小,然后两次解引用得到char*类型的指针,指向POINT。
-
*--*++cpp+3
:cpp先前置++,再 * 解引用,得到char * * 类型的地址c+1。然后前置 --,再 * 解引用,得到char * 类型的地址,最后再跳过3个char类型的大小,指向ENTER中的E。
-
*cpp[-2]+3
:cpp[-2] == *(cpp - 2),cpp先往后退2个char * * 类型的大小,然后 * 解引用,得到char * * 类型的c+3,再解引用,得到char * 类型的地址。最后跳过3个char类型的大小,指向FIRST中的S。
-
cpp[-1][-1]+1
:cpp[-1][-1] == * ( * (cpp - 1) - 1),cpp先往后退1个char * * 类型的大小,再 * 解引用,得到char * * 类型的c+2。然后后退1个char * 类型的大小,在解引用,得到char*类型的地址。最后跳过1个char类型的大小,指向NEW中的E。
-
%s
:打印字符串,从这个地址开始解引用打印,遇到\0停止(\0不打印)。
总结
这篇文章我们通过做一些指针笔试题更加深入的理解了指针。
大家有什么问题可以在评论区多多交流,如果文章有错误的地方,欢迎在评论区指正。感谢大家的阅读!大家一起进步!
点赞收藏加关注,C语言学习不迷路!