1. 程序一:

首先我们研究如下程序:

回答如下问题:

1. 程序运行时n,a,b,c的段地址在哪个寄存器中?

全局变量的存储空间在什么段里?局部变量的存储空间在什么段了?参数在什么段里?函数的返回值存储在什么地方?

全局变量的存储空间在什么时候分配?什么时候释放?

局部变量的存储空间在什么时候分配?什么时候释放?

2. 函数f3在调用与返回方式与函数f1与f2有何不同?

我们编译完成后,进入debug查看。

首先,我们执行到main函数处,然后开始单步执行。我们看到,每次单步执行时,涉及到取数据或者数据赋值的操作,debug都会显示出要操作的地址的地址和当前值。我们先来验证这个地址和值是否是正确的:

由于源程序中赋值n为0,不好验证是否正确,我们将n的赋值暂时改为n=10;我们验证如下:

(语句执行后DS:01a6处的值)

(语句未执行前DS:01a6处的值)

可以看到,n=10这条语句执行时,debug右侧显示出要操作的地址为DS:01A6,而程序执行后,DS:01a6处的值变为了0A,也就是说,被赋值的n的地址就是这里。由此,我们可以确定,debug单步执行时的地址和当前值是可信的。

我们依据每次单步执行时的debug值来确定每个变量的位置:

这里,n=0;全局变量的地址为:DS:01A6,DS段值为0B96。

执行到此,我们看到的依然是全局变量n的地址。

继续执行,我们进行到f2函数调用时。我们看到, f2(1,2);中的参数,2和1分别被压入了栈中。并且其参数压入的顺序是从右向左压入的。其段地址当然是SS。

在f2中参数被调用时,是用BP加偏移量的方式被调用。最后时,将结果给了AX。在这里变量C就没有再内存中赋值,而是直接用SI、AX来保存的过程值。而参数被调用的时候,用的段寄存器是SS。

另外在退出f2函数的过程中,我们看到:

在入栈是分别是AX(第二个参数)、AX(第一个参数)、BP、SI。而出栈是只将SI、BP出栈,并没有将AX出栈。在这个时候,参数就被释放掉了。

然后程序就返回了,所以我们在此处确定,函数返回值return是通过AX返回的。

将n赋值时,将变量AX的值赋值给了n。

这里也是n的赋值。

我们在编写这样一个程序:

从这里我们更清晰的看出局部变量是定义在栈段里。而且,我们看到在函数一被调用时就有sub sp+02,所以我们可以看出,在程序调用的时候就分配下了变量的空间。

所以:程序运行时,n的段地址是DS,A的段地址是SS,B的段地址是SS,C没有在数据段中,使用的是寄存器AX。

全局变量的存储空间在数据段中,局部变量的存储空间在栈段里,参数的存储空间在栈段里,函数的返回值储存在AX中。

全局变量的存储空间在执行到声明变量的语句时分配,程序结束后被释放。

局部变量在函数被调用时分配,在函数结束后被释放。

参数的存储空间在调用函数时分配,在函数调用结束后释放。

我们对比f1和f3调用的不同,发现:1.调用时,f1是call 偏移地址,而f3是call 段地址+偏移地址。2.返回时,f1是直接RET,f3是先RETF再RET。

2. 程序二:

我们编写如下程序:

问题:变量n与a的存储空间分配方式有何不同?

编译完成后进入debug跟踪。

我们看到,A在程序开始就被分配,A在偏移位置为0194的位置存放。而N则是PUSH进栈,这时候才在栈中分配的,在这之前是在SI中存放的。而且,C程序的编译器对程序作了优化,将语句简化。

我们做验证:

编写程序:

首先我们看到,C语言在实现的时候,并没有按照我们在C语言中的语句顺序这样一条一条的翻译,而在底层的实现方式是,将n=2与n++放在一起。且变量n放置在寄存器SI中。

3. 程序三:

我们编写程序如下:

问题:

1. 程序中所有变量的存储空间相邻么?tc2.0中,整型、字符型、长整数型数据的存储空间分别为多大?

2. 不同的数据类型对数据运算方式有何影响?

Debug查看main函数执行时a、b、c、a1、a2的地址。

观察知:a、b、c、a1、a2的地址分别为:0194、0196、0198、0199、019B。

我们看到他们之间的差值分别是:2、2、1、2。我们知道int型变量的大小是2字节,所以可以看到,这些变量是连续的。

而且我们还看到,在int和char行数据++的时候,都是用的inc指令,而long型的是用的ADD指令。说明不同类型的数据所对应的数据运算方法不同。

我们看到这里,long型的变量,先用add,在用adc指令。这是因为long型变量长度是4字节,而汇编没有四字节的加法。只能先将低两位进行加法,然后再将有高两位与溢出标志位进行加法(这里是因为低两位相加的时候有可能溢出,如果溢出,应该向高两位进1,,而adc指令是带溢出位进行运算的。这样以来就可以保证高两位的值是正确的)。这样来完整的完成一次四字节的加法。

我们查询TC的变量长度:

我们可以推测,a2后的变量的地址为019F。我们验证:

这里也说明不同类型的变量是连续的,也可以说明long占用了4个字节。

4. 程序四:

编写程序如下:

问题:变量a,b和他们的各个数据项的存储空间是如何分配的?

首先我们分析自定义数据类型:它是由一个int型,三个char型变量共同组成的。这样一个数据类型应该再内存中占用5个字节。而两个自定义数据类型的变量a、b,他们一个是全局变量,一个是局部变量。在分配时,应当一个在数据段中,一个在栈段中。

编译完成后debug查看结构体内的变变量来进行验证:

我们看到,在自定义的结构体stu类型的全局变量a中,其各个数据项的排列是连续的。且整个变量a都在数据段,所占用的长度为5个字节。

在在自定义的结构体stu类型的局部变量b中,其各个数据项的排列依然是连续的。且整个变量都在栈段里,所占用的字节为5个。并且,在这里我们发现,虽然是对栈段做操作,但是这里没有用PUSH指令,用的也是MOV指令。并且我们看到,在程序一开始的时候,

Sp就做了相应的修改,把b的空间分配出来了。

5. 程序五:

我们编写如下函数,看结构体变量是如何传递和返回的:

反汇编如下:

我们看到,在汇编中有LEA这条指令,LEA就是目标地址传送指令: 将一个近地址指针写入到指定的寄存器。格式:LEA reg16,mem16。

在程序中,我们看到这这样的调用,结合上下文我们可以确定是调用的func函数。我们看其反汇编的代码:

我们看到,程序将0b28给了ax,但是我们知道,ax存放子函数返回值的寄存器。那么ax中放的是自定义的数据类型的变量么?显而易见,ax是放不下5个字节的。那么,又没有可能是偏移地址呢?我们查看。

果然,在数据段中我们找到了自定义类型a的存放地址。也就是说,func函数返回自定义类型的变量是靠返回其在数据段中的偏移地址返回的。

我们查看f函数的语句,看到f函数的偏移地址为0256。

我们找到调用语句:

我们看到,F中是用栈传入的结构体变量。

所以,我们可以知道,自定义数据类型的变量在函数中返回时,是靠返回其在数据段中的偏移地址返回的。而其当做参数被调用时是借助栈机制传入函数的。

深入研究C语言 第二篇的更多相关文章

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

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

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

    没有读过第一篇的读者,可以点击这里,阅读深入研究C语言的第一篇. 问题一:如何打印变量的地址? 我们用取地址符&,可以取到变量的偏移地址,用DS可以取到变量的段地址. 1.全局变量: 我们看到 ...

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

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

  4. 【OpenGL】第二篇 Hello OpenGL

    ---------------------------------------------------------------------------------------------------- ...

  5. 第二篇 SQL Server代理作业步骤和子系统

    本篇文章是SQL Server代理系列的第二篇,详细内容请参考原文. SQL Server代理作业由一系列的一个或多个作业步骤组成.一个作业步骤分配给一个特定的作业子系统(确定作业步骤去完成的工作). ...

  6. 【译】第二篇 SQL Server代理作业步骤和子系统

    本篇文章是SQL Server代理系列的第二篇,详细内容请参考原文. SQL Server代理作业由一系列的一个或多个作业步骤组成.一个作业步骤分配给一个特定的作业子系统(确定作业步骤去完成的工作). ...

  7. [翻译]Go与C#的比较,第二篇:垃圾回收

    Go vs C#, part 2: Garbage Collection | by Alex Yakunin | ServiceTitan - Titan Tech | Medium 目录 译者注 什 ...

  8. [ 高并发]Java高并发编程系列第二篇--线程同步

    高并发,听起来高大上的一个词汇,在身处于互联网潮的社会大趋势下,高并发赋予了更多的传奇色彩.首先,我们可以看到很多招聘中,会提到有高并发项目者优先.高并发,意味着,你的前雇主,有很大的业务层面的需求, ...

  9. 深入理解javascript对象系列第二篇——属性操作

    × 目录 [1]查询 [2]设置 [3]删除[4]继承 前面的话 对于对象来说,属性操作是绕不开的话题.类似于“增删改查”的基本操作,属性操作分为属性查询.属性设置.属性删除,还包括属性继承.本文是对 ...

随机推荐

  1. Linux 之HTTP服务,APACHE

    1.基础知识 HTTP:超文本传输协议,超链接URI:Uniform Resource Identifier,全局范围内唯一命名符MIME:Multipurpose Internet Mail Ext ...

  2. XMl.02-约束

    DTD约束 DTD的书写位置 XML构建模块 DTD定义元素 DTD属性的定义 DTD实体的定义 schema约束 命名空间 schema约束的书写流程 XML被设计为一种很灵活的标记文档. 但是,有 ...

  3. WCF Misconfiguration: Security Not Enabled

    Abstract: No transport or message security has been defined. Explanation: Applications that transmit ...

  4. RabbitMQ消息队列1: Detailed Introduction 详细介绍

    1. 历史 RabbitMQ是一个由erlang开发的AMQP(Advanced Message Queue )的开源实现.AMQP 的出现其实也是应了广大人民群众的需求,虽然在同步消息通讯的世界里有 ...

  5. DOM Document

    1.DOM Document对象 定义:每个载入浏览器的 HTML 文档都会成为 Document 对象.Document 对象使我们可以从脚本中对 HTML 页面中的所有元素进行访问. Docume ...

  6. Word2013创建目录

    1.写好文档内容后,将光标移到标题行,点击“开始”里的“样式”->“创建样式”,为该标题创建一个新的样式,同时点击“修改”,在打开的窗口中选择左下方的“格式”,进行标题格式的调整.依次可设定子标 ...

  7. 关于nagios监控远程服务器对服务器性能影响的测试

    1.    Nagios监视远程服务器时,是通过在控制端执行以下命令进行数据收集的: /usr/local/nagios/libexec/check_http -I 192.168.16.11 /us ...

  8. sftp上传 - 待完

    1.搭建本地sftp 1.1.下载msftpsrvr.exe软件 下载地址:http://www.download3k.com/Install-Core-FTP-Mini-SFTP-Server.ht ...

  9. Jquery / js 判断数据类型方法(限制文本框类型输入)

    当想要判断文本框中的值是否为自己想要的类型时,可以通过一些方法作出判断,这里对于光标离开文本框时判断文本框中输入的是否是数值类型,如果不是,做出提示 $("#WORKYEARS") ...

  10. web前端安全 XSS跨站脚本 CSRF跨站请求伪造 SQL注入

    web安全,从前端做起,总结下web前端安全的几种技术: 1,XSS XSS的全称是Cross Site Scripting,意思是跨站脚本,XSS的原理也就是往HTML中注入脚本,HTML指定了脚本 ...