C++逆向分析——引用
1 2 3 4 5 6 7 | void main() { int x = 1; int & ref = x; ref = 2; printf ( "%d \n" , ref); return ; } |
反汇编代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | int x = 1; 00724A5F C7 45 F4 01 00 00 00 mov dword ptr [x],1 int & ref = x; 00724A66 8D 45 F4 lea eax,[x] 00724A69 89 45 E8 mov dword ptr [ref],eax ref = 2; 00724A6C 8B 45 E8 mov eax,dword ptr [ref] 00724A6F C7 00 02 00 00 00 mov dword ptr [eax],2 printf ( "%d \n" , ref); 00724A75 8B 45 E8 mov eax,dword ptr [ref] 00724A78 8B 08 mov ecx,dword ptr [eax] 00724A7A 51 push ecx 00724A7B 68 30 7B 72 00 push offset string "%d \n" (0727B30h) 00724A80 E8 4D C6 FF FF call _printf (07210D2h) 00724A85 83 C4 08 add esp,8 |
修改为指针,看看反汇编结果:
1 2 3 4 5 6 7 | void main() { int x = 1; int * ref = &x; *ref = 2; printf ( "%d \n" , *ref); return ; } |
是不是发现下面的反汇编结果和上面引用的一模一样!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | int x = 1; 00B44A5F C7 45 F4 01 00 00 00 mov dword ptr [x],1 int * ref = &x; 00B44A66 8D 45 F4 lea eax,[x] 00B44A69 89 45 E8 mov dword ptr [ref],eax *ref = 2; 00B44A6C 8B 45 E8 mov eax,dword ptr [ref] 00B44A6F C7 00 02 00 00 00 mov dword ptr [eax],2 printf ( "%d \n" , *ref); 00B44A75 8B 45 E8 mov eax,dword ptr [ref] 00B44A78 8B 08 mov ecx,dword ptr [eax] 00B44A7A 51 push ecx 00B44A7B 68 30 7B B4 00 push offset string "%d \n" (0B47B30h) 00B44A80 E8 4D C6 FF FF call _printf (0B410D2h) 00B44A85 83 C4 08 add esp,8 |
引用类型
引用类型就是变量的别名,其在初始化时必须要赋值。
// 基本类型
int
x =
1
;
int
& ref = x;
ref =
2
;
printf(
"%d \n"
,ref);
// 类
Person p;
Person& ref = p;
ref.x =
10
;
printf(
"%d \n"
,p.x);
// 指针类型
int
****** x = (
int
******)
1
;
int
******& ref = x;
ref = (
int
******)
2
;
printf(
"%d \n"
,x);
// 数组类型
int
arr[] = {
1
,
2
,
3
};
int
(&p)[
3
] = arr;
p[
0
] =
4
;
printf(
"%d \n"
,arr[
0
]);
如上是引用类型作用在各个类型下的例子,那么引用类型是如何实现的呢?其本质是什么?我们可以看下反汇编代码:
会发现这段反汇编和指针的反汇编一模一样的:
这时候我们暂时的出结论:引用类型就是指针。
在我vs2022里实验下:
但如果引用类型就是指针,为什么C++需要新创建一个引用类型的概念呢?它们之间必然存在着一些区别,我们可以从初始化、运算、赋值来看反汇编代码的区别:
我们可以很清晰的看见区别从运算到赋值都不一样,指针运算到赋值改变的是指针本身,而不是指针指向的那个地址,而引用则不一样其从运算到赋值改变的是所引用的变量,我们得出这几个结论:
-
引用必须赋初始值,且只能指向一个变量,从一而终(专一);
-
对引用赋值,是对其指向的变量赋值,而不是修改引用本身的值;
-
对引用做运算,就是对其指向的变量做运算,而不是对引用本身做运算;
-
引用类型就是一个弱化了的指针;个人理解:引用类型就是一个*p。
C++设计引用类型是因为指针类型很难驾驭,一旦用不好就回出问题,所以取长补短设计了引用类型。
那么引用类型在实际开发中的作用是什么呢?我们可以用在函数参数传递中:
#include <stdio.h>
void
Plus(
int
& i) {
i++;
return
;
}
void
main() {
int
i =
10
;
Plus(i);
printf(
"%d \n"
, i);
return
;
}
如上代码中Plus函数的参数是一个引用类型,当我们把变量i传递进去,i就会自增1,而实际上也就修改变量i本身的值;换一种说法就是,我们之前函数参数传递的是值,而这里传递的是变量的地址。
那么在构造类型中又是怎么样的呢?
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 | #include <stdio.h> class Base { public : int x; int y; Base( int a, int b) { this ->x = a; this ->y = b; } }; void PrintByRef(Base& refb, Base* pb) { printf ( "%d %d \n" , pb->x, pb->y); printf ( "%d %d \n" , refb.x, refb.y); } void main() { Base b(1, 2); PrintByRef(b, &b); return ; } |
我们可以看见除了读取的表现形式不一样,实际上汇编代码是一模一样的;但是指针类型是可以重新赋值并运算的,而引用类型不可以。
当一个变量是int类型的,而我们引用类型却是一个其他类型的,会怎么样呢?
int
x =
10
;
Person& ref = (Person&)x;
这是可以编译的,但是没有实际意义,所以在使用引用的时候原来是什么类型就应该使用什么类型。
大家都知道,我们使用指针的时候是可以修改指针本身的,这会存在一定的风险,而C++中提供了引用类型,不可以修改引用本身,我们可以修改被引用的值,当我们不想其他人修改引用类型对应引用的值,可以使用const这个关键词,这种方式我们称之为常引用:
【推荐】国内首个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恶意软件特征及分类