38

是的,我知道“cdecl”是一个显着的调用约定的名称,所以请不要向我解释调用约定。我要问的是缩写(?)“cdecl”实际上代表什么。我认为这是一个糟糕的命名选择,因为乍一看它让人想起“C​​ 声明符”(C 的一个相当独特的句法方面)。事实上,有一个名为cdecl的程序,其唯一目的是破译 C 声明符。但据我所知,C 声明符语法与调用约定完全无关。

简化版:“stdcall”代表“标准调用约定”。“cdecl”代表什么?

4

8 回答 8

32

它来自已声明的 C 函数(与未声明的 C 函数相反,这在 K&R C 中很常见)。

当时它与 pascal 调用约定(被调用者清除堆栈的地方)共存,因此在编程语言之后调用它是有道理的。

你可能想知道的关于调用约定的一切。

于 2011-02-13T21:13:52.593 回答
17

你读得太多了。它代表一般调用 C 函数的实现的调用约定(但对于可变参数尤其重要)。

它不一定是“C”和“声明”组合的缩写;名称只是名称,尤其是在编程中。助记符有帮助,但即使“malloc”的意思是“分配内存”,它还有我们知道并附加到它的附加含义;例如,“alloca”也“分配内存”。

或者以“结构”,它“意味着”一个“结构”,但是“结构”本身是如此通用,如果没有我们潜意识地赋予“结构”的含义,我们将无可救药地迷失方向——因为仍在学习术语的新程序员经常会迷失方向.

于 2011-02-13T22:23:08.997 回答
14

C 声明。由/为 C 引入的声明。

[编辑]

老实说,我不得不承认我实际上并不知道这是否代表什么,尽管它实际上是由/为 C 引入的。但是由于caller必须清理分配的内存(与大多数其他调用约定相反)。它也可能是“来电者结束清洁”的助记符,我认为这实际上是一个很好的记忆辅助工具。:D

于 2011-02-13T21:03:47.980 回答
3

cdeclC 声明)调用约定通常是 x86 C 编译器的默认调用约定

在计算机科学中,调用约定是一种实现级(低级)方案,用于子例程如何从调用者接收参数以及如何返回结果。各种实现中的差异包括参数、返回值、返回地址和作用域链接的放置位置(寄存器、堆栈或内存等),以及如何在调用者和调用者之间划分准备函数调用和之后恢复环境的任务。被调用者。

(来源)

调用约定可能与特定编程语言的评估策略有关,但通常不被视为它的一部分(反之亦然),因为评估策略通常在更高的抽象级别上定义并被视为语言的一部分而不是作为特定语言编译器的低级实现细节。

(来源)

cdecl代表 C 声明)是一种调用约定,它起源于 Microsoft 的 C 编程语言编译器,并被许多用于 x86 架构的 C 编译器使用。在cdecl中,子例程参数在堆栈上传递。整数值和内存地址在EAX寄存器中返回,浮点值在ST0x87 寄存器中。寄存器EAXECXEDX是调用者保存的,其余的是被调用者保存的。x87 浮点寄存器在调用新函数时必须为空(弹出或释放),并且ST0在退出函数时必须为空。不用于返回值时也必须为空。ST7ST1ST7ST0

在C 编程语言的上下文中,函数参数按从右到左的顺序被压入堆栈,即最后一个参数首先被压入。

考虑以下 C 源代码片段:

int callee(int, int, int);

int caller(void)
{
    return callee(1, 2, 3) + 5;
}

在 x86 上,它可能会生成以下汇编代码(Intel 语法):

caller:
    ; make new call frame
    ; (some compilers may produce an 'enter' instruction instead)
    push    ebp       ; save old call frame
    mov     ebp, esp  ; initialize new call frame
    ; push call arguments, in reverse
    ; (some compilers may subtract the required space from the stack pointer,
    ; then write each argument directly, see below.
    ; The 'enter' instruction can also do something similar)
    ; sub esp, 12      : 'enter' instruction could do this for us
    ; mov [ebp-4], 3   : or mov [esp+8], 3
    ; mov [ebp-8], 2   : or mov [esp+4], 2
    ; mov [ebp-12], 1  : or mov [esp], 1
    push    3
    push    2
    push    1
    call    callee    ; call subroutine 'callee'
    add     esp, 12   ; remove call arguments from frame
    add     eax, 5    ; modify subroutine result
                      ; (eax is the return value of our callee,
                      ; so we don't have to move it into a local variable)
    ; restore old call frame
    ; (some compilers may produce a 'leave' instruction instead)
    mov     esp, ebp  ; most calling conventions dictate ebp be callee-saved,
                      ; i.e. it's preserved after calling the callee.
                      ; it therefore still points to the start of our stack frame.
                      ; we do need to make sure
                      ; callee doesn't modify (or restores) ebp, though,
                      ; so we need to make sure
                      ; it uses a calling convention which does this
    pop     ebp       ; restore old call frame
    ret               ; return

调用者在函数调用返回后清理堆栈。

调用约定通常是 x86 C 编译器的cdecl默认调用约定,尽管许多编译器提供了自动更改使用的调用约定的选项。要手动将函数定义为cdecl,有些支持以下语法:

return_type __cdecl func_name();

调用约定是调用约定的名称。__cdecl, __stdcall,__pascal__fastcall可以在支持这些约定的编译器的 C++ 函数声明中显式指定。__cdecl是应用程序和静态库的默认值。__stdcall是系统调用(包括 Windows API 调用)的默认值,建议用于 32 位 Windows 中的库 DLL。__thiscall在 Microsoft 编译器中默认使用 16 位和 32 位模式下的成员函数。Microsoft、Borland、Watcom 和 Gnu 是编译器品牌。适用于 Windows 的英特尔编译器与 Microsoft 兼容。适用于 Linux 的英特尔编译器与 Gnu 兼容。Symantec、Digital Mars 和 Codeplay 编译器与 Microsoft 兼容。在 64 位模式下,每个操作系统都有一个默认调用约定,而其他调用约定在 64 位模式下很少见。

其他约定:

  • __帕斯卡
  • __fortran
  • __thiscall
  • __stdcall
  • __fastcall
  • __msfastcall
  • __regcall
  • __vectorcall

(来源)

于 2021-11-03T14:56:31.310 回答
2

CDECL 一词源于 Microsoft 的 BASIC 及其混合语言编程生态系统。该生态系统允许 Microsoft 的四种主要语言(BASIC、FORTRAN、Pascal 和 C)中的任何一种调用任何其他语言。每种语言都有稍微不同的调用约定,并且每种语言都有一种方法来声明外部函数或过程以使用特定的约定。

在 BASIC 中,DECLARE必须先使用该语句,然后才能使用该语句调用外部函数CALL。要表示外部 FORTRAN 或 Pascal 过程或函数,您可以编写以下之一

DECLARE SUB Foo ()
DECLARE FUNCTION Foo ()

C 调用约定与其他语言不同,部分原因是参数以相反的顺序压入堆栈。您可以通过添加 CDECL 修饰符来告知 BASIC:

DECLARE SUB Foo CDECL ()
DECLARE FUNCTION Foo CDECL ()

相反,当用 FORTRAN 或 Pascal 编写时,修饰符是 [C]。这表明 CDECL 特定于 BASIC 的 DECLARE 语句,而不是先前建立的术语。当时,“C 调用约定”没有特定的术语。只有随着 WIN32 中新的调用约定(stdcall、fastcall 等)的出现,“cdecl”才被采用并成为事实上的名称,以在没有其他术语的情况下指代遗留约定。

总之,CDECL 的意思是“C 声明”。它起源于 BASIC 编译器,而不是 C 编译器,它是一个任意选择的 BASIC 关键字,并且有些多余,因为普通的“C”不能是关键字。

关于 CDECL 的详细信息可以在这个 1987 年的文档中找到:

https://archive.org/details/Microsoft_Macro_Assembler_5.1_Mixed_Language_Programming_Guide/page/n1/mode/2up?q=cdecl

于 2021-11-04T00:02:24.150 回答
1

C 有函数和变量的概念,汇编程序/机器代码没有。当想要将值传递给函数时,这需要使用 cpu 寄存器或内存中与寄存器(通常是指定的堆栈指针)的固定偏移量处的值来完成。因此,当我们在函数开始处跳转到新地址时,正确的值位于正确的位置,以便函数正确执行。同样适用于返回值。

这在实践中如何工作在描述系统调用约定的文档中定义。调用约定是 CPU 体系结构和操作系统独有的。

在 x86 Windows 上,使用了许多调用约定。在“cdecl”调用约定中,调用者将函数放在堆栈上供被调用者使用。函数完成后,调用者会清理自己的堆栈。

Windows API 使用类似于 cdelc 的“stdcall”调用约定,只是被调用者清理堆栈。(这意味着 vararg 函数调用 call 不能使用)。它具有节省代码空间的好处。

Windows 也有一个“fastcall”调用约定,它使用寄存器进行参数传递。

__cdecl由于 windows 允许多种调用约定,因此 C 编译器 (cl) 使用,__stdcall和扩展了 C 语言__fastcall。这装饰了函数声明并允许程序员指定使用的调用约定。

其他平台,如 ARM、Itanium 和其他平台有自己的调用约定。

于 2021-10-28T15:15:42.923 回答
0

更新后,在评论指出我错了之后,我已经完全修改了这一点。cdecl表示此函数使用与函数相同的调用约定Cextern "C"此外,意味着函数名称不应进行C++名称修改。

至于为什么叫它cdecl,我已经不知道了。

于 2011-02-13T21:08:33.650 回答
0

CDECL 没有缩写,它只是调用约定的名称。

如果这不是您想要的,而是 CDECL 的历史,那么:

这是 Microsoft 特定的调用约定属性(在 1985 年到 1995 年之间引入时),后来成为标准。

太糟糕的页面从 Internet 上丢失了(wayback 也没有任何内容),但是拥有旧 MSDN CD 的人可能会在“C 语言参考”中找到主题“声明摘要”,其中清楚地说明了以下内容:

属性序列 : /* 属性序列是 Microsoft 特定的 */

属性 属性序列 opt 属性:/* Microsoft 特定 */ 之一

__asm __fastcall __based __inline __cdecl __stdcall

也在同一个旧的 MSDN 文档中 __cdecl

__cdecl 主页 | 概述 | 我如何

微软特定 —>

这是 C 和 C++ 程序的默认调用约定。因为堆栈是由调用者清理的,所以它可以做可变参数函数。__cdecl 调用约定比 __stdcall 创建更大的可执行文件,因为它要求每个函数调用都包含堆栈清理代码。以下列表显示了此调用约定的实现。

否则,我们都会迷失在你的问题中(请修改它);)

于 2021-11-03T21:29:04.233 回答