1.静态、动态存储变量的区别 C语言源码: fun() { int i = 0x100; static int c = 0x200; i = c; } 汇编代码: push bp mov bp, sp push si mov si, 100h ;定义动态变量 i = 0x100 mov si, ds:[0000] ;对静态变量赋值i = c;ds:[0000]内存储的是静态变量C pop si pop bp retn 总结: 其实静态变量类似于全局变量,实现方式类似。 |
C语言源码: void change(int[]); fun() { int a[] = {1,2}; add(a); } void change(int a[]) { int tmp; tmp=a[0]; a[0]=a[1]; a[1]=tmp; } 汇编代码: push bp mov bp, sp sub sp, 4 ;开辟堆栈空间,保存数组数据 push ss lea ax, [bp-4] push ax ;将es:di压栈 mov ax,0 push ax push ds ;将ds:si压栈 mov cx,4 ;设置数组数据所占字节数,即需复制字节数 call sub_100C7 lea ax, [bp-4] ;此命令就是将数组的首地址传给调用的函数 push ax ;将数组的首地址压栈传递给子程序change call sub_100C6 pop cx ;平衡堆栈 mov sp, bp pop bp retn sub_100C6: ;change函数 push bp mov bp, sp push si push di ;tmp局部变量 mov si, [bp+4] ;将数组首地址读入si寄存器 mov di, [si] ;si保存的是一个地址,这个地址中存储就是a[0],即指针 tmp = a[0] mov ax, [si+2] ;ax = a[1] mov [si], ax ;a[0]=ax a[0]=a[1] 通过ax寄存器中转 mov [si+2],di ;a[1]=tmp pop di pop si pop bp retn sub_100C7: ;复制数据段数组数据至开辟的堆栈空间中 push bp mov bp, sp push si push di push ds lds si, [bp+6] les di, [bp+0Ah] cld shr cx, 1 rep movsw adc cx, cx rep movsb pop ds pop di pop si pop bp retf 8 总结:传递数组其实就是传递数组的首地址,可以理解为传递的是数组指针,对数组的操作,将会影响数组中的数据。 |
这个的内容比较多,有点重量级。呵呵。。 C语言源码: int add(int,int); fun() { int a = 0x10; int b = 0x100; int c = add(a,b); } int add(int a,int b) { int c; c = a + b; return c; } 汇编程序代码: push bp mov bp, sp 压栈bp,bp寄存器将在后续部分作为基址寄存器使用 sub sp, 2 在堆栈中分配空间给变量C push si push di mov si, 10h 变量a=0x10 mov di, 100h 变量b=0x100 push di push si 压栈b,a做为参数传递给add函数 call sub_100B0 pop cx pop cx 平衡堆栈,pop cx机器指令较短,执行效率高,相当于add sp,4 mov [bp-2], ax 将返回值付给变量C,C语言中通过ax寄存器返回函数值 pop di pop si mov sp, bp pop bp retn 至此函数fun返回 sub_100B0: push bp mov bp, sp push si mov si, [bp+4] c = a add si, [bp+6] c = a + b mov ax, si 将结果传给ax寄存器 pop si pop bp retn add子程序返回 总结:函数进入时基本上都有push bp,mov bp,sp语句,此两条语句将bp寄存器压栈,过后做为基址寄存器使用。 调用函数时,先将参数从右到左依次压栈,执行call指令时,将ip压栈,接着将bp压栈,所以在16位的CPU中,取参数时,bp+4取得第一个参数,bp+6取得第二个参数,依次类推。局部变量使用使用sub sp,2指令在堆栈中开辟空间存储,所以取局部变量时,通过bp-2取得第一个局部变量,bp-4取得第二个局部变量,函数若有返回值一般通过ax寄存器返回。最后要执行mov sp,bp,pop bp指令平衡局部变量占用的堆栈空间,实际的参数并未清空。Ret后弹出ip值,返回调用程序执行的地址。 一般情况下由调用函数的程序平衡堆栈,所以上例pop cx执行两次,相当于add sp,4平衡堆栈。但数组中传送字符串的函数使用了ret n,由子程序本身进行了平衡堆栈的工作。 从上可以看到,在调用函数时,调用参数并未在子函数中更改,即在子函数中使用了参数值,但并未真正更改参数的值。使用指针做为参数时的情况,后续再研究。 |
4.数组的定义使用 C语言程序: int x[] = {1,2}; int i = x[0]+x[1]; 汇编程序代码: Push bp mov bp, sp sub sp, 6 用到三个局部变量,其中数组2个,所以分配6个字节的堆栈空间 push ss lea ax, [bp-6] push ax push ds mov ax, 194h push ax 以上压栈操作给子程序传递参数 mov cx, 4 数组占用字节长度 call SCOPY@ 注意:此处为远调用,压入CS、IP寄存器 mov ax, [bp-6] ax寄存器内容x[1] add ax, [bp-4] x[0]+x[1] mov [bp-2], ax 将结果付给[bp-2],即变量i mov sp, bp pop bp retn 以上语句为返回子程序 子程序SCOPY@ push bp mov bp, sp push si push di push ds 保存子程序中用到的寄存器 lds si, [bp+6] ds:si为调用程序中的DS,Ax的值,初始化字符串复制源地址 les di, [bp+0Ah]es:di为堆栈空间的地址 cld shr cx, 1 rep movsw 将数据段中定义的数组内容复制到堆栈中分配的空间 adc cx, cx rep movsb 此处有疑问,不太清楚。 pop ds pop di pop si |