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. 深入浅出讲解:php的socket通信

    对TCP/IP.UDP.Socket编程这些词你不会很陌生吧?随着网络技术的发展,这些词充斥着我们的耳朵.那么我想问:1.         什么是TCP/IP.UDP?2.         Socke ...

  2. Google V8编程详解(二)HelloWorld

    转自http://blog.csdn.net/feiyinzilgd/article/details/8248448 上一章讲到了V8的编译和安装,这一章开始从一个demo着手. 这里选用了官方文档的 ...

  3. 转向Web

    一直搞桌面软件开发,用C++绘界面那蛋疼的日子是记忆犹新,也不想钻研什么网络协议了,好好搞Web方面吧,它的开放很让人激动 Python是个好东西,适合许多场合,包括云计算,科学计算,软件测试,运维等 ...

  4. Android Studio tips2

    Android不推荐把字符串进行硬编码,一般的做法是把字符串定义在laylout里,并在xml文件里对键值进行引用 例如<第一行代码>中 Hello word程序中"Hello ...

  5. MongDB/C# 杂项

    1.MongDB的时间类型字段输出时为UTC的解决方法:保存到数据库中的数据还是按UTC存的,读出来的就按标识值读 [BsonDateTimeOptions(Kind = DateTimeKind.L ...

  6. 通过属性 Cesium的FBO主要支持两种方式

    角色其实就是一类权限的分组,所以给用户分配角色其实也是在给用户分配权限.在oracle中有三个比较常用的角色.对于一般不是很严格的系统可以授予开发用户CONNECT.RESOURCE角色权限即可. 其 ...

  7. machine learning 笔记 normal equation

    theta=(Xt*X)^-1 Xt*y x is feature matrix y is expectation

  8. FZU 1686 神龙的难题 (重复覆盖)

    Problem 1686 神龙的难题 Accept: 397    Submit: 1258Time Limit: 1000 mSec    Memory Limit : 32768 KB  Prob ...

  9. LeetCode OJ 106. Construct Binary Tree from Inorder and Postorder Traversal

    Given inorder and postorder traversal of a tree, construct the binary tree. Note:You may assume that ...

  10. 2016CCPC 合肥--最大公约数//每一年通向它的路上,多少人折戟沉沙,多少人功败垂成,有人一战成名,有人从头再来。

    有这样一个有关最大公约数的函数:函数 f(x, y): { c=0 当 y>0: { c +=1 t = x % y x = y y = t } 返回 c * x * x} 给出三个正整数n,m ...