• C语言数组和指针笔试题(五)(一定要看)


    感谢各位大佬对我的支持,如果我的文章对你有用,欢迎点击以下链接
    🐒🐒🐒个人主页
    🥸🥸🥸C语言
    🐿️🐿️🐿️C语言例题
    🐣🐓🏀python

    指针运算笔试题解析

    题目1

    #include 
    	int main()
    	{
    		int a[5] = { 1, 2, 3, 4, 5 };
    		int* ptr = (int*)(&a + 1);
    		printf("%d,%d", *(a + 1), *(ptr - 1));
    		return 0;
    	}
    	//程序输出的结果是什么
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    解析

    a[5]是一个整形类型的数组,&a是取的整个数组的地址,因此&a+1就是跳过整个数组,(int*)是强制类型转换,是将&a+1强制转换成int类型的指针(&a+1也可以写成int(*)[5])

    *(a+1)中的a是数组首元素的地址,a+1=&a[1],解引用结果就是a[1]=2

    ptr-1这里就要好好说一下了,我们用一个图来表示
    在这里插入图片描述
    如图因为&a+1是跳过整个数组,因此ptr的地址在a[4]之后,与a[4]相差4个字节,而ptr-1是跳过1个元素,也就是往前跳过4个字节,因此ptr-1的地址其实就是&a[4],解引用之后就是a[4]=5

    结果

    在这里插入图片描述

    题目2

    //在X86环境下,假设结构体的大小是20个字节,程序输出的结构是什么
    struct Test
    {
    	int Num;
    	char* pcName;
    	short sDate;
    	char cha[2];
    	short sBa[4];
    }*p = (struct Test*)0x100000;
    int main()
    {
    	printf("%p\n", p + 0x1);
    	printf("%p\n", (unsigned long)p + 0x1);
    	printf("%p\n", (unsigned int*)p + 0x1);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    解析

    0x开头的是16进制,因此0x1其实就是1,p是结构体指针,因为结构体指针+1是跳过一个结构体的大小,而结构体大小是20个字节,又因为是16进制,所以结果是0x100014

    ,(unsigned long)p是将p强制类型转换为无符号的long,所以p+1就是0x100001

    而printf(“%p\n”, (unsigned int*)p + 0x1),因为是将p转换为无符号整形指针,所以+1就是跳过4个字节,就是0x100004

    因为是16进制的地址,所以需要4个字节,结果是
    0x00100014
    0x00100001
    0x00100004

    结果

    在这里插入图片描述

    题目3

    #include 
    	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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    解析

    int* ptr1 = (int*)(&a + 1)和第一题是一样的所以就不解释了

    int* ptr2 = (int*)((int)a + 1)中(int)a是将a强制转换为整形类型,因为整形类型的运算和数学运算是差不多的,因此这里的+1其实就数学上的+1,举个例子假如a的地址是0x0012ff40,通过转换这个地址对应的数字是1244992,所以(int)a+1其实就是1244992+1=1244993,然后将这个结果强制类型转换为整形指针,所以转换之后的地址就是0x0012ff41

    我们知道内存存储是分大小端的,这里VS是用小端存储
    在这里插入图片描述
    当用%x打印是02 00 00 00中2前面的0不会打印,所以结果是2000000

    而*ptr打印就是4

    结果

    在这里插入图片描述

    题目4

    #include 
    	int main()
    	{
    		int a[3][2] = { (0, 1), (2, 3), (4, 5) };
    		int* p;
    		p = a[0];
    		printf("%d", p[0]);
    		return 0;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    解析

    这是一个二维数组,但是要注意数组的元素,我们可以看到数组中用括号括起来了两个数组,这里需要用到逗号表达式, (,)其实只有最后一位有效,(0,1)就是1,因此二维数组我们可以这样写,a[3][2]={1,3,5};因为是三行二列,而{ }中只有三个元素,剩下的既全为0,a[0]在上一篇博客已经讲了,是一维数组{1,3}的数组名,因为p=a[0],所以p[0]=a[0][0],最后结果就应该是1

    结果

    在这里插入图片描述

    题目5

    假设环境是x86环境,程序输出的结果是什么
    #include 
    	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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    解析

    (p)[4]是一个数组指针,因为p=a中a是数组名表示数组首元素的地址,所以a其实就是int( * )[5],而p是int()[4]

    在这里插入图片描述
    因为p中只有4个元素,所以p+1就是跳过4个整形元素,因此绿色是p+4所包含的元素,红色就是a[4][2],对于&p[4][2] - &a[4][2]就是-4,用%p来打印的话就需要将-4的补码写出来

    -4的原码:10000000 00000000 00000000 00000100
    反码: 111111111 111111111 111111111 111111011
    补码: 111111111 111111111 111111111 111111100
    因为是打印的%p是地址,所以将补码看成16进制来打印,结果是FFFFFFFC
    而用%d打印的话其实就是-4

    结果

    在这里插入图片描述

    题目6

    #include 
    	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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    解析

    &aa+1中&aa是整个二维数组的地址,因此&aa+1是跳过整个二维数组,(int*)将&aa+1强制转换为整形类型的指针

    (aa+1)中aa是第一行的地址,(aa+1)就是aa[1],aa[1]就行相当于第二行的数组名,aa[1]=&aa[1][0],这里的强制类型转换其实是多余的,因为第二行第一个元素本来就是整形,取他的地址就是整形类型的指针

    所以*(ptr1-1)就是10,*(ptr2-1)就是5

    结果

    在这里插入图片描述

    题目7

    #include 
    	int main()
    	{
    		char* a[] = { "work","at","alibaba" };
    		char** pa = a;
    		pa++;
    		printf("%s\n", *pa);
    		return 0;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    解析

    这是一道阿里巴巴的笔试题
    chara[]是一个数组,数组里面的类型都是char,数组括号里面都是三个字符串,但是这三个字符串是放不下,因为这个数组里面的都指针char*,字符串是放不进去的,所以放进去的是他们的地址,比如work存放在a中的地址是w的地址,at存放的则是a地址…

    而pa存放的是a的首元素地址,pa++后就是a的第二个元素如图
    在这里插入图片描述

    结果

    在这里插入图片描述

    题目8

    #include 
    	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;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    解析

    在这里插入图片描述
    上面是没有进行自增自减操作时每个数组元素对应的地址
    char*c中存储的分别是
    ENTER中E的地址
    NEW中N的地址
    POINT中P的地址
    FIRST中F的地址
    而cp中c+3就是第四个元素的地址,c+2就是第三个元素的地址…
    cpp是将cp的首元素地址传给cpp,所以cpp就是c+3的地址
    在这里插入图片描述
    上图是经过自增自减操做后的结果,
    printf(“%s\n”, **++cpp);对应的颜色是红
    printf(“%s\n”, *-- * ++cpp + 3);对应的颜色是绿
    printf(“%s\n”, *cpp[-2] + 3);对应的颜色是黑
    printf(“%s\n”, cpp[-1][-1] + 1);对应的颜色是粉
    我们一个一个的解析
    第一次打印: **++cpp先将cpp进行自增,所以cpp中的cp从cp中第一个元素c+3变到第二个元素c+2 然后两次解引用之后就是POINT中P的地址,打印字符串就为POINT
    第二次打印:由于++cpp将cpp中cp指向的地址永久变化了,所以第二次的++cpp就是从c+2变到了c+1,解引用再–,c+1就变成了c,所以c+1指向的地址就由1变成0,之后再解引用就是ENTER中E的地址,再+3后,就是箭头值的第二个E的地址,打印字符串就是ER
    第三次打印:*cpp[-2]+3我们可以写成 * *(cpp-2)+3,由于上一次打印使cpp指向的c+2变成了c+1,所以这里的cpp-2就使cpp指向的第三个元素c+1变为第一个元素c+3(注意这里不是自增自减),再经过两次解引用后,就是FIRST中F的地址,+3就变成了箭头指向的S的地址,所以打印结果为ST
    第四次打印cpp[-1][-1] + 1可以写成 *( *(cpp-1)-1)+1,cpp-1就是从第三个元素c+1变为第二个元素c+2,解引用再-1就是从原来指向的下标2变成指向的下标1,解引用再+1就是NEW中E的地址,打印的结果就是EW

    结果

  • 相关阅读:
    连搜索、地图、网盘都要重构了?百度这次玩得有点大
    Docker启动Java项目报异常:FontConfiguration.getVersion
    C# CAD备忘录
    ElementPlus· banner轮播图实现
    C++中的map
    视觉与机器人的九点标定(三)
    一行行的代码解密马尔可夫链
    VMware——虚拟机的创建(Linux)
    使用 xml资源文件定义菜单
    PowerShell:在代理服务器后面使用 PowerShell
  • 原文地址:https://blog.csdn.net/2301_79178723/article/details/133499734