三、基础语法2(30小时精通C++和外挂实战)

三、基础语法2(30小时精通C++和外挂实战)

  • B-02内联函数
  • B-04内联函数与宏
  • B-05_const
  • B-06引用
  • B-07引用的本质
  • B-08-汇编1-X86-X64汇编
  • B-09-汇编2-内联汇编
  • B-10-汇编3-MOV指令
  • C-02-汇编5-其他常见指令
  • C-05-汇编8-反汇编分析
  • C-07-const引用、特点

B-02内联函数

内联函数

作用:将函数调用展开成函数体代码,说白了,如果

#include <iostream>
using namespace std;inline void func(){cout << "func()" << endl;cout << "func()" << endl;cout << "func()" << endl;}inline int sum(int v1, int v2){return v1 + v2;
}int main(){func();func();func();int c =sum(10, 20);cout << c << endl;getchar();return 0;
}

如上代码调用func函数,如果没有内联,汇编很简单几句,就是调用,但如果是内联函数,就相当于下方,直接在函数调用展开称为函数体,执行代码很多,机器码也很多

int main(){cout << "func()" << endl;cout << "func()" << endl;cout << "func()" << endl;cout << "func()" << endl;cout << "func()" << endl;cout << "func()" << endl;cout << "func()" << endl;cout << "func()" << endl;cout << "func()" << endl;int c =sum(10, 20);cout << c << endl;getchar();return 0;
}

从这个角度想,这样是代码体积变大,好像没有什么意义,还不如不内联,直接调用。

//开辟栈空间
void func(){cout << "func()" << endl;cout << "func()" << endl;cout << "func()" << endl;}
//结束栈空间int sum(int v1, int v2){return v1 + v2;
}

还是有意义的,虽然函数调用的话可以减少体积,平时封装函数,就是减少体积、提高复用率,但是函数调用是有代价的,开辟栈空间,我们每调用一次函数,一个函数开始时开辟栈空间,此函数结束时,调用栈空间。

一旦内联函数,就不存在函数调用了,程序运行时,直接执行函数体中的内容,不存在调用函数,也就是不存在函数栈空间的开辟,也不存在回收栈空间,这样其实执行效率变高了,就不会去分配回收内存了,就直接相加了

什么情况下使用内联函数呢?
第一,调用的函数代码体积不是很大,只有几行
第二,函数经常调用,每隔几秒就调用一次
像体积小,且调用次数高的函数,建议将其声明为内联函数(不是内联,经常调用会频繁开辟、回收栈空间这个操作),一旦内联就不存在这种内存操作了,直接将代码拿来使用,可以提高效率

Inline是建议编译器将函数内联,不一定会执行的,如果代码体积太大,编译器优化时就不会将函数内联。
递归函数不会被内联如下

inline void run(){run();
}

在这里插入图片描述

下面我们来窥探一下内联的本质
我们先不内联,正常调用函数可以发现反汇编有call调用,接着我们内联反汇编
注意当我们写入inline进行函数内联时,进入反汇编并没有看到内联的效果,因为这是debug调试模式下的,而内联函数inline是优化时起作用的,所以我们要进入release模式下看结果。

我们先进入release模式下,将inline先去掉没有内联,反汇编看一看,我们运行后发现代码如下

    20: 	int c =sum(10, 20);21: 	cout << c << endl;
8B 0D 54 30 26 00    mov         ecx,dword ptr ds:[263054h]  
68 90 19 26 00       push        261990h  
6A 1E                push        1Eh  
FF 15 34 30 26 00    call        dword ptr ds:[263034h]  
8B C8                mov         ecx,eax  
FF 15 5C 30 26 00    call        dword ptr ds:[26305Ch]  

即便编译器不是内联,也会在release模式下进行优化,直接就将30的值push进去了,最终代码变成上面那样了,直接代码 cout << 30<< endl;连加法都省掉了,我们现在要证明inline的优化作用,而这样编译器直接优化了会看不出来,此时我们需要将其禁止优化

右击项目属性,禁止优化,此时无inline,反汇编看到存在函数调用无优化

    20: 	int c =sum(10, 20);
6A 14                push        14h  
6A 0A                push        0Ah  
E8 DE FF FF FF       call        sum (09216A0h)  
83 C4 08             add         esp,8  
89 45 FC             mov         dword ptr [c],eax  

这时我们要加入inline,刚刚在debug模式是不行的,此时我们要在release模式禁用优化下看其作用,但是还不行,我们在属性的C++下的优化下,禁用优化,还需要,将内联函数扩展选为任何适用项,此时就能看出了如下

    20: 	int c =sum(10, 20);
B9 0A 00 00 00       mov         ecx,0Ah  
83 C1 14             add         ecx,14h  19: 20: 	int c =sum(10, 20);
89 4D FC             mov         dword ptr [c],ecx  

就相当于int c = v1 +v2;这个是直接将函数体中的内容拿过来直接执行,没有进行调用

递归函数就没必要加inline最终在汇编中还是调用函数

刚刚我们是在汇编的方式进行的,现在我们将inline去掉另其运行生成exe在release模式下,我们使用IDA这个工具
有func\sum这两个函数

当加入inline时,发现内联的函数没了,只有一个main函数,是优化掉了,将内联的函数体直接运行,无调用,相当于只有一个main函数执行。

B-04内联函数与宏

内联函数与宏都可以减少函数调用的开销

#define add(v1,v2) v1+v2				#定义宏,add这个宏将左边的东西替换成右边的东西int main(){int c = add(10, 20);				#宏替换变成int c = v1+v1;	cout << c << endl;getchar();return 0;
}

像这种做这些数学运算的,建议将其直接转换成函数代码,就不要存在函数调用,两种方式,一种写成宏,一种写成内联函数,

推荐大家使用内联函数,内联函数目前看来还是个函数,看起来是个函数,我们写代码时是有提示的在内联函数里,在写宏的时候没有提示

函数有个特点,传参

在这里插入图片描述

在这里插入图片描述

如果看别人写C++代码表达式在左边,将右边的值赋给表达式,不要惊讶,这是可以的,但是C语言是不可以的,这个了解一下

B-05_const

Const这个知识点很重要,我们要重视

#include <iostream>
using namespace std;int main(){Const	int age = 10;age = 20;getchar();return 0;
}

我们只在第一次定义的时候可以赋值,使用const,以后都不能更改这个值,防止别人更改
Const必须在定义的时候使用

如果修饰的是类、结构体(的指针),其成员也不可以更改
在C/C++中有结构体的概念如

#include <iostream>
using namespace std;struct Date {				#定义了一个结构体int year;int month;int day;
};int main(){Date d = { 2011, 2, 5 };			#定义一个结构体变量d并依次赋值,在C语言中的定义需要struct Date d = { 2011, 2, 5 };	
d.year =2015;int age = 10;age = 20;getchar();return 0;
}Const Date d = { 2011, 2, 5 };	我们在定义是加入const,成员无法更改Date *p = &d1;//用一个指针指向d1这个结构体,指针类型为date会执行date类型的//我们可以使用指针来修改所指向的d1的成员值p->year = 2015;//这是使用指针简介去修改//结构体本身访问成员用点,指向结构体的指针去访问结构体的成员使用的是->,语法规定cout << d1.year << endl;const Date *p = &d1;/不能通过指针去修改d1的东西#include <iostream>
using namespace std;struct Date {int year;int month;int day;
};int main(){int age = 10;const int *p1 = &age;//p2不是常量,*p2是常量int const *p2 = &age;//p3是常量,*p3不是常量int * const p3 = &age;//p4是常量,*p4是常量const int * const p4 = &age;//p5是常量,*p5是常量int const * const p5 = &age;*p4 = 20;	//age =20p4 = &height;*p4 = 40;	//height =40;getchar();return 0;
}

在这里插入图片描述

引用就下节课再讲,引用这东西非常非常的关键
再下节课就将汇编,讲完后,大家对C++汇编代码就可以自己去看了

我们这只是学语法,学完后我们可以把别人的库拿来,

B-06引用

在这里插入图片描述

在C语言中,使用指针(pointer)可以间接获取、修改某个变量的值

int age = 10;
int *p = &age;
*p = 20;
cout << *p << endl;

&age 是取一个地址值
p里面存放着age的地址
*p是取p里面存放的地址对应的age中的值

在C++中,使用引用(reference)可以起到跟指针类似的功能

	int age = 10;
#定义了一个引用refage,refage相当于是age的别名,使用refage就是使用ageint &refage = age;			refage = 20;cout << age << endl;

指针可以修改指向,引用不能修改指向
引用开始 定义指向某个变量,以后就将其作为那个变量

可以利用引用初始化另一个引用,相当于某个变量的多个别名

int age = 10;
int &refage = age;
int &refage1 = refage;
int &refage2= refage1;

引用的价值之一是比指针更安全
指针用的好的话很强大,用的不好的话很危险,指针可以随意修改指向,它就有可能指向不该指向的地址,如某些存放重要数据的地址,引用只有定义时指向,之后便不能修改指向,要安全

void swap(int v1,int v2){int temp = v1;v1 = v2;v2 = temp;
}int main(){int a = 10;int b = 20;swap(a, b);cout << "a="<<a<<",b="<<b << endl;getchar();return 0;
}

如上程序,是无法将ab的值相互调换的,传值是相当于swap(int v1=a,int v2=b),将变量a与b传给V1和V2,在函数swap中没有a与b的值,这不是全局变量,所以调用此函数,无法变换值
而使用地址传递时,相当于swap(int *v1=&a,int *v2=&b)这个地址在全部空间都能找到,可以直接访问地址内的值然后,将其对换
正确写法如下

void swap(int *v1,int *v2){int temp = *v1;*v1 = *v2;*v2 = temp;
}int main(){int a = 10;int b = 20;swap(&a, &b);cout << "a="<<a<<",b="<<b << endl;getchar();return 0;
}

这是指针,然后发现会比较麻烦,传递是需要些&,取值需要加*很麻烦,但是有了引用,我们就不需要这么做,我们只需要在函数中定义引用即可,如下

void swap(int &v1,int &v2){int temp = v1;v1 = v2;v2 = temp;
}int main(){int a = 10;int b = 20;swap(a, b);cout << "a="<<a<<",b="<<b << endl;getchar();return 0;
}

这样是可以交换的,相当于swap(int &v1=a,int &v2=b),V1引用A,V2引用B,别名可以作为外面的AB的值,使用引用代码量减少,简单,之前指针的的效果也能达到,在函数里面的V1能指向外面的A

函数每次被调用,都要创建一个变量,产生的V1和V2都是全新的,存储空间也是全新的,当调用完后V1和V2的存储空间就被销毁了,再次调用swap,又会创建新的存储空间所以和之前讲 的从一而终并不矛盾

void swap(int &v1,int &v2){int temp = v1;v1 = v2;v2 = temp;
}int main(){int a = 10;int b = 20;swap(a, b);cout << "a="<<a<<",b="<<b << endl;int c = 2;int d= 3;swap(c, d);cout << "c=" << c << ",d=" << d << endl;getchar();return 0;
}

如果在同一个V1,不能重新指向

一定要在定义引用的同时给其一个变量,否则会报错

int &v1=a

默认情况下引用变量的类型必须和指向的类型相同

引用所指向的变量与是否为全局变量无关下面也是可以引用的

Int abc = 1;
Int main(){
Int &ref = abc;
}

B-07引用的本质

引用的本质就是指针,只是编译器削弱了它的功能

指针可以改变指向,而引用不能改变,可以间接修改其值

写法上引用比指针简单,而本质上引用就是指针

一个指针多大与CPU架构有关,

int main(){int age = 10;
// *p就是age的别名int *p = &age;*p = 30;cout << sizeof(p) << endl;//看指针变量p占多少字节,这个是看运行环境决定,在64位环境中是8个字节,X86的32位运行环境是4字节cout << sizeof(age) << endl;//整型变量一般就是4个字节和我们的CPU架构是没有什么关系的//ref就是age的别名,定义好refage那一刻,往后只要使用refage就是使用age,它就是age,所以refage占4个字节int &refage = age;refage = 40;cout << age << endl;cout << sizeof(refage) << endl;//此处实际在此时age是多少个字节,这样测引用占多少个字节是不标准的cout << sizeof(&refage) << endl;//变成&refage测也是不对的,这个相当于取出refage的地址,看地址占多少字节就是看指针多少自己与CPU架构相关,这样无法证明引用占多少字节getchar();return 0;
}

我们现在使用汇编来看其本质,我们打入断点进入调试状态,接着转汇编,这还不是最终的汇编,里面的类似【P】之类像变量的,是VS为了帮助我们理解生成的,为了看到最终的汇编,我们右击将显示符号名去掉

    26: 	int *p = &age;
8D 45 F4             lea         eax,[age]  
89 45 E8             mov         dword ptr [p],eax  27: 	*p = 30;
8B 45 E8             mov         eax,dword ptr [p]  
C7 00 1E 00 00 00    mov         dword ptr [eax],1Eh  32: 	int &refage = age;
8D 45 F4             lea         eax,[age]  
89 45 DC             mov         dword ptr [refage],eax  33: 	refage = 30;
8B 45 DC             mov         eax,dword ptr [refage]  
C7 00 1E 00 00 00    mov         dword ptr [eax],1Eh  

为了看到最终的汇编,我们右击将显示符号名去掉 ,以去掉之后就发现不一样了

    26: 	int *p = &age;
8D 45 F4             lea         eax,[ebp-0Ch]  
89 45 E8             mov         dword ptr [ebp-18h],eax  27: 	*p = 30;
8B 45 E8             mov         eax,dword ptr [ebp-18h]  27: 	*p = 30;
C7 00 1E 00 00 00    mov         dword ptr [eax],1Eh  32: 	int &refage = age;
8D 45 F4             lea         eax,[ebp-0Ch]  
89 45 DC             mov         dword ptr [ebp-24h],eax  33: 	refage = 30;
8B 45 DC             mov         eax,dword ptr [ebp-24h]  
C7 00 1E 00 00 00    mov         dword ptr [eax],1Eh 

左边的地址值是右边机器码的地址值,我们的机器码、代码都是载入内存的,每一个机器码都有地址,每次启动时代码地址值可能会变,只要改变代码,再启动,地址都会变,或者切换运行环境,代码值也会变

每个应用都有起始地址,如果每个应用的起始地址固定,那么函数地址也固定,但是如果起始地址随机,那么函数地址也随机

我们现在转汇编看的不是内存条的真实的物理地址,是虚拟地址,是操作系统给的虚拟地址,它会将虚拟地址映射到真正的内存条上去
我们发现两者一样,引用的本质就是指针

为什么能通过引用间接访问age,因为它的本质就是指针,这样的写法简单了

int &refage = age;
refage = 30;

本质上refage是个指针,存储着age的地址,将30赋值给refage所指向的age中
还是个弱化的指针,这是编译器的特性,编译器层面已经规定它的写法。
在这里插入图片描述

B-08-汇编1-X86-X64汇编

引用的本质是指针,我们现在要读懂汇编代码

利用汇编挖掘编程语言本质(免费课,建议看),课后有时间可以看完

汇编语言较多,它是与CPU挂钩的

在这里插入图片描述

ARM汇编是嵌入式,移动设备上的(iPad、iPhone 、Android)

我们现在着重看X64汇编,X64汇编根据编译器不同,有两种书写格式

Intel
AT&T
在我们windows平台,在VS中使用的是Intel汇编,如果是MAC平台一般格式为AT&T

汇编语言是不区分大小写的

在这里插入图片描述

学习汇编2大知识点

1,汇编指令
2,寄存器

寄存器是在CPU里面的,程序一启动载进内存,A,B,C变量存储在内存,CPU要对变量进行操作,如进行±运算,会借助寄存器,先将内存中的数据放到寄存器,在CPU计算好,再将值放回内存。
先将数据放到寄存器(离CPU更近)来算更快

在这里插入图片描述
在这里插入图片描述

    39: 	int a = 3;
C7 45 F8 03 00 00 00 mov         dword ptr [a],3  40: 	int b = a + 1;
8B 45 F8             mov         eax,dword ptr [a]  #将A取出给寄存器
83 C0 01             add         eax,1  			#寄存器加1
89 45 EC             mov         dword ptr [b],eax  #将寄存器给B

寄存器非常重要,没有寄存器就没法做很多运算
简单的两个变量间的计算就有寄存器的参与

不同的汇编寄存器是不一样的,如X86,X64,ARM等寄存器不同,汇编也不同
在这里插入图片描述

我们现在只学我们用的上的寄存器

Registers寄存器
RAX\RBX\RCX\RDX:通用寄存器(在64位这些寄存器很常用)

剩下的是有特殊用途的寄存器RBP后的,有机会讲

而在32位

EAX\EBX\ECX\EDX:通用寄存器

更久一些

16bit
AX\BX\CX\DX:通用寄存器

一个寄存器存储多大,和指针一样看CPU架构是什么
X64的一个寄存器如RAX能存8个字节的数据
在这里插入图片描述

虽然我们现在用的是X64汇编,但还是兼容以前的寄存器的

EAX存在于RAX里面这样就兼容了
Mov eax,10  表面上将10给了eax32位的寄存器,实际也对RAX改变AH H height  高字节
AL L low		低字节

在这里插入图片描述
在这里插入图片描述

B-09-汇编2-内联汇编

我们写的C++代码,但我们能否将汇编代码嵌入其中,这就是内联汇编

__asm{}

在这其中就可以写汇编代码了

我们完全可以C++代码和汇编代码混着用

     5: 	int a=10;
C7 45 F4 0A 00 00 00 mov         dword ptr [ebp-0Ch],0Ah  6: 	__asm{7: 		mov eax,a		(表面上a是个,实际上转的是Ad的地址值)
8B 45 F4             mov         eax,dword ptr [ebp-0Ch]  8: 	}

我们给RAX赋值一样会影响到EAX的值

B-10-汇编3-MOV指令

在这里插入图片描述

Mov dest,src6: 	int a = 3;
C7 45 F8 03 00 00 00 mov         dword ptr [ebp-0CH],3  #ebp-8是变量a的地址7: 	int b = a + 1;
8B 45 F8             mov         eax,dword ptr [ebp-0CH]  
83 C0 01             add         eax,1  
89 45 EC             mov         dword ptr [ebp-14h],eax  int main(){
int a = 3;int b = a + 1;getchar();return 0;
}

ebp的值:010FFE58H
a的地址:010FFE4CH == ebp-0CH
a是局部变量,每次使用都会重新分配地址给它,它的地址是变化的,所以最终生成的地址值不能是写死的006FFAB8H,每次调用函数的时候ebp都是新的值,这样就能保证
函数调用分配空间,函数调用完销毁空间

全局变量的地址值是写死的

  18: 	age = 3;
00E4506E C7 05 08 F0 E4 00 03 00 00 00 mov         dword ptr ds:[0E4F008h],3  

全局变量是程序一启动就会存在的,不变的

如果想要弄懂可以看 利用汇编挖掘程序语言的本质

每一个字节都有自己的内存地址

Mov [1128h],3   将3放到1128h地址所对应的存储空间(这里是使用一个字节存储3)
int a = 3;   这里使用4个字节存储3

一般我们把3放到某个内存空间时都要指定一下单位

Ptr固定写法,指定空间大小 Word表示2个字节

mov    word ptr [1128h],3			这里3通过两个字节存储

只知道一个地址值就一个字节,如果给定一个地址 值,一般来说是向高地址吞并自己

 dword为4个字节mov    eax,dword ptr [1128h]    从1128h内存地址开始取出4个字节(1128h、1129h、112Ah、112Bh共4个字节的内容)传给eax

凡是看到【】里面放的都是地址值
凡是看到call 就是调用函数

    17: 	test();
E8 B1 C3 FF FF       call        00941424  18: 	func();
E8 B1 C3 FF FF       call        00941429  

一个字节是8位,用四个字节存储3的话怎么存
4个字节存储3

16进制

00 00 00 03H

2进制

000000000 000000000 000000000 000000011

CPU大小端模式,我们接触的大部分是小端模式(高高低低,高字节放高地址,低字节放低地址)

比较低字节的东西放到低地址的地方

int a = 10;

我们调试,在下面监视窗口,名称输入&a可以看到a的地址值0x00EFF7B0
我们在调试-窗口-可以找到内存,我们选一个,可以窥探到内存,而且精细到每一个字节
我们在内存中搜索0x00EFF7B0,结果如下

0x00EFF7B0  0a 00 00 00 cc cc cc cc 08 f8 ef 00  ....????.??.
0x00EFF7BC  f9 5b b4 00 01 00 00 00 20 64 12 01  ?[?..... d..
0x00EFF7C8  20 74 12 01 ca cd cc 97 71 12 b4 00   t..????q.?.
0x00EFF7D4  71 12 b4 00 00 20 d5 00 00 00 00 00  q.?.. ?.....
0x00EFF7E0  00 00 00 00 00 00 00 00 00 00 f0 00  ..........?.

我们可以让他们每一列显示一个字节

0x00EFF7B0  0a  .
0x00EFF7B1  00  .
0x00EFF7B2  00  .
0x00EFF7B3  00  .
0x00EFF7B4  cc  ?
0x00EFF7B5  cc  ?
0x00EFF7B6  cc  ?
0x00EFF7B7  cc  ?
0x00EFF7B8  08  .
0x00EFF7B9  f8  ?
0x00EFF7BA  ef  ?
0x00EFF7BB  00  .
0x00EFF7BC  f9  ?
0x00EFF7BD  5b  [
0x00EFF7BE  b4  ?

变量a占了4个字节,如上,又是小端模式,所以读数从高像低读,00 00 00 0a 就是10

我们平常编程是不关心大小端模式的

一个变量的地址值,是它所有字节地址中的最小值

寄存器是独立存在的相对于内存,相互独立存在的

汇编;

1,用在性能极致优化
2,外挂
3,软件破解
4,嵌入式开发(一些操作某个硬件的代码只能用汇编)

我们学汇编只是为了弄懂高级语言的的底层是干什么了

C-02-汇编5-其他常见指令

Lea dest,[地址值]Load effect address(装载有效的地址值,将地址值装载进来)Lea eax,[1122H]
直接将地址值赋值给eax
相当于 eax == 1122Hmov eax,dword ptr [1122H]
1122H地址对应的存储空间取出4字节的数据(假设为4)赋值给eaxeax == 4

在这里插入图片描述

在这里插入图片描述

//int a = 10;
//ebp-8是变量a的地址
00B0784E  mov         dword ptr [ebp-8],0Ah  //int b = 5;
//ebp-14h是变量b的地址
00B07855  mov         dword ptr [ebp-14h],5  //eax == a == 10
00B0785C  mov         eax,dword ptr [ebp-8]  //cmp是compare的简称,比较
//比较eax和b的值是否相等(比较后会有结果,结果会影响到下面jne)
00B0785F  cmp         eax,dword ptr [ebp-14h]  //jne:jump not equal,比较结果不相等才跳转
00B07862  jne         00B0787D  24: 		printf("1111");
00B07864  mov         esi,esp  
00B07866  push        0B0CC70h  
00B0786B  call        dword ptr ds:[00B10194h]  
00B07871  add         esp,4  
00B07874  cmp         esi,esp  
00B07876  call        00B012D5  25: 	}26: 	else
00B0787B  jmp         00B07894  27: 	{28: 		printf("2222");
00B0787D  mov         esi,esp  
00B0787F  push        0B0CCB8h  
00B07884  call        dword ptr ds:[00B10194h]  
00B0788A  add         esp,4  
00B0788D  cmp         esi,esp  
00B0788F  call        00B012D5  38: 	getchar();
00B07894  mov         esi,esp  
00B07896  call        dword ptr ds:[00B1017Ch]  
00B0789C  cmp         esi,esp  
00B0789E  call        00B012D5  39: 	return 0;
00B078A3  xor         eax,eax  
40: }

直接看汇编是不容易看出来的,在VS中我们可以显示符号名,会在函数地址的左边显示函数名,对比着看

汇编金手指

权威参考:Intel白皮书

函数的返回值一般放到EAX
函数调用过程中ESP,EBP牵扯到栈

Call之前的push是传参

跳转指令很多,只需掌握使用的即可,不懂的可以在网上查

C-05-汇编8-反汇编分析

    21: 	int a = 1;
003E784E  mov         dword ptr [a],1  22: 	int b = 2;
003E7855  mov         dword ptr [b],2  23: 	int c = a + b;
003E785C  mov         eax,dword ptr [a]  
003E785F  add         eax,dword ptr [b]  
003E7862  mov         dword ptr [c],eax  

为什么要借助寄存器EAX中转一下,为什么不能内存加内存再赋给内存,不行,这是有CPU架构指令的,CPU架构决定有些操作只能针对寄存器,不能直接对内存进行操作

 dword ptr [b]  从b内存地址对应的存储空间取值Mov			dword ptr [b] ,dword ptr [a] 		此操作错误,mov指令不支持此操作,不能内存到内存Intel白皮书已经规定了,没有这样的操作

优化分不同程度,不同编译器有不同优化,有的没有用途的代码在汇编中直接就会消失

指针变量和句部变量是一样的,都在内存中,都有内存地址,

//int age = 3;
00DA7CA8  mov         dword ptr [ebp-0Ch],3  //eax == ebp-0Ch,存放着age的地址值
00DA7CAF  lea         eax,[ebp-0Ch]  //ebp-18h是指针变量p的地址值
//将age的地址值存放到指针变量p所在的存储空间
//int *p = &age;
00DA7CB2  mov         dword ptr [ebp-18h],eax  //*p = 5;
//将age的地址值存放到eax
00DA7CB5  mov         eax,dword ptr [ebp-18h]  
//age = 5;
00DA7CB8  mov         dword ptr [eax],5  

上面两个通过指针简介修改age的值

Mov是带单位的,取数据是取多少个字节
Lea不要取数据,所以地址值左边不用写单位的如dword

Mov         eax,ebp-0Ch 	

错误,mov不支持运算,但在中括号中可以进行简单的地址运算,所以要使用lea,不使用mov将地址传给某个人

如果要改的话得

Sub  	ebp,0Ch 
Mov		eax,ebp

但这样ebp的值就改变了,使用lea的话ebp的值不会发生改变,也能运算

只要将地址给某个就lea,mov是不可以的,就不深究了

引用的汇编代码和指针是一样的,引用的本质就是汇编。

00DA7CAF  lea         eax,[ebp-0Ch]  
00DA7CB2  mov         dword ptr [ebp-18h],eax 

我们以后看到上面两段代码可以知道
1,ebp-18h是指针的地址值,ebp-0Ch是另一个变量的地址值

C-07-const引用、特点

在这里插入图片描述

常引用

引用被const修饰后,就变成了长引用,就不能再被修改了可以访问但不能改

int & const ref = age;ref = 30;

为什么,这里ref可以被修改,这个表面上看确实不太容易,但按照指针的方式看的话,如下

int * const p =  &age;
p=30;			此处不能被修改,const修饰的是右边,修饰的是指针变量(里面存储的是地址),所以指针变量不能被修改,而指针所指向的存储空间时可以修改的
*p=30;			但这可以修改,指针所指向的存储空间时可以修改的,*p就是age

相对于引用来说,ref就是age,与指针相对比就可以看出了

int age = 10;
int height = 120;
//指向变量P1不能修改指向,可以利用指向变量P1间接修改所指向的变量
int * const p1 = &age;
//p1 =&height;
*p1 = 30;//指向变量P2可以修改指向,不可以利用指向变量P2间接修改所指向的变量
int const *p2 = &age;
p2 = &height;
//*p2=30;

注意:引用是有限制的,它本身不能修改指向
引用的本质就是指针,所以两者写法一样时(两者const所在位置相同时),看指针,指针什么行为,它就什么行为

//ref1不能修改指向,但是可以通过ref1间接修改所指向的变量
int & const ref1 = age;		//注意此处引用本身不能修改指向,故此处相当于int &ref1 = age;	
ref1 = 30;//ref2不能修改指向,此处不可以通过ref1间接修改所指向的变量
int const &ref2 = age;
//ref2 = 40;		此处报错,无法修改

Const引用特点,可以指向临时数据

作为函数参数时,可以接受const和非const实参

int sum(const int &v1, const int &v2){return v1 + v2;
}int main(){//非cost实参int a = 10;int b = 20;sum(a,b);//const实参const int c = 10;const int d = 20;sum(c, d);

很多语法不是很懂没关系,到时候看汇编就能验证了

当常引用指向不同类型的数据时,会产生临时变量,即引用指向的,并不是初始化时的那个变量
此处为int类型本身的

	int age = 10;const int &ref = age;age = 30;
00FC5F34  mov         dword ptr [ebp-3Ch],0Ah  
00FC5F3B  lea         eax,[ebp-3Ch]  
00FC5F3E  mov         dword ptr [ebp-48h],eax  
00FC5F41  mov         dword ptr [ebp-3Ch],1Eh  cout <<"age is " << age <<endl;				#结果过30cout << "ref is " << ref << endl;				#结果30

当const为另一种数据类型long时

	int age = 10;const long  &ref = age;age = 30;
00515F34  mov         dword ptr [ebp-3Ch],0Ah  
00515F3B  mov         eax,dword ptr [ebp-3Ch]  
00515F3E  mov         dword ptr [ebp-54h],eax  
00515F41  lea         ecx,[ebp-54h]  
00515F44  mov         dword ptr [ebp-48h],ecx  
00515F47  mov         dword ptr [ebp-3Ch],1Eh  cout <<"age is " << age <<endl;				#结果过30cout << "ref is " << ref << endl;				#结果10

我们发现这两个结果是不一样的,因为变为另一种数据类型时,从汇编代码可以看出,产生了一个临时变量,临时产生了一个地址ebp-54h,所以我们指向的是临时变量的地址,所以结果为10

不同编程语言转成的汇编是一样的吗?
Java、C++、OC、swift写代码 --》汇编\机器码(取决于CPU架构)
编译器不同,产生的汇编可能是不同的,如将其生成X86架构或者X64的汇编、机器码

最终生成什么汇编和你最终运行的平台有关,汇编、机器指令是有CPU定好的,CPU支持哪些汇编指令,哪些机器指令由CPU架构决定

CPU决定支持哪些汇编,机器码

Imm 表示立即数,就是直接写出来的值如3等不是变量a等数
m内存,允许立即数赋给内存,在白皮书是找不到,左边内存,右边内存的即mov [],[]不存在

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/875009.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

TreeSelect增加可筛选功能

TreeSelect官方可筛选示例 <template><el-tree-selectv-model"value":data"data"filterablestyle"width: 240px"/><el-divider /><el-divider />filter node method:<el-tree-selectv-model"value":data&q…

数据安全传输--加密算法

目录 古典加密算法与近代加密算法对比 算法分类 对称加密 常见的对称加密算法 在对称加密算法中密钥共享是一个很麻烦的问题 非对称加密 非对称加密过程 常见非对称加密算法 对称加密和非对称加密两者对比结论 DH算法 身份认证和数据认证技术 hash算法 hash算法特点…

PySide(PyQt),自定义图标按钮

1、在Qt Designer中新建画面&#xff0c;并放置3个按钮&#xff08;QPushButton&#xff09;和一个分组框&#xff08;QGroupBox&#xff09;小部件&#xff0c;分别命名为btn_1&#xff0c; btn_2&#xff0c;btn_3和btnStation。 2、将所有小部件的显示文字内容删除。 3、将…

论文复现:Predictive Control of Networked Multiagent Systems via Cloud Computing

Predictive Control of Networked Multiagent Systems via Cloud Computing论文复现 文章目录 Predictive Control of Networked Multiagent Systems via Cloud Computing论文复现论文摘要系统参数初始化系统模型观测器预测过程控制器设计系统的整体框图仿真结果 论文摘要 翻译…

杰发科技Bootloader(2)—— 基于7840的Keil配置地址

序 在7840的sample代码里面有一个简单的Boot跳转APP的示例 PFlash地址从0开始 DFlash的地址从1000000开始 Boot解析 他的boot地址配置为0 Boot的代码主要是这几行&#xff0c;主要作用就是Flash的跳转 int main(void) {SystemClock_Config();InitDebug();printf("demo…

NSAT-8000与Chroma8000相比,有什么独特优势?

在电源模块的广泛应用推动下&#xff0c;测试效率成为行业关注的焦点。纳米软件响应这一需求&#xff0c;推出了NSAT-8000电源自动测试系统&#xff0c;其0代码操作模式大幅简化了测试流程。那么与Chroma 8000系统相比&#xff0c;有什么不同呢&#xff1f; 一、测试项目搭建 C…

nacos get changed dataId error, code: 403

nacos get changed dataId error, code: 403问题解决 问题出现原因&#xff1a;解决办法&#xff1a;需要在运行项目的配置添加权限账号和密码,重启服务 问题出现原因&#xff1a; 由于nacosserver开启了权限验证&#xff0c;项目启动时出现异常 nacos.core.auth.caching.ena…

数据结构->线性结构->顺序存储->静态链表

一、思路 链表由节点组成。 1、分析需求&#xff0c;画图&#xff1a; 2、定义学生结构体&#xff0c;包含姓名、年龄、性别和下一个学生的指针&#xff1a; #include <stdio.h> #define N 20// 定义性别枚举类型&#xff0c;固定值&#xff0c;不是男就是女 typedef e…

torchscript接口

一、定义 定义script、eager、onnx 模式对比案例生成的模型可以被c调用接口解读 二、实现 定义 可以在高性能环境libtorch&#xff08;C &#xff09;中直接加载&#xff0c;实现模型推理&#xff0c;而无需Pytorch训练框架依赖无需代码&#xff0c;直接加载模型&#xff0c…

国中水务:果汁能救“水”吗?

喝下汇源果汁有什么&#xff08;“功效”&#xff09;&#xff1f;这家公司最有发言权。 今天我们聊聊——国中水务。 最近&#xff0c;国中水务公告称拟通过收购&#xff0c;间接控股北京汇源&#xff0c;即将把“垂涎已久”的汇源收入囊中。 两家的故事得从几年前说起&#…

学习大数据DAY21 Linux基本指令2

目录 思维导图 搜索查看查找类 find 从指定目录查找文件 head 与 tail 查看行 cat 查看内容 more 查看大内容 grep 过滤查找 history 查看已经执行过的历史命令 wc 统计文件 du 查看空间 管道符号 | 配合命令使用 上机练习 4 解压安装类 zip unzip 压缩解压 tar …

git跨库合并

1、背景 A为开发环境的代码仓库&#xff0c;B为生产环境的代码仓库。A和B之间不能通信。开发人员的本地电脑可以和A、B通信。 目的 上线时&#xff0c;需要将A代码合并B代码。 2、实现 2.1 添加远程仓库 2.1.1 代码方式 在B代码仓库中,将A添加为远程仓库。 git remote …

【保姆级教程】油猴脚本的安装使用

目录 前言 一、油猴简介 1. 核心功能 2. 应用场景 3. 安全性与兼容性 4. 社区生态 二、教学开始&#xff08;嫌麻烦直接目录跳转开始学习&#xff09; 1.插件安装&#xff08;以Microsoft Edge浏览器为例&#xff09; 2.获取脚本 3.大展身手 三、扩展&#xff08;脚…

2024年7月23日(samba DNS)

​ 回顾 1、关闭防火墙&#xff0c;关闭selinux systemctl stop firewalld systemctl disable firewalld setenforce 0 2、修改静态IP地址 vim /etc/sysconfig/network-scripts/ifcfg-ens33 #修改uuid的目的是为了保证网络的唯一性 3、重启网络服务 systemctl restart netwo…

Ansible的脚本-----playbook剧本【上】

目录 1.playbook剧本组成 2.playbook剧本实战演练 2.1 实战演练一&#xff1a;给被管理主机安装httpd服务 2.2 实战演练二&#xff1a;定义、引用变量 2.3 实战演练三&#xff1a;指定远程主机sudo切换用户 2.4 实战演练四&#xff1a;when条件判断 2.5 实战演练五&…

【Matlab 传感器布局优化】基于群智能算法的wsn覆盖优化研究

一 背景介绍 无线传感器网络&#xff08;Wireless Sensor Network, WSN&#xff09;作为远程环境监测系统应用的关键技术&#xff0c;能够在有限的能源供应下提供高效的传感和通信服务。覆盖控制是保证高效通信和可靠数据传输的重要手段。鉴于复杂的物理环境限制了节点部署方式…

文本编辑三巨头(grep)

目录 正则表达式 元字符 grep 案例 我在编写脚本的时候发现&#xff0c;三个文本编辑的命令&#xff08;grep、sed、awk&#xff0c;被称为文本编辑三剑客&#xff0c;我习惯叫它三巨头&#xff09;用的还挺多的&#xff0c;说实话我一开始学的时候也有些懵&#xff0c;主要…

Unity3D之TCP网络通信(客户端)

文章目录 概述TCP核心类异步机制 Unity中创建TCP客户端Unity中其它脚本获取TCP客户端接受到的数据后续改进 本文将以Unity3D应用项目作为客户端去连接制定的服务器为例进行相关说明。 Unity官网参考资料&#xff1a; https://developer.unity.cn/projects/6572ea1bedbc2a001ef…

2024年平面设计软件:六大选择

无论是与营销还是产品设计&#xff0c;平面设计都是当今各个行业不可或缺的一部分。然而&#xff0c;随着平面设计软件的范围和复杂性的不断扩展&#xff0c;设计师和业余用户可能更难知道哪些工具最能满足他们的需求。 本文将分析当今流行的分析 6 个平面设计 App&#xff0c…

mac清理软件哪个好用免费 MacBook电脑清理软件推荐 怎么清理mac

随着使用时间的增长&#xff0c;mac电脑会积累一些不必要的垃圾文件&#xff0c;这些文件会占用宝贵的存储空间&#xff0c;影响电脑的运行速度和稳定性。因此&#xff0c;定期清理mac电脑的垃圾文件是非常有必要的。市场上有许多优秀的Mac清理软件&#xff0c;包括一些出色的国…