2
__declspec(naked) void printfive() {
   int i = 5;
   printf("%i\n", i);
}

出于某种原因,这段代码有效,但我不明白i存储在哪里?在调用函数的框架中?它成为全局变量?如果它存储在调用者的框架中,那么编译器如何知道位移,因为您可以printfive()从具有不同框架大小和局部变量的不同函数中调用。如果它是全局的,或者类似的东西static,我已经尝试递归,我可以看到变量没有改变,它确实不是真正的本地。但这很明显,没有入口代码(序言)。好的,我明白了,没有序言,没有框架,没有寄存器更改,但这是值,但是范围会发生什么?此说明符的行为是否在任何参考中定义?这还是 C++ 标准的一部分吗?如果您主要使用这种功能,那就太好了asm {}在它们内部,(或使用 asm 调用它们并希望确保函数没有过度优化),但您可以与 C++ 混合使用。但这是一种脑筋急转弯。

4

3 回答 3

3

我知道这个话题已经有好几年了,这是我自己的答案。

由于没有参考有关此主题的 Microsoft 文档,因此对于那些希望了解更多有关 Keltar 所述需要或不需要的内容的人,这里是 Microsoft 文档,它解释了 Keltar 在此处未解释的大部分内容。

根据 Microsoft 文档,应避免使用。

以下规则和限制适用于裸函数:

  • 不允许返回语句。
  • 不允许结构化异常处理和 C++ 异常处理构造,因为它们必须在堆栈帧中展开。
  • 出于同样的原因,禁止任何形式的 setjmp
  • 禁止使用 _alloca 函数。
  • 为确保局部变量的初始化代码不会出现在序言序列之前,函数范围内不允许初始化局部变量。特别是,
    不允许在函数范围内声明 C++ 对象。但是,嵌套范围内可能存在初始化数据。
  • 不推荐帧指针优化(/Oy 编译器选项),但它会自动为裸函数抑制。
  • 您不能在函数词法范围内声明 C++ 类对象。但是,您可以在嵌套块中声明对象。
于 2019-12-25T18:39:41.920 回答
1

来自 gcc 手册:

使用此属性 ... 表示指定的函数不需要编译器生成的序言/尾声序列。由程序员提供这些序列。唯一可以安全地包含在裸函数中的语句是没有操作数的 asm 语句。应避免所有其他语句,包括局部变量的声明、if 语句等。裸函数应该用于实现汇编函数的主体,同时允许编译器为汇编器构造必要的函数声明。

而且它不是标准的(以及任何 __declspecor __attribute__

于 2013-11-01T10:56:06.387 回答
0

进入或退出函数时,编译器会添加代码以帮助传递或参数。当一个函数被裸声明时,会生成非参数变量赋值代码,如果要获取任何参数,则需要直接访问相关的寄存器或堆栈(取决于 ABI 定义的调用约定)。

在您的情况下,您没有将参数传递给函数,因此即使函数被声明为裸露,您的代码也可以工作。如果您想查看差异,请查看反汇编程序。

于 2013-11-01T10:56:17.367 回答