深入了解C语言

C语言生成的代码在执行效率上比其它高级语言都高.现在让我们来看看C语言生成的代码具体是什么样子的.当你看完本文对于C语言的了解一定会更深一步了.



本文通过一个个实际案例程序来讲解C语言.



研究案例一



工具: Turboc C v2.0,Debug,MASM v5.0,NASM

实例C程序:

/* example1.c */

char ch;

int e_main()

{



e_putchar(ch);



}



目标内容:C语言调用函数的方法与细节



我们使用的C编译器是16位的Turboc C v2.0,它生成的是16位的代码,比较简单,方便我们来研究.同时我们也需要用到DOS下的DEBUG来进行反汇编.由于我们很多案例中的程序并不是完整的C程序,所以Turboc下的Tlink并不能为我们生成目标程序,所以我将使用MASM中的link.exe,同时里面的exe2bin.com也可以为我们把exe文件转换成bin文件.



这个程序没有main函数,我们用e_main来代替main函数.这样我们能避开C语言对main函数进行一系列处理的代码.同样,我们也用e_putchar()来代替我们平常使用的putchar().这里"e"的意思就是"example".



没有了main函数,我们的C程序就没有了入口,所以在开始编译这段C代码之前,我还得写几行简单的汇编代码,通过它来作为我们程序的入口.



; C程序的入口 start.asm

[BITS 16]

[global start]

[extern _e_main]

start:

  call _e_main



按照C语言的习惯,所以C总的名词都要自动在前面加一个"_"下划线.所以,我们在C中的e_main函数,如果要在汇编中调用,就变成了_e_main函数.这段汇编代码只有一句:call _e_main,就是调用我们在C中的e_main函数





这段代码我将用nasm来进行编译.生成start.obj

nasmw -f obj -o start.obj start.asm



下面我们用Turboc C来编译这段C代码:

TCC -mt -oexample1.obj -c example1.c

link start.obj example1.obj,example1.exe,,,

exe2bin example1.exe

这样,我们就得到了这段C代码编译出来的机器代码文件(example1.bin)了.

下面我们用DEBUG这个老DOS的工具来对example1.bin进行反汇编.



DEBUG

-n example1.bin

-l 0

-u 0

xxxx:0000   CALL 0003

xxxx:0003   MOV  AX,000B

xxxx:0006   PUSH AX

xxxx:0007   CALL 0020

xxxx:000A   POP  CX



这里看到蓝色的代码就是我们整个C程序的所生成的代码了.

最开始的第一句CALL 0003是我们用nasm编译的start.asm所生成的代码.

我们主要目标是研究蓝色的C语言的代码,第一句start.asm所生成的代码太简单,就是调用e_main函数.而我们的e_main函数就是蓝色代码部分.



从C源程序中我们看到,我们在e_main做的就是一件事情:调用e_putchar(ch);其中ch是传给出e_putchar的参数.



MOV AX,000B 

000B就是我们的全局变量ch所在内存的地址.C语言会把所有的全局变量在另一块内存区.C代码先把ch的地址传给AX,然后通过

PUSH AX

把AX的值,也就是ch的地址压入堆栈.然后再

CALL 0020

而0020就是e_putchar代码的地址.通过这跳语句,计算机就跳到e_putchar的代码部分去执行了.我在这里并不给出e_putchar的代码,因为我们这个案例只是研究C语言中如何传递参数给其它函数的,并不管e_putchar如何取参数.下在一个案例中,我们将研究函数如何取参数.



在这里我得把CALL指令解释清楚,因为在下个研究函数如何取参数的部分中大家可能会迷惑.CALL XXXX 指令简单地或就是

PUSH IP

JMP XXXX

它首先把当前的执行地址IP压入堆栈,然后跳转到要CALL的地址去.CALL和RET指令是配套的.RET指令等同于

POP IP

也就是回复CALL前的执行地址IP.

正因为这样,所以你一旦使用了CALL指令,你的堆栈指针SP就会自动减2.



POP CX

是每个函数调用完毕后都有的必备操作.在这里它不起任何作用.可能唯一的作用就是与CALL 0020前的PUSH AX像对应.这样堆栈指针SP才能回原.



好了,简单的第一个案例研究结束了.虽然就这4跳指令,但是我们已经可以看出C语言传递参数方法了.总结起来就是

通过"MOV AX,参数地址"把参数的地址传到AX,然后"PUSH AX"把参数的地址压入堆栈.最后"CALL 函数地址"转向执行要调用的函数.最后调用完后,"POP CX",恢复堆栈指针SP.



研究案例二



工具: Turboc C v2.0,Debug,MASM v5.0,NASM,TASM

实例C程序:

/* example1.c */

char ch;

extern void e_putchar(char c);

int e_main()

{



ch=0x44;

e_putchar(ch);



}

实例汇编程序:

; eio.asm

_TEXT segment byte public ’CODE’

DGROUP group _TEXT

  assume cs:_TEXT,ds:DGROUP,ss:DGROUP

  public _k_putchar

_k_putchar proc  near

 push  bp

 mov  bp,sp

 mov  ah,0eh

 mov  bx,7h

 mov  al,byte ptr [bp+4]

 int  10h

 pop  bp

 ret

_k_putchar endp



 



目标内容:C语言中函数使用参数的方法



这一节我们将使用TASM用汇编来写个标准的C函数.这一节的内容大家可能在很多汇编的书籍上都看到过.讲的是C语言和汇编语言的连接方法.可能你会奇怪,我们这里已经有了MASM,NASM两个汇编编译器了,为什么还要使用TASM另外一个汇编编译器.我不知道MASM是否可以和我们的Turboc C配合,但是TASM是肯定可以和Turboc C完全配合的.毕竟它们都是Borland公司的产品,而且Turboc C中用-S生成的汇编代码是完全按照TASM中的语法而定的.这足以见Turboc C和TASM之间"亲密的"关系了.
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值