签到

06月21日
尚未签到

共有回帖数 0

    孤单的狼

    等级:
    近日对C反汇编代码进行了小小的研究,就此写下心得体会,与大家分享
    (以十六位为准,32位类似)

    1.C变量问题
    C中的变量按各分类可以分为好几种,但是在底层的实现却只能分为3种.
    .内存中变量 -- 一般来说,全局变量和static关键字声出来的变量都有专属的内存地址
    .堆栈中变量 -- 实际上它也存在于内存,但是它是没有固定的地址,(使用BP寻址),所有的临时变量(局部变量)都生长于堆栈
    .寄存器中变量 -- 使用register关键字生成的临时变量有可能生长在寄存器中,与机器字长相同的临时变量通常都有一个存在于寄存器,C中默认为一个,但有可能是两个,不可能再多了,C中通常使用SI(ESI)作为寄存器变量存放位置

    .补充的问题:堆栈临时变量中,C并没有使用随时变动的SP作为其寻址的指针,而是使用BP,在切入执行申请临时变量时,C总是把SP的值传结BP然后给SP加上所有临时变量的长度,这正是标准C(不是C99)规定所有变量声明必须在函数的前面,这有利于编译器统计所有变量的长度,并开辟变量堆栈空间,用堆栈来实现临时变量,纠正了我们两个传统想法
    ..临时变量占用可执行文件的空间
    ..在C99中推迟变量的声明可以节省空间
    ...我想说明的是,很多书上在对比以下两种代码:

    int i;
    ...语句若干...
    for(i = 0; i  .....){}



    for(int i = 0; i ....) {}

    时,也就是说推迟变量的声明,可以节省空间,还提到诸如,如果在...语句若干...部分函数就已返回,那么i的空间就浪费了,这完全是错误的,首先,临时变量当函数完成后,空间会被归还,再次,编译器并不会推迟变量的声明,而这种写法的好处只是使我们更容易使用接受
    ..返回临时变量的地址从来不会得到正确的结果,临时变量的地址是以堆栈段ss来寻址的,返回地址后,默认是用DS寻址,结果可想而知

    2.值传递,递归问题
    堆栈,是C实现的根本,临时变量使用了堆栈,函数调用中参数的传递也是使用了堆栈,实际上参数的按值传递的变量与临时变量的性质是一样的,函数执行时,调用方把各个值压入堆栈中,然后使用汇编中的call调用函数,call会将IP也入栈,而传递来的参数与临时变量的区别在于,函数执行后总会将sp传给bp,并使用bp寻址,传递来的参数变量在SP的上方(+寻址),而执行后开辟的临时变量在SP的下方(-寻址)
    递归实际上强烈依靠堆栈,在16位环境中,一个堆栈的最大长度是64K,也就是说如果每一次递归使用2个字节的堆栈,那么递归的深度是32K,(一个没有参数没有临时变量的近调用(near))就是这个值,所谓近调用(16位环境),就是函数调用时只将IP入栈(IP=2bytes),那么你大约可以计算一下你设计的递归函数可以运行多少层,16位环境中这个数值是少得可怜的,减少函数的参数和内部的临时变量可以增加其层数
    实际的问题是,递归并不是强烈推崇的算法,如果递归深度过大,而可执行文件堆栈长度有限的话,往往会有问题发生

    3.循环问题
    本来以为在循环的反汇编码中可以看到loop的身影,但事实上并非如此,C为了对付千变万化的循环结构变化,全部使用jmp语句来代替,而更令人惊奇的是,for(;;)与 while()没有本质的区别,纠正一个错误的观点: for(;;)比while(1)来得快.很多人认为while(1)每次运行后都会把1与0作对比,以判断是否到循环的尽头,但事实上,编译器总会把这个语句优化掉.
    一个典形的for循环包含4个部分:初始化部分,语句部分,增量部分,判断部分,而事实上代码也是按这个顺序排列的,while包含更少.
    4.与汇编比较,效率问题
    与汇编的比较,C的效率是可以值到95%以上的,亏损的效率主要在于C变量方面频繁使用内存,而且所有语句结构都会被规格化,这无疑多少会给它的效率带来影响,举个数组复制的例子来说:
    char a[4000],b[4000];
    for(int i = 0; i  4000; i++) a = b;

    这是个典形表达方式,但是底层处理后,汇编码变得累赘不堪,因为它被格式化了,
    而汇编码的写法是:
    char a[4000], b[4000];
    asm {
    mov ax, ds
    mov ax, es
    mov si, b
    mov di, a
    mov cx, 2000
    rep movsw
    }

    5.寄存器问题
    C的中寄存器被使用得非常规炬,这一点感觉很好,AX通常用作数学运算和返回值,而其它的通用寄存器却不见得有什么独到的用法,SI和DI更是少用(除了被用来作寄存器变量之外),C更倾向于使用内存,这也正是其效率比汇编要低的一个原因

    7.一些发现的有趣的东西
    判断ax是否为0 -- or ax,ax
    判断ax是否为1 -- cmp ax,1
    这两句的区别是机器码上后者比前者多了一个字节,而后者作了一次减法,速度可能比前者慢,我是说可能
    把ax置0 -- xor ax,ax
    把ax置1 -- mov ax,1
    这个汇编程序员都应该知道如何使用了,后者比前者机器码多了一个字节




    楼主 2016-03-09 13:24 回复

共有回帖数 0
  • 回 帖
  • 表情 图片 视频
  • 发表

登录直线网账号

Copyright © 2010~2015 直线网 版权所有,All Rights Reserved.沪ICP备10039589号 意见反馈 | 关于直线 | 版权声明 | 会员须知