[音乐] 本周的主要内容是介绍高级语言程序的机器级表示,
上两讲分别介绍了C语言程序中的过程调用, 以及选择结构和循环结构对应的机器级代码表示,
本讲讲介绍数组和指针类型的分配, 和访问主要介绍全局静态区数组,
和函数内局部数组的初始化和访问,数组和指针之间的关系,
以及指针数组和多维数组的访问的。
首先我们来看一下数组的分配和访问,
数组是由一个一个的数组元素构成的, 它们在内存里面挨个按顺序进行存放,
比如说我们定义了一个具有4个元素的静态存储型,
short型的数组类型,这样的一个数组A,我们可以
声明成static表示它是静态的 short类型的数组,一共有4个数组元素,
那么,第i个元素,它的地址就是我们要去访问A[i]的时候, 那么这个A[i]
的地址必须算出来,这个算的公式是把数组的首地址 加上2乘上i,这个i是下标变量,
那么,这个地方为什么是2呢,是因为这是short型的,占2个字节,
我们假定数组A的首地址是放在edx 这个寄存器中,下标变量是放在ecx里面,
那如果我们要把A[i]这个数组元素
取到AX寄存器里面,所用的汇编指令应该是什么呢? 很显然这个肯定是用的是mov指令,
那么这个mov指令的长度后缀是应该是什么呢? 因为这个地方A[i]是一个short型的,
所以我们传送的长度是16位, 所以后缀用W表示,
传送的源是A[i], 目是AX,目的地是AX,所以源操作数的地址
我们是采用一种称为基址
加比例变址的方式,也就是说 这一个基地址就是数组的首地址,
是在edx当中的,这个i呢,
是下标变量,它实际上是一个变址值放在ecx里面,
每次是增加2个字节,也就是它的比例变址是2,
因为它是short型的数组,
因此,它是2乘上i,i在ecx里面,
这个是源操作数的地址,从这个地址里面,
取出来的就是A[i],然后最后呢,我们要送到AX寄存器里面,
所以我们可以看出所用的汇编指令,应该是这样一条指令。
这就是刚才的那条指令,比例因子就是2,
这个地方ecx就是i,称为 变址值,它存放在变址寄存器里面,
这个ecx实际上是一个变址寄存器, 这个i在循环当中等于0、
1、 2、 3,循环的话, 这个i每次都会增量,从0变到1,变到2,变到3,
下面我们来看一个例子,这里面给 出了几个数组S、
SA、 D和DA这4个数组,这些数组的元素类型分别是
char类型,然后是指针类型,double 类型,和指向double
的指针类型, 这些数组,它每一个元素应该有多长,
整个的这个数组的大小是多少,起始地址是什么? 元素i的地址,比如说S[i]的地址,SA[i]
D[i]、 DA[i],地址各是多少,我们可以来看一下,
很显然在这个地方,这个的数组元素的大小肯定是1,只有1个字节,
这个是指针,当然是4个字节,在32位机器当中,
double型当然是8个字节,指针还是4个字节, 数组的大小,因为它有10个元素,所以就是
10个字节,这边有10个元素,每个是4个字节,所以一共占40个字节,这边是
8个字节一个元素,10个元素当然是占80个字节,那么下面这个是40个字节,
起始地址很显然它是S[0]就是第一个 的元素,它的地址就是这个数组元素的起始地址,
这个就是第一个最前面的这个元素的地址,当然这个也是
D[0],它的地址 然后这边是DA[0]
的地址,第i个元素的地址 当然只要把起始地址加上i,
乘上这个比例因子就是 1,这个地方就是i乘上
比例因子是4,这个地方就是加上i乘8,
这个地方是加上i乘4, 这个就是元素i的地址,
所以在这个程序当中,我们要
去取第i个元素它的值,只要把第i个元素的地址
算出来,然后通过mov指令,把这个地址里面的值取出来就可以了,
根据刚才我们的分析,完整的表就是这样子的, 下面我们来看一个例子,
看一下这个数组元素,它在内存是怎么存放的? 现在我们有一个程序,在这里面定义了一个数组,
这个 buf 是int型的数组,只有两个数组, 数组元素,初始值分别是10和20,
在这个main前面定义的一定是在静态区进行分配的,
在静态区进行分配的这些数据链接以后,在可执行文件
当中,它是分配在这个数据段的, 那么有关可执行文件的格式,以及可执行文件对应
当中的这些存储段,比如说只读数据段,数据段,代码段等等,
这些概念我们在后面会介绍,现在我们直接用就行了,在这个里面我们知道
这个buf在静态区,假定在静态区的这个地址是这个地址,
也就是说它的这个第0个元素,就是包含0,
它所在的地址就是它,这个地址是8048908,
那么,这个字节的地址就是8048909,这个是804890A,
804890B,这个是804890C,每一个字节占一个单元,
每个单元的地址就是往后增就行了,0A的地址是908,909,90A,90B
C、 D、 E、 F,是这样子的, 刚才我们讲过了它的值,或者是这个最前面的那个元素的地址
都是等于它,那么通常编译器 会把这个放到寄存器里面,就是把这个值
会先送到edx里面,然后要用到这个值的时候,我们只要取edx 的内容就可以了,那么我们现在我们假定这边的这个i
这个i分配在ecx里面,然后这个sum的值分配在eax里面,
因为我们最后返回的这个值总是在eax里面的, 那么,我们来看一下这个循环体当中,
这个语句它对应的指令序列是什么样子的? 然后对i这个循环变量进行修订的这个语句,i++
它又是可以用什么指令来实行?很显然在这个当中,我们只要把eax的内容
加上这个把i里面的内容再送到eax里面去就行了,
所以这个地方我们可以猜出一定是用一个add指令,因为这个地方是+ 是add指令,add,add,
add指令,后缀应该是什么呢?因为这边是int型的,上面是int型的,
这后缀是i,表示4个字节,buf[i]的地址应该是等于
0,然后基地址是放在edx里面的,
就是它的首地址,所以是基址1,
EDX,变址值是放在ECX里面的
所以是ECX,比例变址当然应该是4,因为这是int型的。
E AX就是把sum里面的内容
和这个buf[i]的内容,相 加,加出来的结果还是放到EAX里面去
这是一种指令,当然我们也可以写成另外一种指令
就是addl,这个地方呢,buf的值实际上已经
得到了,是这个值,然后这边
基址值那就是0,然后这边是ECX是变址值,
然后是4是比例因子,然后这个
第一个操作数在EAX当中
这个和这个都是一样的,这条语句的功能当然很简单
就用add,当然也是l,因为i的
类型也是int型,所以也是l,i是在ECX里面的。
所以它只要和1
相加就可以了,这是i++对应的这个指令
因此我们看到,可以用这样的两条指令
它或它来实现前面这个,然后 用这条指令来实现后面这个
这个地方写错了,这个地方应该是 dollar符号,写错了,应该是dollar符号,后面是个立即数
刚才我们讲的那个例子当中,buf呢 是放在所有的函数前面的,因此它是静态区域
是一种静态的全局变量,如果我们把buf这个数组
定义在一个函数里面,它就是一个 局部变量,我们称为自动型的变量
它在分配的时候,应该是分配在栈里面的局部变量
数组的首地址呢是通过EPB来定位的
这个adder这个函数的栈帧的底部在这个位置
然后紧接着在这个栈帧里面就会给局部变量分配空间
这个buf有两个数组元素,因此它占8个字节
这8个字节分别是第一个数组元素,buf[0]是在
EBP减8的位置,后面一个数组元素在EBP减4的位置
所以这个地方放的是10,这个地方放的是20,应该是这样子的。
那么对buf这个数组进行初始化的这个指令,实际上也就是
往这个栈里面赋值,把10和20赋进去的这个指令
应该是什么指令呢?很显然,这个地方当然 也是mov指令,因为直接是传送,把10
和20传送过去,传送指令的后缀,mov指令的后缀是l,表示传送4个字节
因为这个地方是int型的,传送的应该是
立即数,比如说是第一个
buf[0]传送的是10,传送到的那个地址
应该是EBP减8的那个位置
所以这边应该是负8,然后基地址呢是
EBP,通过这条指令,我们就把10赋给了buf[0]
然后同样的,下面一条指令,也是mov指令 是把20赋给
EBP减4的那个位置 这个实际上就是buf[1]
刚才我们看到的实际上就是这样两条指令,把10
送过去,又把20送过去,执行完这样两条指令以后,实际上在这个位置
放的应该是10,对应的机器数就是 0000000A
这个地方对应的机器数就是00000014 所以这个是最小的地址
然后这个地址一点一点加到这,然后这边,14所在的地址也是四个单元当中最小的地址
所以它的机器数是按顺序写的 可以按顺序写,跟前面我们讲的那个例子当中
它是0A000000是不一样的
在这个里面,我们可以看到它占四个字节,这四个字节放的是
10这个真值,这个真值对应的机器数 是0000000A
然后它放在这个存储器当中的时候
那么这个0A是最小lsb 叫最低有效字节,最低有效字节
的地址是,最小的地址是908这个地址 这是909,这是90A,这是90B
因为IA-32是小端方式, 所以,最低有效字节放在最小的地址上面
所以我们这时候看的时候它是0A0000,是倒过来的。
因为我们这个地方,它是最小的地址,这是高地址 往这边是高地址,最左边的是低地址
而我们现在这个里面,最右边的是 低地址,最左边的是高地址,所以我们写的时候是这样子的
所以,这个要注意 假定这个buf的首地址在
EDX里面,得到buf首地址对应的指令应该是什么呢? 也就是说这个首地址,buf的首地址当然应该在这个地方
就是0A对应的这个地址,0A对应的这个地址实际上是ebp
减8这个地址,因此我们要把ebp减8 送到EDX里面去,就是把ebp减8
送到EDX里面,因此这个地方我们用的是装入有效地址这条指令,而不是用的mov指令
如果是mov指令,就意味着把ebp减8那个 位置当中的数据也就是这边的10
送到EDX,而我们现在要的是,不是10,而是10所在的这个地址
因此我们是用的取有效地址lea这样 的指令,而不是用的mov指令,这个是要注意的
这个地方如果是
sum和这个相加,而sum我们是放在eax里面的
因此,加的时候我们是把sum的值 加上这个buf[i],而buf[i]是
根据edx的内容,也就是buf的首地址 加上i乘4
得到的是buf[i]的地址,将其内容与eax中的sum相加
加出来以后再送到eax,也就是 再赋给sum,所以这个地方edx里面的值放的是
0A所在的这个地址,也就是buf这个数组的首地址
这个呢放的是要么是0要么是1,如果是0,那就是 0乘4还是指向0A这个地方开始
ecx=1也就是i等于1的时候,实际上是指向的是14这个位置的这个地址
然后,这样取四个字节取过来,进行相加,是这样子
[音乐]