C语言疑难杂症-指针
\\\插播一条:文章末尾有惊喜哟~///
进修阶段:高中信竞、大学编程。
前置知识:二进制与十六进制,C语言根底,数组。
指针初学可能比较难了解,我这篇文章尽量用通俗易懂的方式来讲解。
1.指针概述
为什么有指针这个东西?由于指针很贴近计算机内部的实际工作原理,与内存实际的寻址方式类似。C语言能够说是高级语言中最贴近机器的语言,而像Python、JavaScript这种更亲和于人类的语言与机器的关系则比较远了,甚至它们自身可能还是用C语言写出来的呢。
指针能够形象地比喻为在内存中定位的导航员。内存那么大,怎么知道我须要的东西存在哪里呢?能够让指针来记录与导航。我们先来了解一下内存。
2.预备知识
2.1内存
钻研过组装机、电脑配件的话,一定知道内存条这个东西。此时这个时代,一台家用电脑的内存一般是2GB、4GB、8GB、16GB、32GB、64GB等等。
把CPU比作干活的人,则内存就相当于工作台。CPU在跑的程序以及很多相关数据都存在内存内,这就相当于人在干活时要把相关资料放在工作台上使用。
内存能够看成是一个宏大的数组,我这里记为
memory[0..n]
memory的每一个单元存1B=8bit,memory[0]就是内存的第一个单元。这个中括号里面的数,即数组下标,被称为内存地址,简称为地址。我确定了一个地址,也就相当于确定了内存中的一个单元。
通常,我们说的32位机器,意思就是地址是32bit的,最大支持的内存是
memory[0x00000000..0xFFFFFFFF]
最小地址是0,最大地址是0xFFFFFFFF=2^32-1,最大支持
也就是说32位机器理论上最大支持4GB的内存,这就是它渐渐被淘汰的理由。
而64位机器理论上最大支持的内存是
这个量级远大于目前的需求,因此128位机器在相当长的时长内不会出现。
2.2变量与数据类型
C语言有很多数据类型,不同的数据类型在内存中的占用空间和存储格式也不一样。不同数据类型的存储格式比较复杂,这里不详述。我只谈谈不同的占用空间。
一个变量在内存中占用都是不间断空间,记T类型的变量在内存中占用sizeof(T)字节的空间。当我声明一个T类型的变量a时,内存会寻找不间断且可用的sizeof(T)个单元,把它们分配给变量a,假如说是memory[100..103]这4B的空间。此后我对变量a进行读写,也就相当于对memory[100..103]这4B的空间进行读写。
某些数据类型的占用空间:short短整形占用2B,int整形占用4B;float浮点形占用4B,double双浮点型占用8B;char字符型占用1B.
3.指针
指针是一种特殊的数据类型,指针类型的变量应存储的是内存地址。在32位机器上,任何一个内存地址都是32bit=4B,故任何一个指针型变量都占用4B.
此时问题来了,指针指向内存中的一个单元,我怎么知道这个单元里面存的是什么东西,是什么类型的数据?因此,在声明指针变量的时候,也要声明这个指针指向数据的类型,假如int型指针、char型指针等。
3.1指针的声明
在语法上,声明指针类型使用*符号,例如
int *a, b, *c; //a和c是指针,b不是指针char *ch;
这两句代码声明了int*型的变量a与c、int型变量b以及char*型变量ch.依据语法,我们习惯上称T型指针为T*型,含义就是T*型变量是指针,其所指的相关内存单元存的是T型数据。(注意,这里的b变量不是指针,仅仅为普通的int整形。)
3.2指针的使用
指针有很多种使用方式,包括动态申请内存、函数地址传参等等。我这里仅介绍最简略的使用方式。我提供一份例程,可直接从例程中进修指针的声明与使用。
例程:
int x=1, y=2; //声明x与y并赋初值int *p=&x; //声明p且p指向x*p=11; //p修改xp=&y; //p指向y*p=12; //p修改yprintf("x=%d, y=%d", x, y); //打印x与y
第一行,在内存中申请不间断的4B区域存入int型数据1,记为变量x;再在内存中申请不间断的4B区域存入int型数据2,记为变量y.假设x对应内存区域memory[100..103],y对应内存区域memory[200..203].第一行执行完毕,内存如图1所示:
图1第一行执行完毕
第二行,在内存中申请不间断的4B区域存入int*型地址数据100,记为变量p.这里&符号是取地址运算符,表示取变量x的首地址,在本例中就是100.这一行代码是声明指针同时赋初值,相当于以下两行代码:
int *p;p=&x;
第二行执行完毕,内存如图2所示(100的十六进制是0x64):
图2第二行执行完毕
第三行,将p所指的地址起4B空间内存入int型数据11.这里*是解地址运算符,表示取得指针所指的内存空间。第三行执行完毕,内存如图3所示:
图3第三行执行完毕
假如第三行改为执行p=(int*)11;,则是先把int型数据11强制转换为int*型数据11,然后赋值给p,如图3.2所示:
图3.2第三行改为执行p=(int*)11;
此时p所指的内存空间不一定是可用的。因此像这样直接给指针赋值一个常数的情况非常罕见。
第四行,p存y的首地址。第四行执行完毕,内存如图4所示:
图4第四行执行完毕
第五行,把变量y的值改为12.第五行执行完毕,内存如图5所示:
写文章
Keil系列教程09_调试仿真
strongerHuang
?关注他
44人赞同了该文章
说明:
本文原创作者‘strongerHuang’
首发于微信公众号‘嵌入式专栏’,同时也更新在我的个人网站:EmbeddedDevelop
标签:Keil、 MDK-ARM、μVision
该教程基于MDK-ARM,大局部内容也适用于Keil其它3款(C51、 C251、 C166)产品。
一、写在前面
调试对于程序员来说是一个比较重要的环节,因此就很有必要掌握Keil的这些调试功能。
点击Debug调试按钮,进入调试状态,就会出现如下图Debug Toolbar调试工具栏。
我们调试须要用到的按钮包含上图框出来的这些按钮,但常用的按钮就是调试工具栏前八个:复位、全速运行、停下运行、单步运行、逐步运行、跳出运行、运行到光标行、跳转到暂停行。
二、相关描述
这些工具栏按钮的主要含义能够参看《Keil系列教程03_主窗口和工具栏详细说明》。
调试工具栏按钮位于菜单Debug中,同时调试视图窗口位于菜单View。能够参看文章《Keil系列教程04_菜单概述》。
同样,这些调试工具栏按钮可通过快速键实现,有些默认没有快速键,能够通过Configuration添加快速键。请参看文章《Keil系列教程08_Configuration(二)》Shortcut Keys快速键章节内容。
下面率领大家掌握那八个调试按钮、四个断点按钮,以及局部调试视图窗口的内容。
三、八个调试按钮
保证代码编译没误,点击“启动/停下调试”按钮(如下图),或快速键Ctrl + F5,进入调试模式。此时就会出现如文中开头的调试工具栏。
3.1 Reset复位
Reset复位,让程序复位到起点,恢复到初始状态。
3.2 Run全速运行
Run全速运行(F5),让程序处于运行状态。此按钮在程序停下运行时有效。
3.3 Stop停下运行
Stop停下运行,让正在运行的程序停下下来。此按钮在程序运行时有效。
3.4 Step单步运行
Step单步运行(F11),每点一次按钮,程序运行一步(单步),遇到函数会跳进函数执行(箭头进入括号)。此按钮在程序停下运行时有效。
3.5 Step Over逐步运行
Step Over逐步运行(F10),每点一次按钮,程序运行一行(单行),遇到函数跳过函数执行(箭头跳过括号)。此按钮在程序停下运行时有效。
3.6 Step Out跳出运行
Step Out跳出运行(Ctrl + F11),每点一次按钮,程序跳出当前函数执行(箭头跳出括号),直到跳出最外面的函数(main函数)。此按钮在程序停下运行时有效。
3.7 Run to Cursor Line运行到光标行
Run to Cursor Line运行到光标行(Ctrl + F10),将光标放在某一行,点击该按钮,程序全速运行,直到运行到光标的位置停下下来。(前提是程序能运行到光标所在行)。
3.8 Show Next Statement跳转到暂停行
Show Next Statement跳转到暂停行,点击该按钮,跳转到程序暂停(停下)所在行。主要用我们翻开多个文件,不知道程序暂停在哪里。此按钮在程序停下运行时有效。
四、四个断点按钮
调试时,这四个按钮也常用,从左到右依次是:插入/移除断点、使能/失能断点、失能所有断点、取消所有断点。
4.1插入/移除断点
Insert/Remove Breakpoint插入/移除断点(F9),点击一下按钮,在光标所在行插入一个断点,再点击一次,则移除断点。
该按钮操作即是用鼠标在代码所在行前面点击一次,注意独有在灰色所在行才有效(也就是程序能执行的地方),如下图:
4.2使能/失能断点
Enable/Disable Breakpoint使能/失能断点(Ctrl + F9),在有断点根底上,点击一下按钮,失能该断点,再点击一次,则使能该断点。
失能断点就是保留该断点,但该断点没效(程序执行到该处不停下)。
4.3失能所有断点
Disable All Breakpoints失能所有断点,保留所有断点,但都没效(和上面一个道理),但这里不能再次使能所有断点。
4.4取消所有断点
Kill All Breakpoints取消所有断点(Ctrl + Shift + F9),和上面失能不一样,这里是不保留断点,即取消工程所有断点,也叫杀掉所有断点。
五、调试视图窗口
调试视图窗口独有进入调试模式时才会出现,其窗口名称详情可参看文章《Keil系列教程04_菜单概述》中View菜单章节。
这里讲述几个在调试时常用的视图窗口。
5.1 Watch窗口
Watch查看变量的窗口,有Watch1和Watch2两个窗口,都一样,方便用户多个窗口同时查看。
查看这些调试视图窗口能够通过菜单,或点击工具栏按钮翻开。
选中变量 ->右键 -> Add ‘xxx’ to ->Watch1,此时自动会弹出Watch1窗口:
5.2 System Viewer窗口
System Viewer系统(外围IO、USART、TIM等)窗口,能够了解查看外设寄存器的窗口,如下图,查看USART寄存器值的窗口。
这些调试视图窗口只针对于调试时,方便大家查看一些调试信息,掌握这些内容会提高你的调试效率,以及更精确定位Bug。
六、说明
1.该文档仅供个人进修使用,版权所有,禁商用。
2.本文由我一个人编辑并整理,难免存在一些错误。
彩蛋:最近有同学跟我要单片机的资料,我特意花几个月时间,总结了我10年产品研发经验,资料包几乎覆盖了C语言、单片机、模电数电、原理图和PCB设计、单片机高级编程等等,非常适合初学者入门和进阶。除此以外,再含泪分享我压箱底的22个热门开源项目,包含源码+原理图+PCB+说明文档,不是市面上打包卖的那种课程,我认为教程多未必是好事,10年前我自学快,除了自身执行力以外,还有就是教程少。不要害羞做伸手党,等你一个小红点。后期我也会组建一些纯技术交流的小圈子,让大家能认识更多的大佬,有个好的圈子,你对行业的认知一定是最前沿的。