c语言元素实现汇编分析

关于C语言局部变量的研究
2009-06-23 10:33

前言:最近学习了汇编语言,想通过反汇编TC2.0的程序,了解C语言的各项元素到底是如何实现的。先做TC2.0的研究,DOS下的C语言研究透彻了。打好基础,学完Windows编程后,再对VC6的东西进行反汇编。

变量:C语言的局部变量一般定义在堆栈中,不同的类型占用不同的字节数。

以下定义了一个int整型变量:

push bp         将bp寄存器压栈,在后续程序中做为基址寄存器使用。

mov bp, sp

sub    sp, 2       在栈中分配空间存储局部变量

mov [bp-2], 100h为局部变量赋值

mov sp, bp      恢复sp寄存器,将局部变量占用堆栈恢复,之前不能改变bp寄存器内容,否则程序无法正常返回

pop bp          

retn                  弹出ip寄存器内容,函数返回

a)         整型变量:long 之外其他的整型,均分配16位,占用两个字节;long整型占用32位,占用四个字节,一般会放在ax,dx寄存器中。

b)        浮点型变量:比较复杂,暂未分析。

c)        字符变量:使用8位(即一个字节)存储字符,但C语言编译器在栈中分配2个字节的存储空间(或许是因为分配两个字节效率高)。


C语言运算符的实现
2009-06-23 10:38

C语言程序:

int a = 0x100;

int b = 0x200;

int c,d,e,f,g;   

c = a + b;

d = b - a;

e = a * b;

f = b / a;

g = b % a;

对应的汇编语句:

push    bp

mov    bp, sp

sub     sp, 0Ah   分配堆栈

push    si        保存函数中要用到的寄存器,C语言偏向使用si,di寄存器

push    di

mov    si, 100h

mov    di, 200h

mov    ax, si

add     ax, di

mov    [bp-8], ax   c = a + b

mov    ax, di

sub     ax, si

mov    [bp-6], axd = b - a

mov    ax, si

mul    di

mov    [bp-4], axe = a * b

mov   ax, di

cwd

idiv    si

mov    [bp-2], ax   f = b / a

mov    ax, di

cwd

idiv    si

mov    [bp-0Ah], dxg = b % a

pop     di

pop     si         恢复函数中使用的寄存器

mov    sp, bp        恢复堆栈指针

pop     bp

retn               返回程序pop ip

呵呵。。。写的比较简单,但是基本上反应了C语言编译器的意图。确实很佩服写TC2.0编译器的那些前辈,反汇编的代码精炼的很。C语言的执行效率就是通过简练的汇编代码保证的。

C语言的算术运算
2009-06-23 10:39

C语言程序:

int a = 0x100;

int b = 0x200;

int c = 0x300;

int d = a * b + c + 'a';

对应的汇编语句:

push    bp

mov    bp, sp

sub     sp, 4

push    si

push    di

mov    si, 100h

mov    di, 200h

mov    [bp-4], 300h  变量C

mov    ax, si

mul    di                  a * b

add    ax, [bp-4]    a * b + c

add    ax, 61h ; 'a'   a * b + c + ‘a’

mov   [bp-2], ax     将计算结果赋给d变量

pop    di

pop    si

mov   sp, bp

pop   bp

retn

写的都比较简单,但是原理都是一样的。

++、- -、+=的研究
2009-06-24 11:36

C语言程序:

int x = 1;

int y = 10;

int i = 0x99;

x++;

x = x + 1;

y--;

y = y - 1;

i += 1;

i += 2;

对应的汇编语句:

push   bp

mov   bp, sp

sub    sp, 2

push   si

push   di

mov   si, 1            变量x

mov   di, 0Ah        变量y

mov   [bp-2], 99h   变量i

inc    si                x++

mov   ax, si

inc    ax

mov   si, ax          x = x + 1

dec    di                y--

mov   ax, di

dec    ax

mov   di, ax            y = y - 1

inc    [bp-2]           i += 1

add    [bp+var_2], 2i +=2

pop   di

pop   si

mov  sp, bp

pop    bp

retn

后续还有更深入一些的研究。


C语言的关系运算符
2009-06-24 11:38

1、大于号的使用

C语言程序:

int x = 1;

int y = 10;

if( x > y)

      x = 0x10;

else

      y = 0x100;

对应的汇编语句:

push   bp

mov   bp, sp

push   si

push   di

mov   si, 1

mov    di, 0Ah           x=1;y=10xy赋值

cmp    si, di               比较xy

jle    short loc_100A6小于或等于转移至y=0x100

mov    si,10h

jmp    short loc_100A9跳至返回部分代码

loc_100A6:

mov    di, 100h

loc_100A9:

pop    di

pop    si

pop    bp

retn

2、大于等于号的使用

C语言程序:

int x = 1;

int y = 10;

if( x >= y)

      x = 0x10;

else

      y = 0x100;

汇编关键部分:

cmp    si, di

jl     short loc_100A6小于转移至y=0x100

mov   si, 10h

jmp    short loc_100A9

loc_100A6:

mov    di, 100h

loc_100A9:

pop    di

pop    si

pop    bp

retn

3、小于号的使用

C语言部分

if( x < y)

      x = 0x10;

else

      y = 0x100;

汇编关键部分:

cmp    si, di

jge     short loc_100A6大于或等于转移至y=0x100

mov    si, 10h

jmp    short loc_100A9

loc_100A6:

mov    di, 100h

loc_100A9:

pop    di

pop    si

pop    bp

retn

4、小于等于的使用

C语言部分

if( x <= y)

      x = 0x10;

else

      y = 0x100;

汇编关键部分:

cmp    si, di

jg     short loc_100A6大于转移至y=0x100

mov    si, 10h

jmp    short loc_100A9

loc_100A6:

mov    di, 100h

loc_100A9:

pop    di

pop    si

pop    bp

retn

5、等于的使用

C语言部分

if( x == y)

      x = 0x10;

else

      y = 0x100;

汇编关键部分:

cmp    si, di

jnz    short loc_100A6不等于则转移至y=0x100

mov    si, 10h

jmp    short loc_100A9

loc_100A6:

mov    di, 100h

loc_100A9:

pop    di

pop    si

pop    bp

retn

6、不等于的使用

C语言部分

if( x != y)

      x = 0x10;

else

      y = 0x100;

汇编关键部分:

cmp    si, di

jz    short loc_100A6等于则转移至y=0x100

mov    si, 10h

jmp    short loc_100A9

loc_100A6:

mov    di, 100h

loc_100A9:

pop    di

pop    si

pop    bp

retn

小结:综上所述,TC编译器将关系运算符转变为相反的关系运算,保持了源程序的顺序,即相反的比较跳转至else部分,而正常的跳转至if后语句。

C语言逻辑运算符
2009-07-16 17:27

一、逻辑运算符:

1、与运算符的使用

C语言程序:

int x = 1;

int y = 10;

if( x && y)

      x = 0x10;

else

      y = 0x100;

对应的汇编语句:

push   bp

mov   bp, sp

push   si

push   di

mov   si, 1

mov   di, 0Ah

or     si, si           求或运算,若为零则跳转至y=0x100

jz     short loc_100AA

or     di, di          求或运算,若为零则跳转至y=0x100

jz     short loc_100AA

mov   si, 10h

jmp   short loc_100AD

loc_100AA:

mov    di, 100h

loc_100AD:

pop    di

pop    si

pop    bp

retn

注意:与运算时,只要有一个为0则结果就为0,所以判断x是否为0,为0则跳转至y=0x100;若不为零,再判断y是否为0,若为零则亦跳转至y=0x100

2、或运算符的使用

C语言程序:

int x = 1;

int y = 10;

if( x || y)

      x = 0x10;

else

      y = 0x100;

对应的汇编语句:

push   bp

mov   bp, sp

push   si

push   di

mov   si, 1

mov   di, 0Ah

or      si, si

jnz    short loc_100A5  求或运算,若不为零则跳转至x=0x10

or     di, di

jz     short loc_100AA  求或运算,若为零则跳转至y=0x100

mov   si, 10h

jmp   short loc_100AD

loc_100A5:

mov    si, 10h

jmp    short loc_100AD

loc_100AA:

mov    di, 100h

loc_100AD:

pop   di

pop    si

pop    bp

retn

注意:求或运算时,先判断x是否为0,不为0则直接x=0x100;若为0,则再判断y是否为0,为0则直接y=0x100

3、非运算符的使用

C语言程序:

int x = 1;

int y = 10;

if(!y)

      x = 0x10;

else

      y = 0x100;

对应的汇编语句:

push   bp

mov   bp, sp

push   si

push   di

mov   si, 1

mov   di, 0Ah

or    di, di

jnz    short loc_100A6y不为零则执行y=0x100

mov   di, 10h         若为零则执行x=0x10

jmp    short loc_100A9

loc_100A6:

mov    si, 100h

loc_100A9:

pop    di

pop    si

pop    bp

retn

注意:求非运算时,先判断y是否为0,若不为0y后,结果为假,则直接y=0x100,不为0则直接x=0x100

        综上所述:可以看到 TC 编译器,处理逻辑运算符时,先判断左边第一个参数,后判断接下来的参数,所以在使用时应该将最有可能出现的情况放在左边做判断,效率会更高一些。
C语言If语句
2009-07-16 17:29

       If语句

C语言程序:

int x = 1;

int y = 10;

if( x > y)       

      x=0x10;

else

{

      if(x==y)        

             y = 0x100;

      else

             y =0x102;

}

汇编程序:

push   bp

mov   bp, sp

push   si

push   di

mov    di, 1

mov    si, 0Ah

cmp    di, si

jle      short loc_100A6x<=y则跳转至loc_100A6进入else部分

mov    di, 10h         x=0x10

jmp    short loc_100B2

loc_100A6:                           

cmp    di, si

jnz    short loc_100AF

mov    si, 100h

jmp    short loc_100B2

loc_100AF:

mov    si, 102h

loc_100B2:

pop    di

pop    si

pop    bp

retn

C语言Switch语句
2009-07-16 17:30

       Switch语句

C语言程序:

int x = 1;

int y = 10;

switch(x)

{

      case 1:y = 0x100;

      case 2:y = 0x101;

      case 3:y = 0x102;

      default:y = 0x110;

}

汇编程序:

push   bp

mov   bp, sp

push   si

push   di

mov    di, 1

mov    si, 0Ah

mov    ax,1            di(即变量x)赋给ax

cmp    ax,1            case 1x1比较

jz      short loc_100B0  y=0x100

cmp    ax,2           case 2x2比较

jz      short loc_100B3  y=0x101

cmp    ax,3            case 3x3比较

jz      short loc_100B6  y=0x102

jmp    short loc_100B9y=0x110

loc_100B0:mov si,100h

loc_100B3:mov si,101h

loc_100B6:mov si,102h

loc_100B9:

mov   si,110h

pop    di

pop    si

pop    bp

retn

总结:switch语句将会转变为类似多个if语句的结构。以下为上面的switch语句对应的C语言if语句描述,若不加break语句,则每条if语句顺序执行。

If(x==1) y=0x100;

If(x==2) y=0x101;

If(x==3) y=0x102;

Y=0x103;

C语言循环语句
2009-07-24 15:30

C语言的各种循环语句

1、While循环

C语言程序:

int x = 1;

while(x<10)

{

      x++;

}

对应的汇编语句:

push   bp

mov   bp, sp

push   si

mov   si, 1

jmp   short loc_1009C

loc_1009B: inc si

loc_1009C:    

cmp   si,0Ah              判断x是否大于10

jl      short loc_1009B小于则跳至inc six++

pop  si

pop  bp

retn

理解:先判断while语句后的条件,若是真,则执行后续语句,若为假则退出。

2、for循环

C语言程序:

int x = 0;

int i;

for(i=0;i<10;i++)

{

      x++;       

}

对应的汇编语句:

push   bp

mov   bp, sp

push    si

push   di

xor    di,di            设置x=0

xor    si,si             设置i=0

jmp    short loc_1009F

loc_1009D:

inc    di

inc    si

loc_1009F:

cmp   si,0Ah           比较i是否大于10

jl     short loc_1009D

pop  di

pop  si

pop  bp

retn

理解:先判断条件,若条件为真,则执行i++,执行下列语句;若条件为假,则退出循环。

3、do while循环

C语言程序:

int x = 1;

do

{

      x++;

}

while (x<10);

对应的汇编语句:

push   bp

mov   bp, sp

push   si

mov   si,1

loc_10099:

inc    si

cmp   si,0Ah

jl      short loc_10099

pop  si

pop  bp

retn

理解:先执行后续语句,后判断条件。换句话说,后续语句最少可以执行一次。

4、breakcontinue语句

理解: break 语句用于 switch 或循环中,退出循环; continue 语句用于结束此次循环。
C语言数组的定义使用
2009-07-24 15:32

数组的定义使用

C语言程序:

int x[] = {1,2};

int i = x[0]+x[1];

汇编程序代码:

Pushbp

movbp, sp

subsp, 6        用到三个局部变量,其中数组2个,所以分配6个字节的堆栈空间

push      ss

lea  ax, [bp-6]

pushax

push      ds

movax, 194h

push      ax       以上压栈操作给子程序传递参数

movcx, 4       数组占用字节长度

callSCOPY@注意:此处为远调用,压入CSIP寄存器

movax, [bp-6]ax寄存器内容x[1]

addax, [bp-4]   x[0]+x[1]

mov[bp-2], ax    将结果付给[bp-2],即变量i

movsp, bp

popbp

retn                  以上语句为返回子程序

子程序SCOPY@

pushbp

movbp, sp

pushsi

pushdi

pushds              保存子程序中用到的寄存器

lds  si, [bp+6]   ds:si为调用程序中的DSAx的值,初始化字符串复制源地址

les  di, [bp+0Ah]es:di为堆栈空间的地址

cld

shr  cx, 1

rep movsw          将数据段中定义的数组内容复制到堆栈中分配的空间

adccx, cx

rep movsb           此处有疑问,不太清楚。

popds

popdi

popsi

popbp

retf8                  堆栈平衡,清空子程序调用前压入的参数。

总结:C语言处理数组是通过在数据段定义数组内容,并且一般以16字节为单位,若不够则用“0”补齐,然后将DS及数组偏移地址,SS及堆栈开始地址压入堆栈供子程序调用,子程序负责将数据段中定义的数组内容复制到堆栈中。

一定要注意复制数组内容的子程序为远调用,曾经在此处分析时,出现问题。

多维数组其实就是按行存放,先放第一行的元素,再存放第二行的元素。

例:int i[2][2]内存中为:i[0][0]i[0][1]i[1][0]i[1][1]

C语言的函数
2009-07-24 15:34

这个的内容比较多,有点重量级。呵呵。。       

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

movbp, sp    压栈bpbp寄存器将在后续部分作为基址寄存器使用

subsp, 2     在堆栈中分配空间给变量C

push      si

push      di

movsi, 10h   变量a=0x10

movdi, 100h  变量b=0x100

push      di       

push      si       压栈ba做为参数传递给add函数

callsub_100B0

popcx      

popcx      平衡堆栈,pop cx机器指令较短,执行效率高,相当于add sp,4

mov[bp-2], ax将返回值付给变量CC语言中通过ax寄存器返回函数值

popdi

popsi

movsp, bp

popbp

retn          至此函数fun返回

sub_100B0:

push      bp

movbp, sp

push      si       

movsi, [bp+4]c = a

addsi, [bp+6]c = a + b

movax, si    将结果传给ax寄存器

popsi

popbp

retn       add子程序返回

总结:函数进入时基本上都有push bpmov bp,sp语句,此两条语句将bp寄存器压栈,过后做为基址寄存器使用。

调用函数时,先将参数从右到左依次压栈,执行call指令时,将ip压栈,接着将bp压栈,所以在16位的CPU中,取参数时,bp+4取得第一个参数,bp+6取得第二个参数,依次类推。局部变量使用使用sub sp,2指令在堆栈中开辟空间存储,所以取局部变量时,通过bp-2取得第一个局部变量,bp-4取得第二个局部变量,函数若有返回值一般通过ax寄存器返回。最后要执行mov sp,bppop bp指令平衡局部变量占用的堆栈空间,实际的参数并未清空。Ret后弹出ip值,返回调用程序执行的地址。

一般情况下由调用函数的程序平衡堆栈,所以上例pop cx执行两次,相当于add sp,4平衡堆栈。但数组中传送字符串的函数使用了ret n,由子程序本身进行了平衡堆栈的工作。

从上可以看到,在调用函数时,调用参数并未在子函数中更改,即在子函数中使用了参数值,但并未真正更改参数的值。使用指针做为参数时的情况,后续再研究。
在C语言中将数组做为参数传递给函数
2009-08-04 08:42

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

movbp, sp

subsp, 4    ;开辟堆栈空间,保存数组数据

push      ss

lea  ax, [bp-4]

push      ax        ;es:di压栈

movax,0

push      ax

push      ds       ;ds:si压栈

movcx,4      ;设置数组数据所占字节数,即需复制字节数

callsub_100C7

lea  ax, [bp-4]   ;此命令就是将数组的首地址传给调用的函数

push      ax        ;将数组的首地址压栈传递给子程序change

callsub_100C6

popcx       ;平衡堆栈

movsp, bp

popbp

retn

sub_100C6:    ;change函数

push      bp

movbp, sp

push      si

push      di       ;tmp局部变量

movsi, [bp+4]  ;将数组首地址读入si寄存器

movdi, [si]     ;si保存的是一个地址,这个地址中存储就是a[0],即指针tmp = a[0]

movax, [si+2]  ;ax = a[1]

mov[si], ax    ;a[0]=ax a[0]=a[1]通过ax寄存器中转

mov[si+2],di  ;a[1]=tmp

popdi

popsi

popbp

retn

sub_100C7:    ;复制数据段数组数据至开辟的堆栈空间中           

push      bp

movbp, sp

push      si

push      di

push      ds

lds  si, [bp+6]

les  di, [bp+0Ah]

cld

shr  cx, 1

rep movsw

adccx, cx

rep movsb

popds

popdi

popsi

popbp

retf8

总结:传递数组其实就是传递数组的首地址,可以理解为传递的是数组指针,对数组的操作,将会影响数组中的数据。

C语言的全局变量和局部变量
2009-08-04 08:43

C语言源码:

int a = 0x99;

int g = 0x100;

fun()

{

      int a = 0x10;

      g = a;

      fun1();

}

fun1()

{

      int c = 0x11;

      a = c;

      g = a;

}

汇编代码:

push bp

mov bp, sp

push si

mov si, 0010           ;局部变量int a = 0x10;

mov ds:[0002], si       ;对全局变量赋值g = a;

call 0023

pop si

pop bp

ret

push bp              

mov bp, sp

push si

mov si, 0011           ;局部变量int c = 0x11;

mov ds:[0000], si      ;对全局变量赋值a = c;    

mov ax, word ptr ds:[0000]

mov word ptr ds:[0002], ax ;以上两句完成g = a的操作

pop si

pop bp

ret

总结:C语言将局部变量存储于堆栈中,在函数结束时自动清除,全局变量存储于定义的数据段中,在程序中全局变量定义后的函数中均可访问。

若定义了全局变量和局部变量,在子函数中若存在与全局变量同名的局部变量,则使用子函数中的局部变量,而不使用全局变量。

C语言静态、动态存储变量的区别
2009-08-08 01:58

       静态、动态存储变量的区别

C语言源码:

fun()

{

      int i = 0x100;

      static int c = 0x200;

      i = c;

}

汇编代码:

push      bp

movbp, sp

push      si

movsi, 100h      ;定义动态变量i = 0x100

movsi, ds:[0000];对静态变量赋值i = c;ds:[0000]内存储的是静态变量C

popsi

popbp

retn

       总结: 其实静态变量类似于全局变量,实现方式类似。
C语言的指针定义、存储
2009-08-08 02:00

      指针的定义及存储方式

C语言源码:

f()

{

      int i = 0x100;

      int *p;

    p= &i;

}

汇编代码:

enter      4, 0        ;分配四个字节空间存储局部变量

mov[bp-4], 100h ;int i = 0x100

lea  ax, [bp-4]   ;将变量i的地址赋给ax

mov[bp-2], ax   ;int *p=&i

leave

retn

总结:指针即地址,指针变量存储的是i的地址。

C语言源码:

f()

{

      int a = 0x100;

      int b = 0x200;

      int *p,*p1,*p2;

      p1 = &a;

      p2 = &b;

      p = p1;

      p1 = p2;

      p2 = p;

}

汇编代码:

enter      6, 0

push      si

push      di

mov[bp-6], 100h  ;变量a int a = 0x100;

mov[bp-4], 200h;变量b int b = 0x200;

lea  si, [bp-6]    ;变量p1 int *p1 = &a,现si中存储的是变量a的地址

lea  di, [bp-4]    ;变量p2 int *p2 = &b,现di中存储的是变量b的地址

mov[bp-2], si    ;p = p1

movsi, di       ;p1 = p2

movdi, [bp-2]   ;p2 = p1

popdi

popsi

leave

retn

       总结: lea 命令是取地址的操作命令。
C语言指针变量作为函数参数
2009-08-08 02:02

C语言源码:

void swap(int *p1,int *p2);

f()

{

      int a = 0x100;

      int b = 0x200;

      int *p1 = &a;

      int *p2 = &b;

      swap(p1,p2);

}

void swap(int *p1,int *p2)

{

      int p;

      p = *p1;

      *p1 = *p2;

      *p2 = p;

}

汇编代码:

enter      4, 0

push      si

push      di

mov[bp-4], 100h  ;局部变量aa = 0x100

mov[bp-2], 200h  ;局部变量bb = 0x200

lea  si, [bp-4]    ;局部变量p1 p1 = &a

lea  di, [bp-2]    ;局部变量p2 p2 = &b

push      di          ;p2压栈

push      si          ;p1压栈

callsub_100B3  ;调用函数swap

popcx            

popcx         ;堆栈平衡

popdi

popsi

leave

retn

sub_100B3:

push      bp

movbp, sp

push      si         ;变量p

movbx, [bp+4]  ;取得变量p1bx中现存放的是地址

movsi, [bx]     ;取得*p1p = *p1

movbx, [bp+6]  ;取得变量p2bx中现存放的是地址

movax, [bx]     

movbx, [bp+4]

mov[bx], ax    ;以上三句完成*p1 = *p2

movbx, [bp+6]

mov[bx], si     ;以上两句完成*p2 = p

popsi

popbp

retn

总结:函数参数为指针,即将指针即地址压入栈中,传递给函数。从上可以看到,函数指针参数并未发生变化,但指针指向的内存空间发生了变化。

C语言的数组和指针的关系及++操作符的使用
2009-08-08 02:04

C语言代码:

fun()

{

      int i[] = {'a','b'};    

      int *p = i;

      int i1= *++p;         

}

汇编代码:

enter 6,0              ;开辟空间存储数组元素

push si                 

push ss

lea ax,[bp-6]

push ax

push ds

mov ax,0

push ax

mov cx,4

call 000A:0029      ;以上为数组元素复制函数准备参数

lea si,[bp-6]          ;将数组首地址赋给si。即int p* = i

inc si

inc si                    ;以上两句完成++p

mov ax,[si]           

mov [bp-2],ax       ;以上两句完成int i1= *++p;

pop si

leave

ret

C语言代码:

fun()

{

      char c[] = {'a','b'};

      char *p = c;

      char c1= *p++;            

}

汇编代码:

enter 4,0              ;开辟空间存储数组元素

push si                 

push ss

lea ax,[bp-4]

push ax

push ds

mov ax,0

push ax

mov cx,2

call 000A:0029      ;以上为数组元素复制函数准备参数

lea si,[bp-4]          ;将数组首地址赋给si。即char p* = c

mov al,[si]            ;[bp-3]中存放字符串'b'[bp-2]未使用

mov [bp-1],al        ;以上两句完成char c1= *p

inc si                    ;p++

pop si

leave

ret

总结:以上均省略了数组元素复制的代码。通过以上代码可以发现C语言编译器可以根据定义的指针的类型,对++操作进行指定的字节后移,char类型占用一个字节,则p++对应为inc siint类型占用两个字节,则p++对应为inc si两次。

指针多使用lea命令将地址赋值给相应的指针变量。另外可以看到p++++p的差别,p++是先操作后加,++p为先加后操作。

C语言的字符串
2009-08-08 02:05

C语言代码:

fun()

{

      char *c = "I love you!";

      char *c1 = "I love you!";

}

汇编代码:

push      bp

movbp, sp

subsp, 4

mov[bp-4], 0      ;char *c = "I love you!"

mov[bp-2], 0Ch   ;char *c1 = "I love you!"

movsp, bp

popbp

retn

总结:字符指针初始化时,将字符存于数据段中,而只给字符指针一个引用地址。字符数组必须定义为static,其实也是存于数据段中。

C语言的函数指针和指向函数的指针变量
2009-08-08 02:06

C语言代码:

main()

{

      int max();

      int (*p)();

      int a,b,c;

      a = 0x10;

      b = 0x20;

      p = max;

      c = (*p)(a,b);

}

max(int a,int b)

{

      int z;

      if(a > b) z = a;

      else

             z = b;

      return b;

}

汇编代码:

push      bp

movbp, sp

subsp, 4                   ;分配变量cb。其中c-[bp-2]b-[bp-4]

push      si

push      di

movdi, 10h                   ;变量a = 0x10

mov[bp-4], 20h           ;变量b = 0x20

movsi, 21Eh              ;p = max即函数的地址,此处21Eh为函数的地址

push      [bp-4]

push      di

callsi ; sub_1021E      ;压栈ba跳转至si指向的函数的地址

popcx

popcx                      ;堆栈平衡

mov[bp-2], ax             ;c = (*p)(a,b),一般函数通过ax返回值。

popdi

popsi

movsp, bp

popbp

retn

sub_1021E:                  ;max函数开始,地址为21Eh

push      bp

movbp, sp

push      si

push      di

movsi, [bp+6]            ;变量b

movax, [bp+4]            ;变量a

cmpax, si                    ;if语句开始

jle   short loc_10232    ;a <= bz = b,否则z = a

movdi, [bp+4]            ;z = a

jmpshort loc_10234

loc_10232:                          

movdi, si                   ;z = b

loc_10234:                          

movax, si                   ;将返回值赋给ax寄存器

popdi

popsi

popbp

retn

总结:函数的指针其实就是函数的开始地址。注意函数的参数必须一致,否则在调用实际的函数时,就会出现问题。

另外:C语言编译器发现局部变量较少时,使用disi寄存器存储局部变量,这样可以提高程序的运行速度。



版权声明:本文为ljxkey原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。