C++ 裸函数调用

2025 年 5 月 5 日 | 阅读 6 分钟

大家好!今天我们将学习 C++ 中的裸函数调用。我们可能会有疑问,为什么 C++ 中的函数被称为裸函数。在了解它之前,我们应该先了解什么是函数调用?

C++ 中的函数调用

在 C++ 程序中,激活函数并允许它在我们要求它执行的地方运行的过程称为 C++ 中的函数调用。

C++ 中有两种类型的函数调用。它们是

  • 带参数的函数调用

在此函数调用中,我们分配或传递任何参数给 C++ 程序。这些参数在程序运行中起着至关重要的作用。

  • 不带参数的函数调用

在此函数调用中,我们分配或传递任何参数给 C++ 程序。这些参数在程序运行中起着至关重要的作用。

示例

文件名:withParameters1. cpp

编码

输入

输出

3628800 is the factorial of 10

示例

文件名:withoutParameters1. cpp

编码

输入

输出

2004310016 is the factorial of 15

裸函数调用

当函数使用 naked 属性声明时,不会生成 prolog 或 epilog 代码,从而允许您使用内联汇编器创建自己的独特 prolog/epilog 序列。

裸函数是一项高级功能。它们为您提供了声明一个从 C 或 C++ 以外的上下文调用的函数的选项,允许您对参数位置或寄存器保存做出不同的假设。

中断处理程序等例程就是示例。虚拟设备驱动程序 (VxD) 的创建者会发现此功能特别方便或易于使用。

naked 属性用于指定函数,当为此类函数生成代码时,不包括 prolog 和 epilog。

使用内联汇编器代码,您可以利用此功能创建自己的 prolog/epilog 代码序列。编写虚拟设备驱动程序很好地利用了裸函数。请记住,x64 平台不支持 naked 属性;它仅在 x86 和 ARM 上有效。

语法

裸函数必须使用扩展属性语法和 __declspec 关键字,因为 naked 属性仅影响函数的声明,而不是类型修饰符。

即使函数带有 __force inline 关键字和 naked 属性,编译器也无法为该函数创建内联函数。

如果 naked 属性用于非成员方法定义以外的任何内容,编译器会抛出错误。

示例

只有编译器生成的 prolog 和 epilog 序列的性质受到 naked 属性的影响。

它不影响为调用这些函数而创建的代码。因此,函数指针不能包含 naked 属性,因为它不被视为函数类型的一部分。此外,数据定义不能使用 naked 属性。

规则和限制

以下是 C++ 中裸函数调用的规则和限制

  • 在此类函数调用中不能使用 return 关键字
  • 在此类函数调用中不能使用 _alloca 函数
  • 不允许在函数范围内初始化局部变量,以确保在 prolog 序列之前不会进入局部变量的任何初始化代码。特别是,函数范围不允许定义 C++ 对象。但是,嵌套范围可以包含已初始化数据。
  • C++ 中的裸函数调用必须在堆栈帧上展开。因此,不允许使用 C++ 异常处理构造和结构化异常处理。
  • C++ 中的裸函数调用必须在堆栈帧上展开。因此,setjmp 不能用于 C++ 中的裸函数调用。
  • 每次在 C/C++ 代码中引用 __fastcall 裸函数的寄存器参数之一时,prolog 代码都应该将该寄存器的值保存到该变量的堆栈位置。
  • 如果裸函数调用在词法范围内,则不能声明 C++ 类对象。但您仍然可以在嵌套块中定义对象。
  • 虽然不建议,但帧指针优化(/Oy 编译器选项)会自动为裸函数禁用。
  • 当使用 /clr 构建裸函数调用时,可以忽略 naked 关键字。

示例

编写 prolog/epilog 代码时要记住的想法

在创建自己的 prolog 和 epilog 代码序列之前,了解堆栈帧的结构至关重要。了解如何使用 __LOCAL SIZE 符号也很有帮助。

堆栈帧布局

此插图显示了可在 32 位函数中使用的典型 prolog 代码

示例

“registers”变量是应保存到堆栈的寄存器列表的占位符,“localbytes”变量指示局部变量在堆栈上所需的字节数。在推送寄存器之后,您可以将任何其他相关数据放到堆栈上。相关的 epilog 代码如下

Epilog 代码

堆栈总是变小(从高内存地址到低内存地址)。推送的 ebp 值是基指针 (ebp) 指向的位置。在 ebp-4 处,驻留区开始。通过从 ebp 中减去正确的量来计算 ebp 的偏移量,以便访问局部变量。

局部大小

为了在函数 prolog 代码的内联汇编器块中使用,编译器提供了一个名为 __LOCAL SIZE 的符号。在自定义 prolog 代码中,此符号用于在堆栈帧上为局部变量分配空间。

__LOCAL SIZE 的值由编译器设置。它的值由所有用户定义的局部变量和编译器生成的临时变量的总和组成。__LOCAL SIZE 只能作为瞬时操作数,不能在表达式中使用。您不允许更改或重新解释此符号的含义。例如

__LOCAL SIZE 符号在采用独特 prolog 和 epilog 序列的裸函数的 prolog 序列中按如下方式使用

示例

这就是 C++ 语言中裸函数调用的全部内容。