一,不同关键字,系统压栈方式

1,如果函数func是__cdecl(VC下的默认调用方式),调用时情况如下

int   main()   
{   
//参数从右到左压栈   
push   4   
push   3   
push   2   
push   1   
call   func   
add   esp   0x10   //调用者恢复堆栈指针esp,4个参数的大小是0x10(4x4)   

C调用约定(即用__cdecl关键字说明)按从右至左的顺序压参数入栈,由调用者把参数弹出栈。对于传送参数的内存栈是由调用者来维护的(正因为如此,实现可变参数的函数只能使用该调用约定)。另外,在函数名修饰约定方面也有所不同。   
    
    _cdecl是C和C++程序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。函数采用从右到左的压栈方式。VC将函数编译后会在函数名前面加上下划线前缀。是MFC缺省调用约定 
    
2.如果函数func是__stdcall,调用时情况如下   
int   main()   
{   
//参数从右到左压栈   
push   4   
push   3   
push   2   
push   1   
call   func   
                //恢复堆栈指针由被调用者func负责,方法是"ret   0x10"   
}   
_stdcall是Pascal程序的缺省调用方式,通常用于Win32   Api中,函数采用从右到左的压栈方式,自己在退出时清空堆栈。VC将函数编译后会在函数名前
面加上下划线前缀,在函数名后加上"@"和参数的字节数。   
    
3.如果函数func是__pascal,调用情况如下   
int   main()   
{   
//参数从左到右压栈   
push   1   
push   2   
push   3   
push   4   
call   func   
//恢复堆栈指针由被调用者func负责,方法是"ret   0x10"   
}   
    
3.如果函数func是__fastcall,调用情况如下   
int   main()   
{   
//参数先用ecx,   edx,   eax传递,然后再压栈   
//不进栈   
//(不知为什么,帮助中写的是从左到右传递的,   
//是不是错了,还是BCB6和BCB5的不一样)   
push   4   
mov   ecx 3   
mov   edx 2   
mov   eax 1   
call   func   
//恢复堆栈指针由被调用者func负责,方法是"ret   0x04",   
//因为只进栈一个参数,其余用寄存器传递,所以用ret   0x04恢复   
}   
__fastcall调用约定是“人”如其名,它的主要特点就是快,因为它是通过寄存器来传送参数的(实际上,它用ECX和EDX传送前两个双字(DWORD)或更小的参数,剩下的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈),在函数名修饰约定方面,它和前两者均不同。   
    
__fastcall方式的函数采用寄存器传递参数,VC将函数编译后会在函数名前面加上"@"前缀,在函数名后加上"@"和参数的字节数。一般用BCB自动生成的函数前边都是带__fastcall

4、thiscall仅仅应用于“C++”成员函数。this指针存放于CX寄存器,参数从右到左压。thiscall不是关键词,因此不能被程序员指定。   
    
5、naked   call采用1-4的调用约定时,如果必要的话,进入函数时编译器会产生代码来保存ESI,EDI,EBX,EBP寄存器,退出函数时则产生代码恢复这些寄存器的内容。naked   call不产生这样的代码。naked   call不是类型修饰符,故必须和_declspec共同使用。   
    
          
6、关键字   __stdcall、__cdecl和__fastcall可以直接加在要输出的函数前,也可以在VC编译环境的Setting...\C/C++   \Code   Generation项选择。当加在输出函数前的关键字与编译环境中的选择不同时,直接加在输出函数前的关键字有效。它们对应的命令行参数分别为/Gz、/Gd和/Gr。缺省状态为/Gd,即__cdecl。   
    
7、要完全模仿PASCAL调用约定首先必须使用__stdcall调用约定,至于函数名修饰约定,可以通过其它方法模仿。还有一个值得一提的是WINAPI宏,Windows.h支持该宏,它可以将出函数翻译成适当的调用约定,在WIN32中,它被定义为__stdcall。使用WINAPI宏可以创建自己的APIs。   
    
    
二、名字修饰约定   
    
1、修饰名(Decoration   name)   
    
“C”或者“C++”函数在内部(编译和链接)通过修饰名识别。修饰名是编译器在编译函数定义或者原型时生成的字符串。有些情况下使用函数的修饰名是必要的,如在模块定义文件里头指定输出“C++”重载函数、构造函数、析构函数,又如在汇编代码里调用“C””或“C++”函数等。   
    
修饰名由函数名、类名、调用约定、返回类型、参数等共同决定。   
    
2、名字修饰约定随调用约定和编译种类(C或C++)的不同而变化。函数名修饰约定随编译种类和调用约定的不同而不同,下面分别说明。   
    
1)、C编译时函数名修饰约定规则:   
    
    __stdcall调用约定在输出函数名前加上一个下划线前缀,后面加上一个“@”符号和其参数的字节数,格式为_functionname@number。   
    
    __cdecl调用约定仅在输出函数名前加上一个下划线前缀,格式为_functionname。   
          
    __fastcall调用约定在输出函数名前加上一个“@”符号,后面也是一个“@”符号和其参数的字节数,格式为@functionname@number。   
    
    它们均不改变输出函数名中的字符大小写,这和PASCAL调用约定不同,PASCAL约定输出的函数名无任何修饰且全部大写。   
    
          
    2)、C++编译时函数名修饰约定规则:   
    
__stdcall调用约定:   
        1、以“?”标识函数名的开始,后跟函数名;   
        2、函数名后面以“@@YG”标识参数表的开始,后跟参数表;   
        3、参数表以代号表示:   
              X--void   ,   
              D--char,   
              E--unsigned   char,   
              F--short,   
              H--int,   
              I--unsigned   int,   
              J--long,   
              K--unsigned   long,   
              M--float,   
              N--double,   
              _N--bool,   
              ....   
              PA--表示指针,后面的代号表明指针类型,如果相同类型的指针连续出现,以“0”代替,一个“0”代表一次重复;   
        4、参数表的第一项为该函数的返回值类型,其后依次为参数的数据类型,指针标识在其所指数据类型前;     
        5、参数表后以“@Z”标识整个名字的结束,如果该函数无参数,则以“Z”标识结束。   
    
         其格式为“?functionname@@YG*****@Z”或“?functionname@@YG*XZ”,例如   
                      int   Test1(char   *var1,unsigned   long)-----“?Test1@@YGHPADK@Z”   
                      void   Test2()                                               -----“?Test2@@YGXXZ”   
    
__cdecl调用约定:   
规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的“@@YG”变为“@@YA”。   
    
__fastcall调用约定:   
规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的“@@YG”变为“@@YI”。   
    
VC++对函数的省缺声明是"__cedcl",将只能被C/C++调用.

BCB在输出函数声明时使用4种修饰符号   
//__cdecl   
cb的默认值,它会在输出函数名前加_,并保留此函数名不变,参数按照从右到左的顺序依次传递给栈,也可以写成_cdecl和cdecl形式。   
//__fastcall   
她修饰的函数的参数将尽肯呢感地使用寄存器来处理,其函数名前加@,参数按照从左到右的顺序压栈;   
//__pascal   
它说明的函数名使用Pascal格式的命名约定。这时函数名全部大写。参数按照从左到右的顺序压栈;   
//__stdcall   
使用标准约定的函数名。函数名不会改变。使用__stdcall修饰时。参数按照由右到左的顺序压栈,也可以是_stdcall;

三,FAR关键字
FAR是民国多少年前的东西啦
    
在16位模式下,指针是16位的   
指针的寻址空间只有64k   
如果指定FAR,说明这个指针指向的地址要加上基地址   
就是说用FAR可以指定64k以外的区域

C/C++ 函数压栈方式的更多相关文章

  1. arm汇编进入C函数分析,C函数压栈,出栈,传参,返回值

    环境及代码介绍 环境和源码 由于有时候要透彻的理解C里面的一些细节问题,所有有必要看看汇编,首先这一切的开始就是从汇编代码进入C的main函数过程.这里不使用编译器自动生成的这部分汇编代码,因为编译器 ...

  2. printf函数压栈解惑

    最近看到一些程序员的笔试题目,经常会考到printf函数的参数压栈问题,总体来讲就是参数从右向左依次压栈,再出栈,但是今天看到一个看似很简单的题目,却一直找不到头绪.题目如下: #include &l ...

  3. printf函数压栈(i++/i--,++i/--i) 终极解密

    #include <stdio.h> void main() { ; printf("%d %d %d %d\n", i, --i, i, i--); } 输出是“3 ...

  4. printf()函数压栈a++与++a的输出

    printf()中a++与++a的输出问题 在C语言中有个很常用的函数printf(),使用时从右向左压栈,也就是说在printf("%d %d %d %d\n",a,a++,++ ...

  5. 在实验中观察指针——C++ 函数参数的压栈顺序

    前言 好久没写东西了,突发奇想,写写函数参数的压栈顺序 先看看这个问题 https://q.cnblogs.com/q/137133/ 然后看我简化的代码,猜输出结果是多少? #include< ...

  6. php用压栈的方式,循环遍历无限级别的数组(非递归方法)

    php用压栈的方式,循环遍历无限级别的数组(非递归方法) 好久不写非递归遍历无限级分类...瞎猫碰到死老鼠,发刚才写的1段代码,压栈的方式遍历php无限分类的数组... php压栈的方式遍历无限级别数 ...

  7. c/c++的函数参数压栈顺序

    整理日:2015年3月18日 为了这句话丢了很多次人.无所谓了,反正咱脸皮厚. 总结一下 编译出来的c/c++程序的参数压栈顺序只和编译器相关! 下面列举了一些常见的编译器的调用约定 VC6 调用约定 ...

  8. C语言函数参数压栈顺序为何是从右到左?(从左向右的话,碰到printf的会陷入死循环)

    上学期学习了汇编语言,并在操作系统实验中使用了汇编+C语言混合编程,中间也了解了一些C语言与汇编语言的对应关系. 由于汇编语言是底层的编程语言,各种函数参数都要直接控制栈进行存取,在混合编程中,要用汇 ...

  9. C语言函数入参压栈顺序为什么是从右向左?

    看到有人提问到,在处理printf/cout时,压栈顺序是什么样的?大家都知道是从右往左,也就是说从右往左的计算,但是,这里的计算不等于输出. a++和++a的压栈的区别:在计算时,遇到a++会记录此 ...

随机推荐

  1. pyvcf 模块

    最近一直在处理samtools freebayes gatk 产生的snp数据, 结果文件都是vcf,于是自己就写了相应的类,但是总是不够完善. 海宝推荐这个模块,他都推荐了 我还抱着我那烂代码不放干 ...

  2. Foobar音乐播放器——最佳音乐播放器 - imsoft.cnblogs

    简单皮肤 下载地址:链接: http://pan.baidu.com/s/1kTwn2dh 密码: lf5f

  3. 移动互联网实战--Apple的APNS桩推送服务的实现(2)

    前记: 相信大家在搞IOS推送服务的开发时, 会直接使用javapns api来简单实现, 调试也直连Apple的APNS服务(产品/测试版)来实现. 很少有人会写个APNS的桩服务, 事实也是如此. ...

  4. webStorm快捷键总结

    Ctrl+Shift+a:快速查找使用编辑器所有功能1.左侧栏目录显影:Ctrl+Shift+F122.文件模板配置:File>Settings>Editor>File and Co ...

  5. java正则表达式 非捕获组详解

    这几天看了下正则表达式,对非捕获组(non-capturing)进行下总结.主要总结 1个 + 2组  一共5个.(?:X) (?=X) (?<=X) (?!X) (?<!X) 一.先从( ...

  6. linux权限管理_ACL权限

    一.什么是ACL权限 ACL是Access Control List(访问控制列表)的缩写,主要的目的是在提供传统的owner,group,others的read,write,execute权限之外的 ...

  7. spark优化之优化数据结构

    概序: 要减少内存的消耗,除了使用高效的序列化类库以外,还有一个很重要的事情,就是优化数据结构.从而避免Java语法特性中所导致的额外内存的开销,比如基于指针的Java数据结构,以及包装类型. 有一个 ...

  8. dedecms 忘记后台密码

    找到admin表 dede_admin,把其pwd的值修改为 默认的 字符串:f297a57a5a743894a0e4, 之后,你的密码就被充值为 admin

  9. SWIFT语言的概览

    Swift用来写iOS和OS X程序.(估计也不会支持其它屌丝系统) Swift吸取了C和Objective-C的优点,且更加强大易用. Swift可以使用现有的Cocoa和Cocoa Touch框架 ...

  10. 复利计算器4.0 【java版】

    import java.util.Scanner; public class FuLi { public static void main(String[] args) { ; Scanner sca ...