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

C语言经典指针运算笔试题图文解析

指针运算常常出现在面试题中,画图解决是最好的办法。

题目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得到的是整个数组的地址,其和首元素地址相同,但类型为 int (*) [5] ,对其 +1 ,跳过 5 * 4 = 20字节

然后对其强制类型转换为int*,此时其 -1 ,跳过 4 字节

故对其解引用得到数组中下标为 4 的元素,值为5。

数组名代表首元素地址,其 +1 就得到数组下标为 1 的元素的地址,对其解引用就得到对应元素,值为2。

题目2:

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", (long)p + 0x1);printf("%p\n", (int*)p + 0x1);return 0;
}

        p是类型为结构体类型的指针,那么其每 +-1 跳过一个结构体变量大小的空间,而此结构体变量大小为20个字节,那么p + 0x1 = 0x100014,以地址的形式打出也是0x100014,因为地址就是以16进制表示的

        当对其强制类型转换为long类型后,p不再代表地址,而是代表整数0x100000,对其 +0x1,就是简单的整数运算,得0x100001。

         当对其强制类型转换为int*类型后,p变为指向整形类型的指针变量,对其 +1,跳过4字节,故值为0x100004。

题目3:

#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;
}

一上来看到这样的数组形式,就会想当然的认为这个二维数组为:

这样的二维数组其书写格式应该为:a[3][2] = {{0,1},{2,3},{4,5}}

而原书写对行的划分使用的是小扩号,应理解为逗号表达式,其结果为a[3][2] = {1,3,5}。由于给出的元素没有达到二维数组元素总个数,剩下拿0来补齐,那么实为:

a[0] 为第一行首元素地址,p[0] == *(p + 0),那么这里最后访问到的应该为第一行第一列的元素,即1。

题目4:

//假设环境是x86环境,程序输出的结果是啥?
#include <stdio.h>
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为一个五行五列存放元素为int类型的二维数组,p为int(*)[4]类型的数组指针,意味着其 +1 一次跳过4个int类型变量空间大小。

假设&p[4][2] = 0x00000001,那么&a[4][2] = 0x00000011,&p[4][2] - &a[4][2] = 0xFFFFFFFC,此为-4的16进制转换

我们知道,两指针相减的绝对值可以得到两指针间元素的个数,那么这里的元素个数就为4,由于是低地址位指针减高地址位指针,所以结果为负值

题目5:

#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", *(ptr1 - 1), *(ptr2 - 1));return 0;
}

&aa得到的是整个二维数组的地址,类型为int(*)[2][5],意味着 &aa + 1 一次跳过整个二维数组的内存空间大小。aa得到的是二维数组首元素的地址,即为第一行元素组成的数组的地址,类型为int(*)[5], 意味着对其 +1 直接指向第二行元素组成的数组,由于数组地址与数组首元素地址相同,此时aa + 1指向第二行元素组成的数组的首元素。

ptr1 此时指向的就为图示&aa + 1的位置,但由于其类型为int*,意味着其 -1仅跳过4字节,ptr1-1解引用得10;*(aa + 1)为int*类型,仍然是一个指针,只不过不再指向一个数组,而是一个整形变量,故ptr2指向了图示位置,解引用得5。

题目6:

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

a为数组名,代表数组首元素地址,而a数组为元素是字符串的数组,故每个元素实则为字符串首字符的地址,正因为是地址的地址,所以用二级指针pa来存。

对pa++解引用拿到"at"字符串的首字符的地址,故:

题目7:

#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;
}

同题六作图:

注:此题作图一定注意++ 、+的区别,一个会改变自身,一个不会。

**++cpp,首先对cpp + 1解引用得到c数组中第3个元素的地址,再解引用访问c数组中第3个元素,此元素为POINT字符串首字符的地址。

*--*++cpp+3,其先对cpp + 1,然后解引用得到cp第3个元素,即c第2个元素地址,然后对其--得到c第1个元素的地址,再解引用得到ENTER字符串的首字符地址,再+3就得到第4个字符的地址,从此字符开始打印,结果为ER。(ps.      ((*(--*(++cpp)))+3)   )

*cpp[-2]+3,cpp[-2]得到cp数组第一个元素,即c数组最后一个元素地址,再解引用得到FIRST字符串首字符地址,+3就得到S字符的地址,从此地址打印,得到ST。

cpp[-1][-1]+1,cpp[-1]得到cp的第2个元素,即c数组第3个元素的地址,再[-1]即对此地址-1解引用,得到字符串NEW的首字符地址,再+1得到E字符的地址,从此字符开始打印,结果为EW。

相关文章:

  • java-SpringBoot执行定时任务-任务调度-@EnableScheduling和@Scheduled
  • JDBC简介
  • css记录:三维变化之transform-style
  • python数据分析-连云港石化基地2023年用电量分析
  • JAVA反射代码
  • go匿名函数
  • ES8新特性:async函数
  • [python学习]--模块管理
  • 面向龙芯LoongArch平台的AMD GPU补丁解决了一个“巨大平台错误“
  • 厂里资讯之异步通知文章上下架
  • state和store的使用场景
  • 公有云_私有云_云平台_服务器安全组_按量付费_vpc私有网络/专有网络原理---分布式云原生部署架构搭建005
  • wifi MLME
  • C++ 65 之 模版的局限性
  • 力扣1206--跳表
  • 002-读书笔记-JavaScript高级程序设计 在HTML中使用JavaScript
  • 4. 路由到控制器 - Laravel从零开始教程
  • AWS实战 - 利用IAM对S3做访问控制
  • Brief introduction of how to 'Call, Apply and Bind'
  • Linux编程学习笔记 | Linux多线程学习[2] - 线程的同步
  • node学习系列之简单文件上传
  • php中curl和soap方式请求服务超时问题
  • SAP云平台运行环境Cloud Foundry和Neo的区别
  • vuex 学习笔记 01
  • vue自定义指令实现v-tap插件
  • Web设计流程优化:网页效果图设计新思路
  • 好的网址,关于.net 4.0 ,vs 2010
  • 每个JavaScript开发人员应阅读的书【1】 - JavaScript: The Good Parts
  • 前端技术周刊 2019-01-14:客户端存储
  • 说说动画卡顿的解决方案
  • 怎样选择前端框架
  • 智能情侣枕Pillow Talk,倾听彼此的心跳
  • ​一文看懂数据清洗:缺失值、异常值和重复值的处理
  • ‌U盘闪一下就没了?‌如何有效恢复数据
  • # AI产品经理的自我修养:既懂用户,更懂技术!
  • $LayoutParams cannot be cast to android.widget.RelativeLayout$LayoutParams
  • (2015)JS ES6 必知的十个 特性
  • (Demo分享)利用原生JavaScript-随机数-实现做一个烟花案例
  • (STM32笔记)九、RCC时钟树与时钟 第一部分
  • (附源码)springboot 基于HTML5的个人网页的网站设计与实现 毕业设计 031623
  • (简单有案例)前端实现主题切换、动态换肤的两种简单方式
  • (每日一问)基础知识:堆与栈的区别
  • (转)EOS中账户、钱包和密钥的关系
  • (转)http-server应用
  • .bat文件调用java类的main方法
  • .NET MVC、 WebAPI、 WebService【ws】、NVVM、WCF、Remoting
  • .net 程序发生了一个不可捕获的异常
  • @autowired注解作用_Spring Boot进阶教程——注解大全(建议收藏!)
  • [ 2222 ]http://e.eqxiu.com/s/wJMf15Ku
  • [ 渗透工具篇 ] 一篇文章让你掌握神奇的shuize -- 信息收集自动化工具
  • [Geek Challenge 2023] web题解
  • [Go 微服务] Kratos 验证码业务
  • [hdu 3652] B-number
  • [java基础揉碎]关系运算符(比较运算符)逻辑运算符赋值运算符三元运算符运算符的优先级
  • [JS入门到进阶] 7条关于 async await 的使用口诀,新学 async await?背10遍,以后要考!快收藏