这与一系列面向对象的 C 帖子有关,但不同之处在于我不需要所有功能,只需要一个:
做到这一点的能力:
struct foo {
int (*bar)(void);
char baz;
};
然后有反向引用。 C 有一个称为 cdecl 的调用约定;基本上将参数推入堆栈,有一个返回指针,然后跳转到某个地址。 该地址处的代码将参数从堆栈中弹出并继续其快乐的方式。
thiscall 约定略有不同,因为它隐式添加了一个额外的参数,即“this”指针。
由于您可以非常轻松地开始在 C 中执行任意字节代码,并且由于 gcc 支持内联汇编器模板,因此听起来您可以创建一些宏,以便您可以执行以下操作:
int bar(void) {
GETTHIS;
cThis->baz = 0;
}
int createFoo(struct Foo*pFoo) {
ASSIGN(pFoo, bar);
}
基本上 ASSIGN 要做的就是以某种方式回避 cdecl 来模拟 thiscall 风格约定,然后 GETTHIS 要做的就是会计技巧的另一面。
我想知道是否:
仅此而已;真正的“我们都是同意的成年人”风格的成员函数的便利性简直太棒了。谢谢!
备注:
我的朋友最终得到了它:https://gist.github.com/1516195...需要指定函数数量并且仅限于x86_64...但是,是的,这是一个非常好的妥协,使事情变得非常不引人注目。
第一个小修正:
C 有一个称为 cdecl 的调用约定;基本上推动争论 到堆栈,有一个返回指针,然后跳转到某个地址。这 该地址处的代码将参数从堆栈中弹出并继续执行 快乐的方式。
被调用者不从堆栈中弹出参数。它读取参数而不将它们从堆栈中弹出。相反,堆栈由调用者清除。之所以如此,是因为在 cdecl 约定中,被调用者不知道参数的确切数量。
现在,关于你的问题。没有严格定义的 thiscall 调用约定。更多信息这里。
Gcc 特定:
thiscall 与 cdecl 几乎相同:调用函数会清除 栈中,参数按从右到左的顺序传递。这 区别在于添加了 this 指针,该指针被压入 最后是堆栈,就好像它是函数中的第一个参数一样 原型
您将无法隐藏额外的
this
参数并在函数体内检索它,特别是因为 this
是 first 函数参数。如果隐藏它 - 所有其他函数参数都将发生变化。相反,您可以(并且应该恕我直言)将其声明为第一个函数参数,并且您将可以直接访问它,而不需要额外的技巧:
int bar(struct foo *cThis) {
cThis->baz = 0;
}
Msvc 特定:
... this 指针在 ECX 中传递,由被调用者进行清理 堆栈,镜像 C 中为此使用的 stdcall 约定 编译器和 Windows API 函数。当函数使用变量时 参数的数量,是调用者清理堆栈(参见 cdecl)。
在这里,您可以通过将
ECX
寄存器的值复制到局部变量中来实现您的技巧。像这样的东西:
#define GETTHIS \
struct foo *cThis; \
_asm { \
mov cThis, ecx \
};
但这很棘手,并且可能并不总是有效。原因是,根据
thiscall
/stdcall
调用约定,ECX
寄存器保留供函数使用,因此编译器可能会生成覆盖 ECX
寄存器值的代码。这甚至可能在您调用 GETTHIS
宏之前发生。
不,C 中不支持“thiscall”,这仅适用于 C++。
这里也没有什么秘密技巧,只是一些语法糖。
当你写作时
class foo {
public:
int bar();
char baz;
};
编译器在概念上将其重写为
struct foo {
char baz;
};
int bar(struct foo* this);
然后当你这样做时
foo F;
F.bar();
这被编译为相当于
struct foo F;
bar(&F);
仅此而已!
如果你想和 Linus 一起玩,你只需要手动执行此操作,因为 他不相信你在输入 g++ 而不是 gcc 时不会立即使用虚拟继承。