C++逆向分析——引用

void main() {
    int x = 1;
    int& ref = x;
    ref = 2;
    printf("%d \n", ref);
    return;
}

反汇编代码:

    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  

 修改为指针,看看反汇编结果:

void main() {
    int x = 1;
    int* ref = &x;
    *ref = 2;
    printf("%d \n", *ref);
    return;
}

 

是不是发现下面的反汇编结果和上面引用的一模一样!

    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]);

如上是引用类型作用在各个类型下的例子,那么引用类型是如何实现的呢?其本质是什么?我们可以看下反汇编代码

images/download/attachments/12714553/image2021-4-4_23-6-8.png

会发现这段反汇编和指针的反汇编一模一样的:

images/download/attachments/12714553/image2021-4-4_23-7-49.png

这时候我们暂时的出结论:引用类型就是指针。

 

在我vs2022里实验下:

 

 

但如果引用类型就是指针,为什么C++需要新创建一个引用类型的概念呢?它们之间必然存在着一些区别,我们可以从初始化、运算、赋值来看反汇编代码的区别:

images/download/attachments/12714553/image2021-4-4_23-17-23.png

我们可以很清晰的看见区别从运算到赋值都不一样,指针运算到赋值改变的是指针本身,而不是指针指向的那个地址,而引用则不一样其从运算到赋值改变的是所引用的变量,我们得出这几个结论:

  1. 引用必须赋初始值,且只能指向一个变量,从一而终(专一);

  2. 对引用赋值,是对其指向的变量赋值,而不是修改引用本身的值;

  3. 对引用做运算,就是对其指向的变量做运算,而不是对引用本身做运算;

  4. 引用类型就是一个弱化了的指针;个人理解:引用类型就是一个*p

images/download/attachments/12714553/image2021-4-4_23-25-6.png

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本身的值;换一种说法就是,我们之前函数参数传递的是值,而这里传递的是变量的地址。

images/download/attachments/12714553/image2021-4-4_23-34-17.png

那么在构造类型中又是怎么样的呢?

#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;
}

 

 

images/download/attachments/12714553/image2021-4-4_23-46-5.png

 

我们可以看见除了读取的表现形式不一样,实际上汇编代码是一模一样的;但是指针类型是可以重新赋值并运算的,而引用类型不可以

当一个变量是int类型的,而我们引用类型却是一个其他类型的,会怎么样呢?

int x = 10;
Person& ref = (Person&)x;

这是可以编译的,但是没有实际意义,所以在使用引用的时候原来是什么类型就应该使用什么类型。

大家都知道,我们使用指针的时候是可以修改指针本身的,这会存在一定的风险,而C++中提供了引用类型,不可以修改引用本身,我们可以修改被引用的值,当我们不想其他人修改引用类型对应引用的值,可以使用const这个关键词,这种方式我们称之为常引用:

images/download/attachments/12714553/image2021-4-4_23-56-58.png

posted @ 2023-04-09 10:48  bonelee  阅读(81)  评论(0编辑  收藏  举报