C++逆向分析——模版
模版
假设有一个冒泡排序的函数:
void
Sort(
int
* arr,
int
nLength)
{
int
i, k;
for
(i =
0
; i < nLength; i++)
{
for
(k =
0
; k < nLength-
1
-i; k++)
{
if
(arr[k] > arr[k+
1
]) {
int
temp = arr[k];
arr[k] = arr[k+
1
];
arr[k+
1
] = temp;
}
}
}
}
但是这个冒泡排序的函数只能对int类型,如果我们想要使用char类型的时候就要重新写一个函数,这就违背了C++的初衷,重复造轮子了:
那么如何避免重复造轮子呢?C++中使用模板来解决这个问题。
函数模板的语法是这样的:
template<
class
形参名,
class
形参名, ...>
返回类型 函数名(参数列表) {
函数体;
}
用模板的方式来修改一下这个冒泡排序函数:
template<
class
T>
void
Sort(T* arr,
int
nLength)
{
int
i, k;
for
(i =
0
; i < nLength; i++)
{
for
(k =
0
; k < nLength-
1
-i; k++)
{
if
(arr[k] > arr[k+
1
])
{
T temp = arr[k];
arr[k] = arr[k+
1
];
arr[k+
1
] = temp;
}
}
}
}
在当前这个函数中我们只有一个地方需要替换修改,所以在写模板关键词时候尖括号内的class形参只有一个,而我们只需要将需要替换的地方改成形参的名字即可。
那么模版其原理是什么,编译器做了什么工作呢?我们可以看一下如下代码的反汇编代码:
使用不同类型的数组传入冒泡排序函数,观察一下执行地址:
可以看见,两个函数的地址完全不一样,这就说明模板的本质就是编译器会在看见不同的传入类型时创建不同的函数。
模板除了可以在函数中使用也可以在结构体(类)中使用模板,其格式如下所示:
template<
class
形参名,
class
形参名, ...>
class
类名 {
...;
}
如下代码,一个结构体,有两个成员函数,一个是比较返回最大的数,一个则是最小的数:
struct Base {
int
a;
int
b;
char
x;
char
y;
int
Max() {
if
(a>b) {
return
a;
}
else
{
return
b;
}
}
char
Min() {
if
(x<y) {
return
x;
}
else
{
return
y;
}
}
};
但这个结构体已经指定了成员变量的数据宽度int、char,而我们想要比较任意类型的话,可以使用模板改造下这段代码:
这个模板想使用的话,我们就需要告诉编译器模板中的T、M分别对应什么,所以如果直接使用Base b;则无法编译。
使用如下格式即可:
Base<
int
,
char
> b;
在我的vs2022里实验下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | #include <stdio.h> template < class T, class M> struct Base { T a; T b; M x; M y; T Max() { if (a > b) { return a; } else { return b; } } M Min() { if (x < y) { return x; } else { return y; } } }; void main() { Base< int , float > base; base.a = 3; base.b = 4; base.x = 1.2f; base.y = 3.4f; int max = base.Max(); float min = base.Min(); printf ( "%d %f\n" , max, min); return ; } |
看下反汇编代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | Base< int , float > base; base.a = 3; 004C197F C7 45 E8 03 00 00 00 mov dword ptr [base],3 base.b = 4; 004C1986 C7 45 EC 04 00 00 00 mov dword ptr [ebp-14h],4 base.x = 1.2f; 004C198D F3 0F 10 05 38 7B 4C 00 movss xmm0,dword ptr [__real@3f99999a (04C7B38h)] 004C1995 F3 0F 11 45 F0 movss dword ptr [ebp-10h],xmm0 base.y = 3.4f; 004C199A F3 0F 10 05 3C 7B 4C 00 movss xmm0,dword ptr [__real@4059999a (04C7B3Ch)] 004C19A2 F3 0F 11 45 F4 movss dword ptr [ebp-0Ch],xmm0 int max = base.Max(); 004C19A7 8D 4D E8 lea ecx,[base] 004C19AA E8 CE F6 FF FF call Base< int , float >::Max (04C107Dh) 004C19AF 89 45 DC mov dword ptr [max],eax float min = base.Min(); 004C19B2 8D 4D E8 lea ecx,[base] 004C19B5 E8 AF F6 FF FF call Base< int , float >::Min (04C1069h) 004C19BA D9 5D D0 fstp dword ptr [min] printf ( "%d %f\n" , max, min); 004C19BD F3 0F 5A 45 D0 cvtss2sd xmm0,dword ptr [min] 004C19C2 83 EC 08 sub esp,8 004C19C5 F2 0F 11 04 24 movsd mmword ptr [esp],xmm0 004C19CA 8B 45 DC mov eax,dword ptr [max] 004C19CD 50 push eax 004C19CE 68 30 7B 4C 00 push offset string "%d %f\n" (04C7B30h) 004C19D3 E8 FF F6 FF FF call _printf (04C10D7h) |
分析下:
看下1.2的16进制表示:
可以看到就是1.2和3.4,只是编译器,将这两个数字放到了常量区。
因此,本质上,模板就是给不同的类和函数在编译期间根据类型取了一个新的名字:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
2020-04-09 OWASP TOP10 Web应用十大安全风险_2017
2020-04-09 移动互联网恶意程序描述格式 ——移动端恶意软件分类再合适不过了
2020-04-09 用短信传播病毒:最新Android手机勒索软件Koler
2020-04-09 2019年Android恶意软件专题报告:未来移动安全呈现四大趋势——资费消耗与隐私窃取分别以高达46.8%和41.9%的占比,成为横行无忌的主要恶意软件类型,其次分别为远程控制、流氓行为、恶意扣费和欺诈软件。
2020-04-09 2018年Android恶意软件专题报告
2020-04-09 浅析Android恶意应用及其检测技术
2020-04-09 Android恶意软件特征及分类