引用如何出现在可从 C 代码调用的函数的签名中?
How can a reference be present in a signature of a function callable from C code?
我有点困惑:我有一个 C++ API,它应该从 C 代码调用并在函数声明中使用 __cdecl
。
有一个带有函数指针的 vtable,如下所示:
void (__cdecl *funptr) (const MyStruct& obj);
引用是一种 C++ 构造,不是吗?怎么会有带引用的__cdecl
?
最后:__cdecl
是否等同于将所有内容包装在 extern "C"
语句中?在那种情况下参考文献呢?
我有点懵..
__cdecl
只是一个调用约定。与C语言无关。它指定谁负责使用函数调用进行堆栈清理。
extern "C"
防止名称修饰,使用c语言调用约定
这些是苹果和橘子。
__cdecl
是一个 non-standard 关键字,用于描述更常见的 x86 ABI 调用约定之一(与 __stdcall
一起),它指定变量如何 passed/stacked呼叫者和被呼叫者。它与 C 无关——一些历史悠久的 Microsoft C 编译器只是使用了这个调用约定,因此得名。许多编程语言都可以使用此调用约定,同样,C 代码也不必使用它。
extern "C"
只是意味着代码应该由 C++ 编译器“像 C”一样编译,禁用 C++ 编译器内部使用的各种名称修改等。它不一定与兼容的 C 相关,但也可以在可能使用不同名称修改的两个不同 C++ 编译器之间共享代码时使用。
两者都与引用的工作方式无关。在 C 中它们不会编译,在 C++ 中它们会编译。您发布的代码是 C++。
这段代码确实看起来很草率。
__cdecl
关键字定义了调用约定:它设置参数应该传递到哪里(寄存器或内存),以什么顺序,函数调用的哪一侧负责处理它们函数调用完成,以及应该在整个调用过程中保留哪些寄存器状态。 extern "C"
声明也控制名称修改,即函数在什么名称下对当前翻译单元之外的代码可见。这两个声明都不会影响参数本身的内存表示。所有这些问题统称为应用程序二进制接口 (ABI)。
这意味着声明 C 调用约定同时提及 C 外来类型的函数签名不一定无用或无意义。当然不要求只有可传递的纯类型(C 和 C++ 之间共享的类型)应该跨越 C/C++ 边界传递:预计有时 C 代码可能会收到不透明的(对 C 的)指向 [=29 的指针=],到内部包含引用的结构,等等。但是,函数签名中直接存在 C 外来类型(引用、non-POD 类 按值传递、non-opaque 指向此类的指针)应该有些令人担忧。它表明编写该代码的人可能没有对 ABI 稳定性考虑太深,或者至少没有像程序员的便利性那样重视它。
现在,引用通常在内部表示为指针,这意味着在 C 端,人们应该期望声明的行为与以下行为相同:
void (*funptr)(const MyStruct *obj);
这就是我认为编写该签名的程序员所假设的。但是,这并不能保证。虽然 C ABI 在每个平台上几乎都是一成不变的,但 C++ 的 ABI 在历史上一直不太稳定。不知道编译器是否可能有一天,例如,开始将 const
引用传递给没有 mutable
字段的“小”类型,就好像它们是 by-value 参数一样,这可能会破坏上述声明在问题中。目前这似乎不太可能,但并非完全超出可能性范围。
我有点困惑:我有一个 C++ API,它应该从 C 代码调用并在函数声明中使用 __cdecl
。
有一个带有函数指针的 vtable,如下所示:
void (__cdecl *funptr) (const MyStruct& obj);
引用是一种 C++ 构造,不是吗?怎么会有带引用的__cdecl
?
最后:__cdecl
是否等同于将所有内容包装在 extern "C"
语句中?在那种情况下参考文献呢?
我有点懵..
__cdecl
只是一个调用约定。与C语言无关。它指定谁负责使用函数调用进行堆栈清理。
extern "C"
防止名称修饰,使用c语言调用约定
这些是苹果和橘子。
__cdecl
是一个 non-standard 关键字,用于描述更常见的 x86 ABI 调用约定之一(与 __stdcall
一起),它指定变量如何 passed/stacked呼叫者和被呼叫者。它与 C 无关——一些历史悠久的 Microsoft C 编译器只是使用了这个调用约定,因此得名。许多编程语言都可以使用此调用约定,同样,C 代码也不必使用它。
extern "C"
只是意味着代码应该由 C++ 编译器“像 C”一样编译,禁用 C++ 编译器内部使用的各种名称修改等。它不一定与兼容的 C 相关,但也可以在可能使用不同名称修改的两个不同 C++ 编译器之间共享代码时使用。
两者都与引用的工作方式无关。在 C 中它们不会编译,在 C++ 中它们会编译。您发布的代码是 C++。
这段代码确实看起来很草率。
__cdecl
关键字定义了调用约定:它设置参数应该传递到哪里(寄存器或内存),以什么顺序,函数调用的哪一侧负责处理它们函数调用完成,以及应该在整个调用过程中保留哪些寄存器状态。 extern "C"
声明也控制名称修改,即函数在什么名称下对当前翻译单元之外的代码可见。这两个声明都不会影响参数本身的内存表示。所有这些问题统称为应用程序二进制接口 (ABI)。
这意味着声明 C 调用约定同时提及 C 外来类型的函数签名不一定无用或无意义。当然不要求只有可传递的纯类型(C 和 C++ 之间共享的类型)应该跨越 C/C++ 边界传递:预计有时 C 代码可能会收到不透明的(对 C 的)指向 [=29 的指针=],到内部包含引用的结构,等等。但是,函数签名中直接存在 C 外来类型(引用、non-POD 类 按值传递、non-opaque 指向此类的指针)应该有些令人担忧。它表明编写该代码的人可能没有对 ABI 稳定性考虑太深,或者至少没有像程序员的便利性那样重视它。
现在,引用通常在内部表示为指针,这意味着在 C 端,人们应该期望声明的行为与以下行为相同:
void (*funptr)(const MyStruct *obj);
这就是我认为编写该签名的程序员所假设的。但是,这并不能保证。虽然 C ABI 在每个平台上几乎都是一成不变的,但 C++ 的 ABI 在历史上一直不太稳定。不知道编译器是否可能有一天,例如,开始将 const
引用传递给没有 mutable
字段的“小”类型,就好像它们是 by-value 参数一样,这可能会破坏上述声明在问题中。目前这似乎不太可能,但并非完全超出可能性范围。