当前位置: 首页 > news >正文

【C语言】指针笔试题2

C语言学习

指针基础
友情链接:C语言专栏


文章目录

  • C语言学习
  • 前言:
  • 指针笔试题
    • 笔试题1
    • 笔试题2
    • 笔试题3
    • 笔试题4
    • 笔试题5
    • 笔试题6
    • 笔试题7
    • 笔试题8
  • 总结:
  • 附录
    • 上文链接
    • 专栏


前言:

继上文【C语言】指针笔试题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);   //&a为数组指针,&a + 1则跳过整个数组,//即此时指向数组最后一个元素(5)的后面//(int*)(&a + 1)又将它强制类型转化为int*的指针printf("%d,%d", *(a + 1), *(ptr - 1));//a数组名首元素地址,a + 1则为的第二个元素地址,// *(a + 1)解引用则为2;//ptr为int*的指针,指向数组最后一个元素(5)的后面,//ptr - 1则指针向后退4个字节(因为指针为int*),指向数组最后一个元素,//*解引用则为5;return 0;
}

笔试题2

代码:

//这里直接告诉大家结构体Test类型的变量大小是20个字节
struct Test
{int Num;char* pcName;short sDate;char cha[2];short sBa[4];
}*p;
//假设p 的值为0x0。 如下表表达式的值分别为多少?
int main()
{printf("%p\n", p + 0x1);printf("%p\n", (unsigned long)p + 0x1);printf("%p\n", (unsigned int*)p + 0x1);return 0;
}

输出(都为16进制):
在这里插入图片描述
解释:

int main()
{printf("%p\n", p + 0x1);//p + 0x1,跳过一个结构体大小,故为0x14(16进制);printf("%p\n", (unsigned long)p + 0x1);//(unsigned long)p,将p强制类型转化为无符号长整形,//(unsigned long)p + 0x1则为0x1;printf("%p\n", (unsigned int*)p + 0x1);//(unsigned int*)p将结构体指针强制类型转换为无符号整型指针,//且unsigned int为4个字节,整数运算://所以(unsigned int*)p + 0x1则跳过四个字节,故为0x4return 0;
}

笔试题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;
}

输出:
在这里插入图片描述

解释:

int main()
{int a[4] = { 1, 2, 3, 4 };int* ptr1 = (int*)(&a + 1);    //&a数组地址,//&a + 1数组指针跳过整个数组,即指向数组最后一个元素的后面//(int*)(&a + 1)强制类型转换,将数组指针转化为(int*)类型的指针;//即ptr1为指向数组最后一个元素后面int*指针int* ptr2 = (int*)((int)a + 1);//下面画图解释printf("%x\n", ptr1[-1]);printf("%x\n", *ptr2);  //下面画图解释//ptr1[-1]可以转换为*(ptr-1),//ptr - 1,ptr为int*指针,-1则为向后退4个字节,指向数组最后一个元素;//故输出为4;return 0;
}

咱们主要来分析一下这两行代码(干货哦!!!):

	int* ptr2 = (int*)((int)a + 1);printf("%x\n", *ptr2); 

在此之前,咱们要对大小端字节序了解,可以看我关于【数据的存储】(由于数据的存储是在此篇博客后面的,所以未附上链接)的介绍。
假设咱们是小端字节序,则数组中元素存储是这样的:
在这里插入图片描述
了解了这个,来分析一下代码:
(int)a:a是数组首元素地址,(int)a是将a强制类型转化为int型的数据;
((int)a + 1):假设a是0x1000,(int)a + 1此时为整数运算则为0x1001,
(int*)((int)a + 1):又将(int)a + 1强制类型转化为int型的指针,那它此时指向哪里呢,看图
a指针指向示意图:
在这里插入图片描述
(int
)((int)a + 1)指针指向示意图:
在这里插入图片描述
那对于*ptr2怎么理解呢?

因为ptr2是int型的指针,所以解引用访问四个字节,有因为咱们是小端字节序,所以它是从从ptr2指针指向的位置起(0x1001)向后访问4个字节(int)内容,即为*ptr2

图示:
在这里插入图片描述
16进制则为0x02000000;
当然,咱们也可以试试大端字节序的情况下是什么样的。

笔试题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;
}

输出:
在这里插入图片描述
解释:

#include <stdio.h>
int main()
{int a[3][2] = { (0, 1), (2, 3), (4, 5) };//重点在这://(0, 1)逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。//即数组可写为int a[3][2] = { 1, 3, 5 };后面默认为0;int* p;p = a[0];//a[0]为第一行的数组名//数组名为首元素地址//即p=&a[0][0]printf("%d", p[0]);//p[0]可以这样看*(p+0);//故为1;return 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;
}

输出:
在这里插入图片描述

解释:

int main()
{int a[5][5];int(*p)[4];//一维数组指针p = a;//首先,咱们先对p与a的类型进行分析:// p是int(*)[4];a是int(*p)[5];这个是要明白的,然后分析代码;// 对于a与p他俩的区别是什么呢://a + 1 会跳过 5 * sizeof(int) 字节(通常 20 字节)//p + 1 会跳过 4 * sizeof(int) 字节(通常 16 字节)printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);return 0;
}

首先,先来看数组a的示意图:在这里插入图片描述
咱们先来看a[4][2]所在的位置,很简单:
在这里插入图片描述
那来看看p[4][2]
在这里插入图片描述
将两者放一起:
在这里插入图片描述
那么对于&p[4][2] - &a[4][2],即指针相减,得到的是两个指针之间的元素,为-4;
对于%d形式的输出,就为-4;
但对于%p形式的输出呢?
解释:

  • %p 用于打印指针(内存地址),而内存地址本质上是无符号整数。
  • -4 的二进制补码:0xFFFFFFFC(即 11111111 11111111 11111111 11111100)。
  • 当 -4 被当作指针(无符号数)时,它会被解释为 0xFFFFFFFC。也可以说,%p不管内存中存储的是什么,它只会原样输出存储的二进制(因为无符号数“补码”的概念)。

笔试题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);//&aa取出整个数组的地址//&aa+1跳过整个数组,指向数组最后一个元素的后面//(int*)(&aa + 1)将数组指针强制类型转化为int*类型的//即ptr1是一个int*的指针,且指向数组最后一个元素的后面int* ptr2 = (int*)(*(aa + 1));//aa二维数组的数组名是第一行的地址,类型为int(*)[5]//aa + 1第二行地址,类型为int(*)[5]//*(aa + 1),可以这样写aa[1]//即数组第二行元素,为一维数组//一维数组数组名是首元素地址//首元素地址类型为int*//(int*)(),多余//即ptr2指向数组第二行第一个元素printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));//*(ptr1 - 1)数组最后一个元素10;//*(ptr2 - 1)数组第一行最后一个元素5return 0;
}

笔试题7

看代码:

#include <stdio.h>
int main()
{char* a[] = { "work","at","alibaba" };char** pa = a;pa++;printf("%s\n", *pa);return 0;
}

输出:
在这里插入图片描述

解释:

#include <stdio.h>
int main()
{char* a[] = { "work","at","alibaba" };//对字符指针数组进行解释://它是将三个字符串的首元素地址存储在数组中char** pa = a;//a 是数组名,在大多数表达式中会退化为指向首元素的指针,即 pa 为 &a[0](类型是 char**)。//pa 存储的是 a[0] 的地址(即 &a[0])pa++;//pa 原本指向 a[0],pa++ 后指向 a[1](即 &a[1])printf("%s\n", *pa);//*pa 得到 a[1],即 'at' 的首地址,printf 打印这个字符串";return 0;
}

笔试题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;
}

输出:
在这里插入图片描述

解释:

int main()
{char* c[] = { "ENTER","NEW","POINT","FIRST" };//数组 c 的每个元素(c[0]、c[1] 等)存储的是指向对应字符串常量的指针://c[0] → 指向 "ENTER" 的首地址(即字符 'E' 的地址)。//c[1] → 指向 "NEW" 的首地址。//c[2] → 指向 "POINT" 的首地址。//c[3] → 指向 "FIRST" 的首地址。char** cp[] = { c + 3,c + 2,c + 1,c };//c数组名为首元素地址,即&c[0];//c + 3 → 指向c[3]     cp[0]//c + 2 → 指向c[2]     cp[1]//c + 1 → 指向c[1]     cp[2]//c  → 指向c[0]        cp[3]char*** cpp = cp;//cp数组名为首元素地址,即&cp[0];//cpp → 指向cp[0]//搞清楚上面这部分,就很简单了//注意:++cpp会产生副作用,会使得cpp改变printf("%s\n", **++cpp);//++cpp → 指向cp[1];//*++cpp 为cp[1]是c + 2 → 指向c[2];//**++cpp 为c[2];//则输出为"POINT"printf("%s\n", *-- * ++cpp + 3);//注意操作符优先级//++cpp → 指向cp[2];//* ++cpp为cp[2] → 指向c[1]//-- * ++cpp  → 指向c[0]//*-- * ++cpp 为c[0] → 指向 "ENTER" 的首地址//*-- * ++cpp + 3 → 指向 "ENTER" 的'E'的地址//则输出为"ER"printf("%s\n", *cpp[-2] + 3);//由于此时cpp → 指向cp[2]//cpp[-2]等效为*(cpp-2)//cpp-2 → 指向cp[0]//*(cpp-2)即为cp[0];//*(cp[0])即为c[3] → 指向 "FIRST" 的首地址。//+ 3 → 指向 "FIRST" 的'S'的地址//则输出为"ST"printf("%s\n", cpp[-1][-1] + 1);//cpp[-1][-1]+1等效为*(*(cpp-1)-1)+1;//先看cpp-1 → 指向cp[1];//*(cpp-1)则为cp[1] → 指向c[2];//*(cpp-1)-1 → 指向c[1];//*(*(cpp-1)-1)则为c[1] → 指向 "NEW" 的首地址。//+1 → 指向 "NEW" 的'E'的地址;//则输出为"EW"return 0;
}

总结:

指针到这就结束了,但我们的学习尚未结束,希望大家对指针都有各自的理解。
谢谢学习至此!!!

请添加图片描述

附录

上文链接

【C语言】指针基础:为什么说指针是C语言的灵魂?
【C语言】指针进阶1:数组与指针
【C语言】指针进阶2:数组、指针、函数

【C语言】指针笔试题1

专栏

C语言专栏

http://www.dtcms.com/a/272950.html

相关文章:

  • 模块三:现代C++工程实践(4篇)第二篇《性能调优:Profile驱动优化与汇编级分析》
  • FlashAttention 快速安装指南(避免长时间编译)
  • QT网络通信底层实现详解:UDP/TCP实战指南
  • Centos 7下使用C++使用Rdkafka库实现生产者消费者
  • 【LeetCode 热题 100】19. 删除链表的倒数第 N 个结点——双指针+哨兵
  • 学习 Flutter (一)
  • html的outline: none;
  • C++STL-deque
  • 1. COLA-DDD的实战
  • 【基础架构】——软件系统复杂度的来源(低成本、安全、规模)
  • 告别卡顿与慢响应!现代 Web 应用性能优化:从前端渲染到后端算法的全面提速指南
  • IDEA运行Spring项目报错:java: 警告: 源发行版 17 需要目标发行版 17,java: 无效的目标发行版: 17
  • Cargo.toml 配置详解
  • 【科研绘图系列】R语言探索生物多样性与地理分布的可视化之旅
  • 网安-解决pikachu-rce乱码问题
  • 访问Windows服务器备份SQL SERVER数据库
  • (C++)任务管理系统(文件存储)(正式版)(迭代器)(list列表基础教程)(STL基础知识)
  • x86交叉编译ros 工程给jetson nano运行
  • Rust and the Linux Kernel
  • Sophix、Tinker 和 Robust 三大主流 Android 热修复框架的详细对比
  • windows10 安装docker到H盘
  • Linux 服务器挖矿病毒深度处理与防护指南
  • 使用Docker将Python项目部署到云端的完整指南
  • Web 会话认证方案详解:原理、流程与安全实践
  • Variables
  • 分库分表之实战-sharding-JDBC分库分表执行流程原理剖析
  • ubantu问题手册
  • 大数据学习5:网站访问日志分析
  • 力扣hot100速通(7.9)|49.字母异位词分组 128.最长连续序列 283.移动零 11.盛最多水的容器 42.接雨水
  • 观成科技:基于自监督学习技术的恶意加密流量检测方案