C++重载底层原理
C++重载原理
C++实现重载使用的是叫name mangling的技术,实际上是靠函数名称和传递的参数类型、数量生成一个新的函数名称,比如
void func(int); void func(double);
(g++ xx.cpp -S
后生成xx.s汇编文件)经过编译后的汇编符号变成
_Z4funci; _Z4funcd;
通过这样的方法实现重载。
而C不能实现重载的原因是因为C不能通过函数名称和传递的参数来生成新的函数符号,所以c文件汇编后的函数名与源文件的函数基本一致。
tips:使用C++filt可以还原C++生成的函数符号
>C++filt _Z4funci >fun(int)
再谈论一下函数调用约定
函数调用约定指的是函数参数的入栈顺序、位置以及由谁恢复堆栈的约定。
__cdecl
参数传递顺序由右到左,堆栈由调用方清理
int __cdecl func(int a1,int a2) { return a1+a2; } int main() { func(1,2); return 0; }
汇编代码如下:
5: return a1 + a2; 00561831 mov eax,dword ptr [a1] 00561834 add eax,dword ptr [a2] 6: } 00561837 pop edi 00561838 pop esi 00561839 pop ebx 0056183A add esp,0C0h 00561840 cmp ebp,esp 00561842 call __RTC_CheckEsp (056123Fh) 00561847 mov esp,ebp 00561849 pop ebp 0056184A ret ... 9: func(1, 2); 005618D1 push 2 005618D3 push 1 005618D5 call func (05613B6h) 005618DA add esp,8
可以看出先将2压入栈,再压入1,最后由main函数恢复栈。
__stdcall
__stdcall
调用约定用于调用 Win32 API 函数。 参数从右到左进行入栈,被调用方将清理堆栈,以便让编译器生成 vararg
函数 __cdecl
。 使用此调用约定的函数需要一个函数原型。 __stdcall
修饰符是 Microsoft 专用的。
vararg
函数指接收可变数量参数的函数。
int __stdcall func(int a1,int a2) { return a1+a2; } int main() { func(1,2); return 0; }
汇编代码如下:
5: return a1 + a2; 00181836 or al,5Fh 6: } 00181838 pop esi 00181839 pop ebx 0018183A add esp,0C0h 00181840 cmp ebp,esp 00181842 call __RTC_CheckEsp (018123Fh) 00181847 mov esp,ebp 00181849 pop ebp 0018184A ret 8 ... 9: func(1, 2); 001818D1 push 2 001818D3 push 1 001818D5 call func (01813BBh)
__fastcall
这个约定仅适用于x86结构,尽可能多得使用寄存器传递函数自变量,前两个参数从左往右存入寄存器ECX、EDX,多余的参数从右往左压入栈内,如果有多余的参数在栈中则由被调用者恢复。
int __fastcall func(int a1,int a2) { return a1+a2; } int main() { func(1,2); return 0; }
汇编代码如下:
5: return a1 + a2; 00B9183D mov eax,dword ptr [a1] 00B91854 in eax,5Dh 00B91856 ret 4 ... 9: func(1, 2, 3); 00B918D1 push 3 00B918D3 mov edx,2 00B918D8 mov ecx,1 00B918DD call func (0B913C0h)
可以看出是先压多余的参数入栈,再将前两个参数存入寄存器中,我也尝试了一下只传一个参数是存ECX还是EDX,结果是ECX。
__thiscall
特定于 Microsoft __thiscall
的调用约定用于 x86 体系结构上的 C++ 类成员函数。 它是成员函数使用的默认调用约定,该约定不使用变量参数(vararg
函数)。
在 __thiscall
下,被调用方清理堆栈,这对于 vararg
函数是不可能的。 自变量将从右到左推送到堆栈中。 指针 this
通过注册 ECX 传递,而不是在堆栈上传递。
__thiscall是C++类成员函数所独有的,在汇编代码中通常是将this作为第一个参数传递。
//TODO:能力尚浅,后续再来研究
__vectorcall
要看懂这个约定首先得知道x86与x64的区别。
//TODO:x86与x64的区别
__clrcall
//TODO:托管函数,也是不太懂
// clrcall3.cpp // compile with: /clr void Test() { System::Console::WriteLine("in Test"); } int main() { void (*pTest)() = &Test; (*pTest)(); void (__clrcall *pTest2)() = &Test; (*pTest2)(); }
__nakedcall
一种比较少见的约定,常用于驱动开发。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律