• 汇编的角度看C语言数据类型与调用约定


    一、关于裸函数

    1、什么是裸函数?

      void __declspec(naked) Function()
      {...}

      上面的函数调用时,为什么会出错?

      void __declspec(naked) Function()
      {

        __asm ret
      }

    2、无参数无返回值的函数框架


      void __declspec(naked) Function()
      {
        __asm
      {
        push ebp
        mov ebp,esp
        sub esp,0x40
        push ebx
        push esi
        push edi
        lea edi,dword ptr ds:[ebp-0x40]
        mov eax,0xCCCCCCCC
        mov ecx,0x10
        rep stosd

        pop edi
        pop esi
        pop ebx
        mov esp,ebp
        pop ebp

        ret
      }
      }


    3、有参数有返回值的函数框架

      int __declspec(naked) Function(int x,int y)
      {
        __asm
        {
          push ebp
          mov ebp,esp
          sub esp,0x40
          push ebx
          push esi
          push edi
          lea edi,dword ptr ds:[ebp-0x40]
          mov eax,0xCCCCCCCC
          mov ecx,0x10
          rep stosd

          mov eax,dword ptr ds:[ebp+8]
          add eax,dword ptr ds:[ebp+0xC]

          pop edi
          pop esi
          pop ebx
          mov esp,ebp
          pop ebp

          ret
        }
      }


    4、带局部变量的函数框架


      int __declspec(naked) Function(int x,int y)
      {
        __asm
        {
          push ebp
          mov ebp,esp
          sub esp,0x40
          push ebx
          push esi
          push edi
          lea edi,dword ptr ds:[ebp-0x40]
          mov eax,0xCCCCCCCC
          mov ecx,0x10
          rep stosd
          mov dword ptr ds:[ebp-4],2
          mov dword ptr ds:[ebp-8],3

          mov eax,dword ptr ds:[ebp+8]
          add eax,dword ptr ds:[ebp+0xC]

          pop edi
          pop esi
          pop ebx
          mov esp,ebp
          pop ebp

          ret
        }
      }


    5、常见的几种调用约定:

      调用约定           参数压栈顺序                平衡堆栈

      __cdecl               从右至左入栈                 调用者清理栈 (外平栈)
      
        __stdcall                         从右至左入栈                 自身清理堆栈

        __fastcall           EDX/ECX传送前两个                       自身清理堆栈
                        剩下:从右至左入栈       


    1int __cdecl Plus(int a, int b)    
    {    
        return a+b;    
    }    
    
        push 2    
        push 1    
        call @ILT+15(Plus) (00401014)    
        add esp,8    


    2int __stdcall Plus(int a, int b)    
        {    
            return a+b;   //函数里边平衡堆栈,内平栈   
        }        
    
        push 2    
        push 1    
        call @ILT+10(Plus) (0040100f)   
      函数内部: 
      ret 8 
    3int __fastcall Plus(int a, int b)    
     {    
        return a+b;    
     }    
    
    mov edx,2    
    mov ecx,1    
    call @ILT+0(Plus) (00401005)    

    函数内部

    ret (放到寄存器中的,不需要平衡堆栈)
    fastcall:如果是传入参数大于两个,没有意义,因为前两个寄存器EDX,ECX来传参数,
          后边的依旧用push的方式传入参数,没有意义
    4int __fastcall Plus4(int a, int b,int c,int d) {   return a+b+c+d; } push 4 push 3 mov edx,2 mov ecx,1 call @ILT+5(Plus) (0040100a)
    函数内部:
    ret 8

    如何判断函数传入几个参数:

      push 了几个不准确

      call完了之后

      add esp,XX也不准确

      最好的方法就是,跟进去看看,用了哪个寄存器,用了哪个参数,然后结合堆栈平衡代码,结合参数调用处的代码。

    二、C语言的数据类型


    1、C语言中的数据类型:


      a.基本类型:整数类型,浮点类型

      b.构造类型:数组类型、结构体类型、共用体(联合)类型

      c.指针类型

      d.空类型(void)
      
    2、学习数据类型的三个要素:

      1、存储数据的宽度

      2、存储数据的格式

      3、作用范围(作用域)


    3、整数类型:char short int long

      





    4、浮点类型:float double

      float和double在存储方式上都是遵从IEEE的规范的

      float的存储方式如下图所示:

      

           符号位                                指数部分 (指数-1)                                                                                    尾数部分 (补0)

          

      double的存储方式如下图所示:
      

      符号位            指数部分                     尾数部分

      将一个float型转化为内存存储格式的步骤为:

      1、先将这个实数的绝对值化为二进制格式

      2、将这个二进制格式实数的小数点左移或右移n位,直到小数点移动到第一个有效数字的右边。

      3、从小数点右边第一位开始数出二十三位数字放入第22到第0位。

      4、如果实数是正的,则在第31位放入“0”,否则放入“1”。

      5、如果n 是左移得到的,说明指数是正的,第30位放入“1”。如果n是右移得到的或n=0,则第30位放入“0”。

      6、如果n是左移得到的,则将n减去1后化为二进制,并在左边加“0”补足七位,放入第29到第23位。
      如果n是右移得到的或n=0,则将n化为二进制后在左边加“0”补足七位,再各位求反,再放入第29到第23位。

      举例说明:

      8.25转成浮点存储

      整数部分8转成2进制

      8/2 = 4 0
      4/2 = 2 0
      2/2 = 1 0
      1/2 = 0 1
      3 - 1 = 2 10
      小数部分0.25转成二进制:
      Float = 6
      0.25*2 = 0.5 0
      0.5*2 = 1.0 1 1 8 23
      0 10000010 00001000000000000000000
      8.25用二进制表示可表示为1000.01

      1000.01 = 1.00001 *2的3次方 小数点向左移动3位 指数为3


      1 10000010 00001000000000000000000 0100 0001 0000 0100 0000 0000 0000 0000 = 41040000


      -8.25转成浮点数是多少呢? 1100 0001 0000 0100 0000 0000 0000 0000 = C1040000
      C1040000

      0.25转成浮点存储

      整数部分0 0

      0.25 * 2 = 0.5 0
      0.5*2 = 1.0 1

      0.01 = 1.0 * 2的2次方 右移动 注意:向右移动2个位 指数为-2


      0 01111101 00000000000000000000000



    5、英文字符存储

      char x = 'A';

      char y = 65;
      A 10101
      参见ASCII编码 B 11111
      C 11110
      , 101011

    6、中文字符存储


    char* x = "啊";

    char* y = "北";

    参见GB2312-80编码

    这种编码存储在的问题

  • 相关阅读:
    泉音作伴到云乡
    给力品牌营销,打造自己的知名度品牌
    【linux】如何利用mkfifo命令让程序产生的文件直接生成压缩文件
    【linux/perl】终端运行的程序怎么屏蔽错误信息的输出?
    四个俄罗斯人算法(Method of Four Russians)
    perl 释放内存问题【转】
    【perl】perl实现case条件判断的语句
    【linux】/dev/null与/dev/zero详解
    【perl】打开多个文件——文件句柄的使用
    EM算法——最大期望算法(Expectationmaximization algorithm)
  • 原文地址:https://www.cnblogs.com/heyhx/p/14193770.html
Copyright © 2020-2023  润新知