C语言入门篇-02

点击链接加入群【�g攻防�J�泰g】:

上篇扯了许多废话,今天就不扯废话了,总之大家要认识到IT界是一个很庞大的体系,从做硬件,焊接电容电阻的工程师,到电路设计,到电路板制作,到软件程序设计,到通信,网络通信,串口通信,界面编程,WEB编程,数据库编程,数据库系统,音频编程,视频编程,安全编程,系统编程,应用程序开发,电脑城做IT销售的,装系统的,电脑维修的,数据恢复的,平面设计,3维设计,高级特效设计,服务器管理,网络架设等等,而每一个具体的方向又是非常复杂,反正举这么多例子 并不是让大家都了解这些,一个人的精力是有限的,我们只能攻其一个方向,但就是一个方向要搞精,不要这也学那也学,学到最后没有学到真本事,现在中国就是不缺二流人才,缺少的是一流人才。好了,说了不扯,又扯上了。

回到正题,今天讲点啥呢?我感觉,学C的人能找到我这文章,应该不是小白了,至少你懂得变量 循环 等知识了。如果这也不懂,那么你得随便找本书看看基本语法。

先来回顾一下01里主机讲了哪些知识点吧,对内存的认识,地址,字节,还讲了整数的存储及范围,还有就是逻辑地址哦。今天就来讲一些有关程序的细节吧。

当我们写一个hello world的程序时,点击组建 就会得到一个 .exe 文件,它是怎么组织的呢?它里面都有哪些内存呢。我们称这样的.exe程序为PE文件,它是微软公司对COFF的扩展,关于COFF,大家可以http://en.wikipedia.org/wiki/COFF 查看一下它的详细介绍,E文不好就不要看了,它是对可执行文件格式的一种规范,微软推出自己的COFF 也就是PE,有一本叫 windows pe 权威指南,讲的就是这个文件格式,不过那本书很高级,做开发的一般不需要研究太深入,了解个大概就行了,而我们要知道的就是,PE是一个文件结构,它有自己的规范,比如我们在程序中写的代码放在.exe文件的什么位置,我们定义的字符串放在.exe文件的什么地方,我们的资源文件放在.exe的什么位置,大概了解一下就行,但是你要有个认识,就是有一种规范在约束着它,如果你的程序不符合这种规范,那么它就不能被操作系统所认识,也就是你随便把一个图片改成.EXE 然后双击执行 ,系统是不会执行的,因为它并不认识 ,关于PE结构大家是有能力就了解,没时间就可以不用了解了,知道它是一个什么东西就行了。

那么我们在加载hello world.exe 系统为它分配了哪些内存呢?这个我们先把这个给大家一个答案。

栈、堆、代码、数据 我们暂时就可以认为是这四个主要内存块,而且在 XP VC6 的环境下的这些地址是相对稳定的。

内存块名字 起始地址
栈区 0X0012E000左右,只是相对稳定
堆区 0X00380000左右,只是相对稳定
代码区 0X00400000
数据区 位于代码区高地址区

对于上表,我们没有给出范围大小,只给出起始地址,并不是说没有,而是大小范围并不一定,我们可以将光标移动到 printf 那句代码的上面,然后按F9 下个断点,然后按F5开始调试,这个时候,程序执行到刚才设置的断点处,默认会弹出一个名叫调试 的小窗口(工具栏),可以拖动它,将它摆到合适的位置,小窗口中有一些按钮,我们可以把鼠标放在上面,停一会会有提示信息,我们需要找的第一个就是memory窗口,点一下,会弹出内存数据窗口,memory窗口是可以调整大小的,建议刚开始把这个窗口接长一点,因为我们要看一下这里面的数据。

1、观察4G的逻辑地址空间,内存窗口有一个address(地址) 文本,在它右边是一个编辑框,可以输入地址,下面有三列,左边一列就是地址,中间是地址里面的数据(16进制),右边一列是数据对应的ASCII字符,那么我们可以看一下地址的范围,为了方便 ,我们01里讲了 逻辑地址空间范围是 从0 到 2^32-1 (32个1,就是FF FF FF FF(以后我管它叫8个F),如果看成32位有符号数的话,就是-1,什么不会算?请看注1),所以我们只需要在编辑框里输入0 就可以定位到起始逻辑地址,输入-1就可以定位到终止逻辑地址,我们可以适当的拖动一下滚动条看一下。


注1:这里是对01知识的一个小回顾,首先这个是32位有符号数,那么我们只需要将它们的值加起来求和就行了。

1111 1111 1111 1111 1111 1111 1111 1111

其实就是 (2^0+2^1+2^2+....+2^30)+ (-2^31) = 2^31-1 -2^31 = -1 用的还是01里的公式哦。

当我们定位到0 或-1的时候,我们发现这些区域里的数值都是?? ,它是什么意义呢,还记得01里说过地址是从物理内存映射过来的吗,也就是逻辑地址空间是一个虚拟空间,真正的空间在物理内存里,逻辑地址只是MMU将物理内存做了一个映射,??表示这个区域并没有对应真实的物理内存,那么都有哪些地址映射了物理内存呢,我们已经在上面的表格中给出来了,大家可以在编辑框中输入 0X12E000,然后看看这个区域有没有数据,如果没有,那么就在内存窗口中随便点一下(激活窗口),按键盘上的PAGEDOWN 键,一直按,直到看到数据,然后找找这个区域的边界,如下图所示(图1),是起始位置

124539502.jpg

好,我们已经看到栈区的开始位置了,接下来就是要定位栈区的结束位置了,怎么定位呢,很好办,还是在内存窗口中点一下以激活内存窗口,然后按键盘上的PAGEDOWN向下翻页,当你看到?? 的时候就证明你已经超过了栈区的下限了,再按PAGEUP 向上 慢慢调整,最终,我们找到如下图的位置,当然这个地址并不是固定的,也就是在你的电脑上可能不是这个地址哦。

124622661.jpg

好了,我想大家都明白了,原来栈就是一块连续的内存,既然是内存块就有起始地址和终止地址。

2、我们再来观察一下堆区吧,系统默认为我们申请了一个堆区,我们使用malloc 等函数进行内存申请的时候,实际上就是在这块区域进行申请的,那么它在我们逻辑地址空间的哪个位置呢?让我们稍微修改一下代码,如下图

124707932.jpg

然后按F5开始调试,这个时候,正常情况下程序会运行到printf 函数就暂停下来,我们将鼠标放在 p变量上,稍微停一小会,就会显示出p的值,如果没有显示出来就多试几次,在我这里显示p的值是0x003807b8 ,好,现在打开内存窗口,在地址编辑框中 输入这个 地址,不要忘了0X,效果如下图

124803316.jpg

然后我们再通过PAGEUP 向上翻页,找这块内存区域的上限,通过PAGEDOWN向下翻页,找这块内存区域的下限

上限图

124839358.jpg

下限图

124919594.jpg

这样我们就定位到了第二个重要内存区域了,这个区域叫做堆区,我们用一些malloc calloc等函数就是在这个区域申请内存哦,要记住哦。

3、下面我们就要研究一下代码区域了,其实我们写程序的一部分就是在组织机器指令,说白了就是在做01的排列,这是最底层的理解了,那么我们就来看看,我们写的程序生成的机器代码是什么样子的呢? 还是上面的例子,继续在调试状态,我们点一下调试窗口里最后一个按钮,叫做反汇编代码窗口,这样就会弹出源码对应的反汇编代码区域了,如下图

124950729.jpg

在这里,我们可以看到一些汇编指令 如 push,call ,add,mov等,我们暂时不关心它们是什么意思,我们在这个窗口里右击,选中Code Bytes,然后窗口中会显示一些额外信息,如下图

125015784.jpg

我们会看到这里面多了一些 16 进制数据,如图中黑色区域(什么,不知道我怎么选中的,告诉你吧,按住ALT键 再进行选中操作,这个方法在许多软件里都有实现,VS里也支持,PUTTY里也支持,You out la),那么它们是什么?它们就是传说中的机器字节码,只不过是16进制表示的,如果你愿意,你可以将它们转成2进制哦,那可真就成了天书了,那么在它们每行的前面都有个地址,这个地址就是逻辑地址哦,也就是说,这些字节码现在就在我们的内存中,在什么区域呢,在代码区呀,,,哈哈,知道了吧,我们上面已经说了,代码区位于0X00400000开始,我们可以看到 每行前面的地址也基本上位于这个区域范围里呀,现在我们就来定位一下吧

代码区上限

125047843.jpg

代码区下限

125213163.jpg

代码区范围还是比较大的

这样子定位是不是很慢呀,有什么较快的办法吗,答案是有的,记住,这些操作是在调试状态下才有的哦,点击菜单栏中 debug(调试),下面有个Modules(模块),点击会弹出对话框,如下图

125128446.jpg

看到了吧,Hello World的地址是 0X00400000 - 0X0042BFFF 这就是代码区域哦。

那么有没有想过这个问题,这些数据是从哪里加载到内存中的呢?你能猜想出来嘛,好吧,我来告诉你,在PE文件里,也就是我们的.EXE文件,

我们随便记下一段指令,就记下 上面有一张图里有一个我选中的黑块,就那一段指令吧,我们把程序停掉。然后点击文件打开,弹出如下对话框

125240458.jpg

在文件类型里,选中所有文件,Open as选成Binary,定位到 HelloWorld.exe(在Debug目录里哦),然后点击打开,如果一切正常的话,会看到下图所示的内容

125310642.jpg

我们按CTRL + F 进行搜索,搜索什么呢,就是那个黑块里的内容,6A 0C E8 01 6E FF FF 83 C4 04 89 45 FC ,搜索,结果如图

125334813.jpg

怎么样,理解了吗,我们写的程序生成的机器字节码都被写进了.exe里,当操作系统加载我们的程序时,为我们的程序分配内存空间,做地址映射,将我们的字节码加载到内存中,其实不只是机器字节码写进去了,字符串也被写进去了呢,你要不相信,可以试试搜索我们printf函数输也的字符串哦,如图

125357795.jpg

明白了吧。这就是PE文件的简介,反正就是要大家理解,我们写的代码生成的数据,代码,资源等都被写入了PE文件里啦,也就是他们是有组织有纪律的一个标准的文件,你不会忘了我们前面讲过的有规范约束着它呢吧。

好,还有一个就是数据区了,数据区相对来说比较简单,当我们在程序中定义一些全局变量,在函数里定义一些静态变量,这些变量是存在于静态数据区的,我们来演示一下。

修改代码,结果演示如下图

125435618.jpg

在地址栏输入全局变量名字,会定位到我们的全局变量的地址,我们可以清楚的看到它们的值

还记得小端模式吗,我们定义的是 0X99887766,但是它在内存窗口显示的却是66 77 88 99 这就是小端模式,而且后面的两个数值也可以看到了,一个就是0X11223344,另一个就是0X55667788,是我们定义的两个其它的静态变量,如果直接在地址栏里输入val,它是计算不出地址的,一定要写全局变量的名字才行哦。我们重点是关心它们的地址,在0X00427318,其实就是在代码区域的大地址端。

好了,这一节就介绍这么多,我感觉如果你把这些理解透了,你的根基可以说是非常牢了,我的建议是希望大家把基础打牢,有句话讲的好呀,基础不牢,地动山摇。哈哈,玩笑开大了,,,今天说的这些并不是要大家全记下来,关键是理解好程序的分区,至于几个区域的作用,我们以后再讲哦。88再见!!!!!!!!!!!!!




你可能感兴趣的:(C语言学习)