《C++文章汇总》
上一篇介绍了引用和汇编《03-Reference、汇编》,本文介绍汇编其他指令、引用和Const。
1.x64汇编要点总结
◼mov dest, src
将src的内容赋值给dest,类似于dest = src
◼[ 地址值 ]
中括号[ ]里面放的都是内存地址
◼word是2字节,dword是4字节(double word),qword是8字节(quad word) ◼call 函数地址
调用函数
◼lea dest, [ 地址值 ]
将地址值赋值给dest,类似于dest = 地址值
◼ret 其实内部也包含jump功能
函数返回
◼xor op1, op2
将op1和op2异或的结果赋值给op1,类似于op1 = op1 ^ op2
◼add op1, op2
类似于op1 = op1 + op2
◼sub op1, op2
类似于op1 = op1 - op2
◼inc op
自增,类似于op = op + 1
◼dec op
自减,类似于op = op – 1
◼jmp 内存地址
跳转到某个内存地址去执行代码 j开头的一般都是跳转,大多数是带条件的跳转,一般跟test、cmp等指令配合使用
◼ 权威参考:Intel白皮书 https://software.intel.com/en-us/articles/intel-sdm
函数的返回值放在eax中
2.Jump Condition Code
01、 | JE, JZ | 结果为零则跳转(相等时跳转) | ZF=1 | ||||||
---|---|---|---|---|---|---|---|---|---|
equal | zero | ||||||||
02、 | JNE, JNZ | 结果不为零则跳转(不相等时跳转) | ZF=0 | ||||||
not equal | not zero | ||||||||
03、 | JS | 结果为负则跳转 | SF=1 | ||||||
sign(有符号\有负号) | |||||||||
04、 | JNS | 结果为非负则跳转 | SF=0 | ||||||
not sign(无符号\无负号) | |||||||||
05、 | JP, JPE | 结果中1的个数为偶数则跳转 | PF=1 | ||||||
parity even | |||||||||
06、 | JNP, JPO | 结果中1的个数为偶数则跳转 | PF=0 | ||||||
parity odd | |||||||||
07、 | JO | 结果溢出了则跳转 | OF=1 | ||||||
overflow | |||||||||
08、 | JNO | 结果没有溢出则跳转 | OF=0 | ||||||
not overflow | |||||||||
09、 | JB, JNAE | 小于则跳转 (无符号数) | CF=1 | ||||||
below | not above equal | < | |||||||
10、 | JNB, JAE | 大于等于则跳转 (无符号数) | CF=0 | ||||||
not below | above equal | >= | |||||||
11、 | JBE, JNA | 小于等于则跳转 (无符号数) | CF=1 or ZF=1 | ||||||
below equal | not above | <= | |||||||
12、 | JNBE, JA | 大于则跳转(无符号数) | CF=0 and ZF=0 | ||||||
not below equal | above | > | |||||||
13、 | JL, JNGE | 小于则跳转 (有符号数) | SF≠ OF | ||||||
little | not great equal | < | |||||||
14、 | JNL, JGE | 大于等于则跳转 (有符号数) | SF=OF | ||||||
not little | great equal | >= | |||||||
15、 | JLE, JNG | 小于等于则跳转 (有符号数) | ZF=1 or SF≠ OF | ||||||
little equal | not great | <= | |||||||
16、 | JNLE, JG | 大于则跳转(有符号数) | ZF=0 and SF=OF | ||||||
not little equal | great | > |
CPU架构决定无法mov 内存到内存,要通过寄存器来进行运算中转
mov带单位,取数据,取多少个字节,lea不带单位,lea赋值内存地址,指针牵扯到取地址值,标记是lea eax,[ebp-0Ch],不能写成mov eax ebp-0Ch,mov指令不能做运算,但可以
sub ebp,0CH//会改掉ebp的值
mov eax,ebp
指针变量赋值
//int age = 3;
//ebp-0Ch是age的地址值
008519C2 mov dword ptr [ebp-0Ch],3
//eax == ebp-0Ch,存放着age的地址值
008519C9 lea eax,[ebp-0Ch]
//ebp-18h是指针变量p的地址值
//将age的地址值存放到指针变量p所在的存储空间
//int *p = &age;
008519CC mov dword ptr [ebp-18h],eax
//*p = 5
//将age的地址值存放到eax
008519CF mov eax,dword ptr [ebp-18h]
//age = 5
008519D2 mov dword ptr [eax],5
指针变量的地址值,另外一个变量的地址值赋值给指针变量去存储
lea eax,[ebp-0Ch]
mov dword ptr [ebp-18h],eax
如下图:引用的内存汇编和指针变量一模一样
3.引用
A.结构体引用
#include <iostream>
using namespace std;
struct Date {
int year;
int month;
int day;
};
int main(int argc, const char * argv[]) {
Date d = {2011,1,5};
Date &ref = d;
ref.year = 2014;
getchar();
return 0;
}
B.指针引用
int main(int argc, const char * argv[]) {
int age = 10;
int *p = &age;
int *&ref = p;
*ref = 30;
int height = 30;
ref = &height;
getchar();
return 0;
}
C.数组引用
int main(int argc, const char * argv[]) {
int array[] = {1,2,3};
int (&arr)[3] = array;
int *p;
//指针数组
int *a[3] = {p,p,p};
//数组指针:指向数组的指针
int (*ar)[3] = &array;
getchar();
return 0;
}
不存在引用的引用,指向引用的指针,引用数组
D.常引用(Const Reference)
◼ 引用可以被const修饰,这样就无法通过引用修改数据了,可以称为常引用
int main(int argc, const char * argv[]) {
int age = 10;
const int &ref = age;
const int *p = &age;
// ref = 30;
// *p = 30;
cout << ref << endl;
cout << *p << endl;
getchar();
return 0;
}
//输出
10
10
◼ const、指针、引用
int main(int argc, const char * argv[]) {
int height = 20;
int age = 10;
//ref1不能修改指向,但是可以通过ref1间接修改指向的变量的值
// int& const ref1 = age;const可以直接去掉,引用本来就不能修改指向
// ref1 = 30;不报错
//ref2不能修改指向,不可以通过ref2间接修改指向的变量的值
int const &ref2 = age;
// ref2 = 30;报错
//p1不能修改指向,可以利用p1间接修改指向的变量
int * const p1 = &age;
*p1 = 40;
//p2可以修改指向,不可以利用p2间接修改指向的变量
int const *p2 = &age;
// *p2 = 50;报错
p2 = &height;
getchar();
return 0;
}
const必须写在&符号的左边,才能算是常引用
◼ const引用的特点
可以指向临时数据(常量、表达式、函数返回值等)
可以指向不同类型的数据
作为函数参数时(此规则也适用于const指针)
✓ 可以接受const和非const实参(非const引用,只能接受非const实参)
✓ 可以跟非const引用构成重载
int func(){
return 8;
}
int sum(int &v1,int &v2){
cout << "sum(int &v1,int &v2)" << endl;
return v1 + v2;
}
int sum(const int &v1,const int &v2){
cout << "sum(const int &v1,const int &v2)" << endl;
return v1 + v2;
}
int main(int argc, const char * argv[]) {
//非const实参
int c = 10;
int d = 20;
sum(c, d);
//const实参
const int e = 10;
const int f = 20;
sum(e, f);
sum(10, 20);
int a = 1;
int b = 2;
const int &ref = 30;
const int &ref0 = a + b;
const int &ref1 = func();
int age = 10;
const double &ref3 = age;
getchar();
return 0;
}
//输出
sum(int &v1,int &v2)
sum(const int &v1,const int &v2)
sum(const int &v1,const int &v2)
◼ 当常引用指向了不同类型的数据时,会产生临时变量,即引用指向的并不是初始化时的那个变量
I.常引用ref指向了相同类型数据
int main() {
int age = 10;
const int &ref = age;
age = 30;
cout << "age is " << age << endl;
cout << "ref is " << ref << endl;
getchar();
return 0;
}
//输出
age is 30
ref is 30
查看汇编,age的值变为了30,ref的值也变为了30
//int age = 10;
mov dword ptr [ebp-0Ch],0Ah
//age的内存地址给到eax
lea eax,[ebp-0Ch]
//[ebp-18h]这段内存空间存放age的内存空间[ebp-0Ch]
mov dword ptr [ebp-18h],eax
//30赋值给[ebp-0Ch]内存地址
mov dword ptr [ebp-0Ch],1Eh
II.常应用ref指向了不同类型数据
#include <iostream>
using namespace std;
int main() {
/*
int age = 10;
const int &ref = age;
age = 30;
cout << "age is " << age << endl;
cout << "ref is " << ref << endl;
*/
int age = 10;
const long &ref = age;
age = 30;
cout << "age is " << age << endl;
cout << "ref is " << ref << endl;
getchar();
return 0;
}
//输出
age is 30
ref is 10
查看汇编,age的值变为了30,而引用ref没有变,在一块新的内存空间中存储
//int age = 10;
mov dword ptr [ebp-0Ch],0Ah
//将10取出来给eax
mov eax,dword ptr [ebp-0Ch]
//将eax给到[ebp-24h]这段内存空间,相当于temp
mov dword ptr [ebp-24h],eax
//将[ebp-24h]这段内存空间 temp给到ecx
lea ecx,[ebp-24h]
//将ecx里面的内存地址的值取出来给到[ebp-18h]
mov dword ptr [ebp-18h],ecx
//将30给到[ebp-0Ch],age就变化了,ref没有变化
mov dword ptr [ebp-0Ch],1Eh
E.不同的编程语言转成的汇编是一样的吗?
Java,C++,OC,Swift写代码--->汇编机器码取决于CPU架构(X86,ARM)。