没有读过第一篇的读者,可以点击这里,阅读深入研究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语言 第一篇(续)的更多相关文章
-
深入研究C语言 第一篇
一. 研究过程 1.第一章:创建编译环境: 我们首先下载TC2.0,找到其中与编译连接相关的程序和文件: (1) 编译器:TCC.exe (2) 连接器:tllike.exe (3) 相关文件:c0s ...
-
深入研究C语言 第二篇(续)
1. 关于如下的程序,关于结构体的拷贝,拷贝是拷贝到内存中的什么地方? 我们进入debug进行反汇编,单步等操作跟踪查看.发现: 在main中,我们看到call 0266应该对应的是转跳到func处执 ...
-
深入研究C语言 第二篇
1. 程序一: 首先我们研究如下程序: 回答如下问题: 1. 程序运行时n,a,b,c的段地址在哪个寄存器中? 全局变量的存储空间在什么段里?局部变量的存储空间在什么段了?参数在什么段里?函数的返回值 ...
-
C语言第一篇博客
你对网络专业或者计算机专业了解是怎样? 进行网络安全,防止信息泄露. 你了解C语言么?C语言主要应用有哪些? C语言简言之就是一门计算机的编程语言. C语言主要应用于应用软件,服务器端开发,系统软件和 ...
-
Scala语言笔记 - 第一篇
目录 Scala语言笔记 - 第一篇 1 基本类型和循环的使用 2 String相关 3 模式匹配相关 4 class相关 5 函数调用相关 Scala语言笔记 - 第一篇 最近研究了下scala ...
-
如何起草你的第一篇科研论文——应该做&;避免做
如何起草你的第一篇科研论文——应该做&避免做 导语:1.本文是由Angel Borja博士所写.本文的原文链接在这里.感谢励德爱思唯尔科技的转载,和刘成林老师的转发.2.由于我第二次翻译,囿于 ...
-
[老老实实学WCF] 第一篇 Hello WCF
老老实实学WCF 第一篇 Hello WCF WCF(Windows Communication Foundation)是微软公司推出的面向服务技术的集大成者,涵盖继承了其之前发布的所有的分布式应用 ...
-
老老实实学WCF[第一篇] Hell wcf
老老实实学WCF 第一篇 Hello WCF WCF(Windows Communication Foundation)是微软公司推出的面向服务技术的集大成者,涵盖继承了其之前发布的所有的分布式应用 ...
-
(转)[老老实实学WCF] 第一篇 Hello WCF
http://blog.csdn.net/songyefei/article/details/7363296#comments 老老实实学WCF 第一篇 Hello WCF WCF(Windows ...
随机推荐
-
每日设置Bing首页图片为壁纸
闲来无事,手痒痒要做一个什么小工具. 于是乎便有了本文. 当有一个想法的时候,首先免不了网上搜索一番以便看一下有木有网友有过类似的想法. 很显然--有! 因此本文大代码是从几个地方搜索,然后组合的. ...
-
错误解决:SharePoint Designer 2010编辑后,出现数据源控件未能执行插入命令,data source control failed to execute the insert command
打了SharePoint 2010 最新的SP 2的补丁,但是使用SharePoint Designer 2010 定义任何一个列表的“插入视图”时,总是出现标题那样的错误: 数据源控件未能执行插入命 ...
-
【Other】千字文 硬笔 楷书 字帖
<千字文>是我国最优秀的一篇训蒙教材,用一千个汉字勾划出一部完整的中国文化史的基本轮廓,代表了中国传统教育启蒙阶段的最高水平.<千字文>通篇首尾连贯,音韵谐美,读起来朗朗上口, ...
-
[实变函数]2.3 开集 (open set), 闭集 (closed set), 完备集 (complete set)
1 $$\beex \bea E\mbox{ 是开集}&\lra E^o=E\\ &\lra \forall\ P_0\in E,\ \exists\ U( ...
-
set集合_定长
//set集合的操作 //便利初始化函数 NSSet *set1 = [[NSSet alloc] initWithObjects:@"aa", @&q ...
-
#ifndef 和 #endif
文件中的#ifndef 头件的中的#ifndef,这是一个很关键的东西.比如你有两个C文件,这两个C文件都include了同一个头文件.而编译时,这两个C文件要一同编译成一个可运行文件,于是问题来了, ...
-
获取linq生成的sql语句
命名空间:using System.Data.Objects; var query = db.TxtRes.Join(db.LangRes, a => new { id1 = a.ResID, ...
-
基于Windows服务器,从0开始搭建一个基于RTSP协议的直播平台
作案工具下载 EasyDarwin 服务端程序,用来接受推流和拉流 FFmpeg 可以用来推流视频数据到服务端,也可以从服务端拉流下来播放,也可以从一个服务端拉流下来,转推到另一个服务端去. Easy ...
-
oracle使用3DES加密
CREATE OR REPLACE PACKAGE dbc_cryptor IS SYSKEY VARCHAR2(16) := '0000000012345678'; SYSIV VARCHAR2(1 ...
-
2018CCPC网络赛
A - Buy and Resell HDU - 6438 The Power Cube is used as a stash of Exotic Power. There are nn cities ...