没有读过第一篇的读者,可以点击这里,阅读深入研究C语言的第一篇。

问题一:如何打印变量的地址?

我们用取地址符&,可以取到变量的偏移地址,用DS可以取到变量的段地址。

1.全局变量:

我们看到,这里的全局变量是在数据段中的。

2.局部变量:

我们看到,这里的局部变量是在栈段中的。

问题二:研究main函数的偏移地址与源代码中main函数的定义位置之间的关系。

我们打印函数的偏移地址,在打印的过程中我们可以发现:

当程序编码如下时,程序运行的结果是:

而将程序的f1函数和f3函数互换,程序运行的结果如下:

可以看到,f1和f3的位置发生了改变,并且改变是相互颠倒了。

我们还知道C语言中有这样的函数声明定义方式:

我们查看他的结果:

我们看到,在第一种方式下,f1—main的偏移地址依次增大;第二种方式中,f1与f3的偏移位置发生了互换。而在第三种方式中f1—main的偏移地址依然是依次增大的。从中我们可以得出结论:C语言程序的函数从01fa处开始。按照函数实现的顺序依次排列。在这里,函数从01fa处开始的原因是由于编译和连接的过程中,在我们编写的函数前添加了一部分固定长度的内容。

问题三:

阅读TC2.0完整目录下的“HELPME!.DOC”,解决以下问题:

a) TCC.exe与TC.exe的区别?

b) TCC.exe和TC.exe生成的exe文件有什么不同?

首先我们参见文档中说明:

TCC.exe与Tc.exe的区别:TC.exe是一个集成环境,质上是命令行编译器集成编辑器,链接器和调试器。而TCC.exe只是一个命令行编译器。

TCC.exe和TC.exe生成的exe文件的不同:问:为什么。TC生成的EXE文件比由TCC.EXE生成的文件要大.在默认配置下TC.EXE生成的exe包含调试的信息。而TCC.EXE生成的没有。

问题四:

进一步通过debug观察两个程序分别通过TC.exe与TCC.exe生成的exe文件,理解TC.exe与TCC.exe对代码不同的优化。两个程序为:

a) 仅打印“Hello World!”的程序;

b) 拥有带参数的子函数的程序。

首先我们看打印“Hello World!”的程序:

源码:

看他们编译后的文件大小:

既然是比较不同点,我们就反汇编试试:开始的反汇编代码都相同,我们直接-U到01fa。发现:

左图为TC编译后,右图为TCC编译后

这里出现了明显的不同:

我们往前查看一些:

左图为TC编译后,右图为TCC编译后

我们可以看出TC编译后的程序,在寄存器保护上比TCC编译后的更加全面。

我们再看有带参数函数的程序:

分别编译并debug反汇编查看:

左图为TC编译后,右图为TCC编译后

我们看到,左图比右图多更多的寄存器保护的语句。

对于代码优化,TC更多的舍弃了效率和文件大小,来保证程序的安全性。而TCC更多的舍弃了程序的安全行,来生成精简的C程序,使得程序更加简短和高效。

问题五:第2章中,程序需要打印函数的段地址和偏移地址,在command中直接运行和在debug中运行打印的段地址不同,偏移地址相同,这是什么原因?

在这里,debug是用来调试程序的,他可以控制程序单步执行,并且查看程序运行中各种寄存器的状态。要做到这一点,debug肯定有他自己的控制方式。他需要将程序从debug内加载。而cmd运行程序,是系统执行的方式。他只需要接受系统的调用就可以执行。由于他们的运行方式不同,所以他们的段地址不同。但是,程序编译完成后,他的程序内的偏移地址就确定了(就像能我们打印main函数的偏移地址,说明这个地址是确定的)。而且每个程序都最大有64K的程序段和64K的数据段和栈段的混合段。所以在系统每次分配的时候,给每个程序都分配固定大小的但是位置不同的内存(一个64K的程序段,一个64K的数据段和栈段的混合段),即栈地址固定,偏移地址从0000-FFFF的内存。

问题六:第2章中,同时用多个dos窗口加载程序,打印所得的段地址和偏移地址都相同,这是什么原因?

在《汇编语言》书中,附注1的内容介绍了Inter系列微处理器的3种工作模式。

(1) 实模式:工作相当于一个8086。

(2) 保护模式:提供支持多任务环境的工作方式,建立保护机制。

(3) 虚拟8086模式:可以从保护模式切换至其中一种8086工作方式。这种方式提供使用户可以方便的在保护模式下运行一个或多个原8086程序。

而我们的windows是基于80386的。我们可以这样轻松的工作,开两个窗口,一个是工作于保护模式的word,一个是工作于虚拟8086模式的DBASE。

也就是说我们现在的command是虚拟8086的模式下工作的。既然是虚拟的,两个command之间就没有什么关联。两个command之间也就不会共用一段真实的内存(他们的内存是虚拟出来的)。所以打印所得的段地址和偏移地址都相同。

问题七:我们使用基于tc2.0的精简开发环境进行综合研究是为什么,这样做对我们有什么帮助?

使用精简的开发环境,可以减少我们的所面对的问题,集中精力解决我当前所要研究和解决的,C语言的基本问题。并且,这样做,我们可以更加深入的研究C程序在编译连接中所用到的深层次的、必须需要的过程。

问题八:语句printf(”%x %x %x\n”, main, &main, *main);打印的结果都是同一个值,试着解释原因。

我们编写这样一个程序:

这可以说明,printf是可以显示常量的。并且&和*常量,显示的都是常量的值。

我们知道,在汇编编译连接的过程中,其实是进行了两次,第一次是编译各种机器码,这是并不知道标号是在什么位置,第二次是在第一次完成后再次将翻译编译标号的地址。

C语言也有可能这样,main被翻译成了main函数所在的偏移地址(一个常量,这个常量的值是由第一次编译确定的)。这样,也就可以说明(long)main出来后显示的是段地址偏移地址的问题。

问题九:使用更多的方法完成打印main函数偏移地址

我们可以使用这样的方法打印:

我们还有这样的办法打印:

问题十: tcc精简环境编译生成的exe文件中的程序可有两个最大为64k的段,那么当我们需要的代码段或者数据段超出64k怎么办?

我们拿数据段做验证,首先计算数据段的大小:

我们知道,一个int型变量在内存中占两个字节,而数据段和栈段共用一个段。段的大小是64K,我们计算64K的数据段能存放多少个int型数据。答案是最多存放32767个int型。我们编写程序如下:

我们故意将数组的数量设为32768,结果发现在编译的时候TCC报错:说我们定义的数据超出范围。这说明在编译的时候,程序会自动检查你编写的程序的数据长度,如果超出,则编译不通过。(注,此时没有TLINK.exe文件)

我们将数组的值设为32767,然后编译,其错误信息如下:

这说明TCC的编译工作已经正常完成,但是由于没有TLINK.exe文件,无法生成.exe。我们放入TLINK.exe,再次编译。

我们发现其仍然提示段已经超出64K。直到数值粗略改到32500左右时,不再报错。

这是为什么呢?这里提出两种可能:

1. 编译连接器自动给程序保留栈:因为程序在执行的过程中不可避免的要用到栈,所以在连接的时候,编译连接器自动给程序保留了一部分栈内存。当它发现这部分内存加上本身定义的数据超过了64K,就报错了。

2. 编译连接器在编译连接过程中向数据段写入了内容:程序连接的过程,是TLINK.exe将c0s.obj、cs.lib、emu.lib、maths.lib中的相关代码与程序的代码连接到一起生成.exe文件。在这个过程中有可能向程序段中加入了数据。导致程序段超出64K。

对于两种猜想我现在还没有想到非常巧妙的方法证明,只能暂时这样猜想。

而:当我们的程序不得不大于64K时。我们可以向系统申请内存,或者使用没人使用的安全内存,将数据或者程序写入这段内存中,在从这段内存中使用数据或者调用代码。

深入研究C语言 第一篇(续)的更多相关文章

  1. 深入研究C语言 第一篇

    一. 研究过程 1.第一章:创建编译环境: 我们首先下载TC2.0,找到其中与编译连接相关的程序和文件: (1) 编译器:TCC.exe (2) 连接器:tllike.exe (3) 相关文件:c0s ...

  2. 深入研究C语言 第二篇(续)

    1. 关于如下的程序,关于结构体的拷贝,拷贝是拷贝到内存中的什么地方? 我们进入debug进行反汇编,单步等操作跟踪查看.发现: 在main中,我们看到call 0266应该对应的是转跳到func处执 ...

  3. 深入研究C语言 第二篇

    1. 程序一: 首先我们研究如下程序: 回答如下问题: 1. 程序运行时n,a,b,c的段地址在哪个寄存器中? 全局变量的存储空间在什么段里?局部变量的存储空间在什么段了?参数在什么段里?函数的返回值 ...

  4. C语言第一篇博客

    你对网络专业或者计算机专业了解是怎样? 进行网络安全,防止信息泄露. 你了解C语言么?C语言主要应用有哪些? C语言简言之就是一门计算机的编程语言. C语言主要应用于应用软件,服务器端开发,系统软件和 ...

  5. Scala语言笔记 - 第一篇

    目录 Scala语言笔记 - 第一篇 1 基本类型和循环的使用 2 String相关 3 模式匹配相关 4 class相关 5 函数调用相关 Scala语言笔记 - 第一篇 ​ 最近研究了下scala ...

  6. 如何起草你的第一篇科研论文——应该做&避免做

    如何起草你的第一篇科研论文——应该做&避免做 导语:1.本文是由Angel Borja博士所写.本文的原文链接在这里.感谢励德爱思唯尔科技的转载,和刘成林老师的转发.2.由于我第二次翻译,囿于 ...

  7. [老老实实学WCF] 第一篇 Hello WCF

    老老实实学WCF  第一篇 Hello WCF WCF(Windows Communication Foundation)是微软公司推出的面向服务技术的集大成者,涵盖继承了其之前发布的所有的分布式应用 ...

  8. 老老实实学WCF[第一篇] Hell wcf

    老老实实学WCF  第一篇 Hello WCF WCF(Windows Communication Foundation)是微软公司推出的面向服务技术的集大成者,涵盖继承了其之前发布的所有的分布式应用 ...

  9. (转)[老老实实学WCF] 第一篇 Hello WCF

    http://blog.csdn.net/songyefei/article/details/7363296#comments 老老实实学WCF  第一篇 Hello WCF WCF(Windows ...

随机推荐

  1. Win8.1无法安装.NET Framework 3.5的解决办法

    这个问题纠结了我很多天,恢复系统也没用,差点儿就重装Win8,现在终于解决了,你也来试试吧! 机型:台电X89 系统:Win8.1 with bing 故障:在未安装.NET Framework 3. ...

  2. xcode调试技巧

    xode报错有时挺无厘头,完全不知道哪里出的问题,最后还得用排除法,记录一些工作中认为有用的调试技巧 1.左侧视图点断点视图,左下角点加号,选择exception breakpoint,类型选c++, ...

  3. zookeeper dubbo 问题解决录

    问题1: 运行起来不报错,不过在Console没有zookeeper的心跳信息,也就是说没有配置上zookeeper,而出错的原因是下面蓝色这段解析不了 spring-dubbo-provider.x ...

  4. JQuery实现的模块交换动画效果

    <!doctype html> <html> <head> <meta http-equiv="content-type" content ...

  5. python学习心得第二章

    python基础 1.关于python编码的问题. python的编码现在主要是两种版本python2.7和python3.5 python2.7默认的是ascii码进行编译,我们可以采用 # -*- ...

  6. delphi中midas是什么

    Delphi中MIDAS到底是什么呢?和他相关组件是什么呢?   MIDAS(Multitiered Distributed Application Services)多层分布式应用服务.   Del ...

  7. 02.Sencha ExtJS 6 - What is Viewport?

    什么是Viewport? Viewport (Ext.container.Viewport)是一个专门的容器用于可视应用领域(浏览器窗口).Viewport渲染自身到网页的documet body区域 ...

  8. [软件工程] 查找二维数组最大子数组的之和 郭莉莉&李亚文

    一. 在主函数中实现二维数组的输入. 代码主要函数maxson(),主要利用for()循环先查找出最大字数组的四角的坐标xmin,xmax,ymin,ymax来确定最大子数组, 在循环中算出之和,编写 ...

  9. Percona 5.7安装

    一.从官网下载Percona5.7 地址:https://www.percona.com/downloads/Percona-Server-5.7/LATEST/ 需要注意是服务器的版本.我这里选择的 ...

  10. 托马斯微积分答案.djvu的书签

    ans.bookmarks --------------------------- <?xml version="1.0" encoding="UTF-8" ...