`
ldb19890624
  • 浏览: 243500 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

函数调用约定与函数名称修饰规则(一)

 
阅读更多

作者:星轨(oRbIt)
E_Mail:inte2000@163.com
转载请注明原作者,否则请勿转载

使用C/C++语言开发软件的程序员经常碰到这样的问题:有时候是程序编译没有问题,但是链接的时候总是报告函数不存在(经典的LNK 2001错误),有时候是程序编译和链接都没有错误,但是只要调用库中的函数就会出现堆栈异常。这些现象通常是出现在C和C++的代码混合使用的情况下或在C++程序中使用第三方的库的情况下(不是用C++语言开发的),其实这都是函数调用约定(Calling Convention)和函数名修饰(Decorated Name)规则惹的祸。函数调用方式决定了函数参数入栈的顺序,是由调用者函数还是被调用函数负责清除栈中的参数等问题,而函数名修饰规则决定了编译器使用何种名字修饰方式来区分不同的函数,如果函数之间的调用约定不匹配或者名字修饰不匹配就会产生以上的问题。本文分别对C和C++这两种编程语言的函数调用约定和函数名修饰规则进行详细的解释,比较了它们的异同之处,并举例说明了以上问题出现的原因。

函数调用约定(Calling Convention)

函数调用约定不仅决定了发生函数调用时函数参数的入栈顺序,还决定了是由调用者函数还是被调用函数负责清除栈中的参数,还原堆栈。函数调用约定有很多方式,除了常见的__cdecl,__fastcall和__stdcall之外,C++的编译器还支持thiscall方式,不少C/C++编译器还支持naked call方式。这么多函数调用约定常常令许多程序员很迷惑,到底它们是怎么回事,都是在什么情况下使用呢?下面就分别介绍这几种函数调用约定。


1.__cdecl

编译器的命令行参数是/Gd。__cdecl方式是C/C++编译器默认的函数调用约定,所有非C++成员函数和那些没有用__stdcall或__fastcall声明的函数都默认是__cdecl方式,它使用C函数调用方式,函数参数按照从右向左的顺序入栈,函数调用者负责清除栈中的参数,由于每次函数调用都要由编译器产生清除(还原)堆栈的代码,所以使用__cdecl方式编译的程序比使用__stdcall方式编译的程序要大很多,但是__cdecl调用方式是由函数调用者负责清除栈中的函数参数,所以这种方式支持可变参数,比如printf和windows的API wsprintf就是__cdecl调用方式。对于C函数,__cdecl方式的名字修饰约定是在函数名称前添加一个下划线;对于C++函数,除非特别使用extern "C",C++函数使用不同的名字修饰方式。


2.__fastcall

编译器的命令行参数是/Gr。__fastcall函数调用约定在可能的情况下使用寄存器传递参数,通常是前两个 DWORD类型的参数或较小的参数使用ECX和EDX寄存器传递,其余参数按照从右向左的顺序入栈,被调用函数在返回之前负责清除栈中的参数。编译器使用两个@修饰函数名字,后跟十进制数表示的函数参数列表大小,例如:@function_name@number。需要注意的是__fastcall函数调用约定在不同的编译器上可能有不同的实现,比如16位的编译器和32位的编译器,另外,在使用内嵌汇编代码时,还要注意不能和编译器使用的寄存器有冲突。


3.__stdcall

编译器的命令行参数是/Gz,__stdcall是Pascal程序的缺省调用方式,大多数Windows的API也是__stdcall调用约定。__stdcall函数调用约定将函数参数从右向左入栈,除非使用指针或引用类型的参数,所有参数采用传值方式传递,由被调用函数负责清除栈中的参数。对于C函数,__stdcall的名称修饰方式是在函数名字前添加下划线,在函数名字后添加@和函数参数的大小,例如:_functionname@number

4.thiscall

thiscall只用在C++成员函数的调用,函数参数按照从右向左的顺序入栈,类实例的this指针通过ECX寄存器传递。需要注意的是thiscall不是C++的关键字,不能使用thiscall声明函数,它只能由编译器使用。

5.naked call

采用前面几种函数调用约定的函数,编译器会在必要的时候自动在函数开始添加保存ESI,EDI,EBX,EBP寄存器的代码,在退出函数时恢复这些寄存器的内容,使用naked call方式声明的函数不会添加这样的代码,这也就是为什么称其为naked的原因吧。naked call不是类型修饰符,故必须和_declspec共同使用。

VC的编译环境默认是使用__cdecl调用约定,也可以在编译环境的Project Setting...菜单-》C/C++ =》Code Generation项选择设置函数调用约定。也可以直接在函数声明前添加关键字__stdcall、__cdecl或__fastcall等单独确定函数的调用方式。在Windows系统上开发软件常用到WINAPI宏,它可以根据编译设置翻译成适当的函数调用约定,在WIN32中,它被定义为__stdcall。

(未完)

分享到:
评论

相关推荐

    函数调用约定与函数名称修饰规则

    函数调用约定与函数名称修饰规则是编程中至关重要的概念,尤其在使用C/C++时。函数调用约定,也称为调用约定或调用约定协议,规定了函数调用时参数如何入栈、谁负责清理栈以及如何传递返回值。函数名修饰规则则涉及...

    函数调用约定与函数名称修饰规则.pdf

    ### 函数调用约定与函数名称修饰规则 #### 调用约定(Calling Convention) 调用约定是指在程序设计语言中为了实现函数调用而建立的一种协议。这种协议规定了该语言的函数中的参数传送方式、参数是否可变以及由谁...

    剖析C++函数调用约定

    C++函数调用约定是编程过程中非常重要的概念,它规定了函数调用时参数传递的顺序、谁负责清理堆栈以及函数名是否需要修饰。在Visual C/C++编译器中,有四种主要的函数调用约定:__cdecl、__stdcall、__fastcall和...

    常用的函数调用约定的比较

    函数调用约定(Calling Convention)在程序设计中扮演着重要的角色,它决定了函数参数如何传递、栈的清理方式以及函数名称如何进行修饰等问题。不同的调用约定对程序性能、可移植性和兼容性等方面都有着直接影响。...

    DLL中调用约定和名称修饰

    ### DLL中调用约定与名称修饰详解 #### 一、调用约定(Calling Convention) 调用约定是在程序设计语言中为了实现函数调用而建立的一种协议。它规定了该语言的函数中的参数传递方式、参数是否可变以及由谁来处理...

    函数调用约定

    在程序设计领域中,函数调用约定(Calling Convention)是定义如何传递参数、清理栈空间以及如何分配寄存器等的一组规则。这些规则对于确保程序正确运行至关重要,尤其是在涉及不同编译器或语言间的交互时。本文将...

    C_C++函数符号生成规则(函数名的修饰);C++ 函数重载.pdf

    在C++语言中,函数符号生成规则主要有四种:cdecl调用约定、std call调用约定、fastcall调用约定和thiscall调用约定。 1. cdecl调用约定:”?+函数名+参数类型代号+函数返回类型代号+参数类型代号+结束标识”...

    函数调用约定.docx

    函数调用约定是编程语言中规定函数调用过程的一个重要机制,主要涉及到参数传递和堆栈清理的责任分配。在C和C++中,不同的调用约定适用于不同的场景,特别是涉及到DLL(动态链接库)和Win API函数时,选择正确的调用...

    关于C和C++中函数的调用约定.doc

    在C和C++编程中,函数的调用约定(Calling ...总之,理解并正确使用函数调用约定和函数名修饰规则是避免C和C++程序中常见错误的关键。正确应用这些规则可以保证程序的正确链接和执行,提高代码的兼容性和可维护性。

    DLL创建与函数调用规则

    总的来说,DLL的创建与函数调用规则涉及MFC的使用、DLL类型的选择以及入口函数的编写等多个方面,理解这些规则是开发高效、稳定的Windows应用程序的关键。开发者需要根据具体需求选择合适的DLL类型,并遵循相应的...

    c/c++中函数调用方式

    在C/C++编程语言中,函数调用方式是程序设计中的关键概念之一,它涉及到如何在函数调用过程中处理参数的传递以及栈空间的管理。根据给定的文件信息,我们可以深入探讨C/C++中几种主要的函数调用方式:__cdecl、__...

    __stdcall调用约定、C调用约定和__fastcall调用约定

    调用约定是编程语言中一个重要的概念,它决定了函数调用时参数如何传递以及栈如何管理。__stdcall、__cdecl和__fastcall各有优势和应用场景,选择合适的调用约定对于优化代码性能和提高程序的可维护性至关重要。了解...

    函数的几种调用机制

    与stdcall不同的是,函数调用结束后,由调用者负责清理堆栈,这使得C调用约定可以支持参数个数不固定的情况。这也反映了C语言的灵活特性。 3. fastcall调用约定:此调用约定旨在提高参数传递的效率。它将部分参数...

    cdecl函数调用,了解printf这样的函数调用,对比stdcall会更清楚.zip

    4. 函数名修饰:`stdcall`函数也会进行名称修饰,但与`cdecl`不同,它的修饰规则是为了适应Windows API,一般不会包含参数类型信息,而是以`@`符号后跟参数总字节数表示。 在C#中,虽然没有直接使用`cdecl`或`...

    C语言函数调用规定.pdf

    调用约定定义了参数压栈的方向、谁负责清理栈以及函数名称的修饰规则等。 常见的调用约定有以下几种: 1. **stdcall调用约定**: - 在stdcall约定中,参数从右到左压栈,即最右边的参数最先被压入栈。 - 函数...

    c++调用约定 c++的集中约定分析

    在C++编程中,函数调用约定是一种规范化的机制,用于规定如何传递参数、如何清理栈空间以及如何确定函数名称等。这些约定对程序员来说至关重要,尤其是在需要跨平台或者与非C++代码进行交互的情况下。本文将深入解析...

    C语言函数调用规定[文].pdf

    理解函数调用约定对于编写跨平台或与特定编译器交互的代码至关重要。不同的编译器可能有不同的默认调用约定,或者提供自定义约定的选项。例如,fastcall调用约定通常用于优化性能,通过寄存器传递部分参数,减少堆栈...

    Windows调用约定.pdf

    Windows调用约定是指Windows平台下函数调用时的规则和约定,它是编译器在生成函数调用代码时遵循的一套机制,以确保调用方与被调用方之间能够正确地传递参数和处理函数返回值。在C++和C语言中,这些调用约定对程序的...

Global site tag (gtag.js) - Google Analytics