目录
引用
在C语言中,使用指针(Pointer)可以间接获取、修改某个变量的值
在C++中,使用引用(Reference)可以起到跟指针类似的功能
注意点
1、引用相当于是变量的别名(基本数据类型、枚举、结构体、类、指针、数组等,都可以有引用)
对引用做计算,就是对引用所指向的变量做计算
2、在定义的时候就必须初始化,一旦指向了某个变量,就不可以再改变,“从一而终”
3、可以利用引用初始化另一个引用,相当于某个变量的多个别名
不存在【引用的引用、指向引用的指针、引用数组】
4、引用存在的价值之一:比指针更安全、函数返回值可以被赋值
void referenceUse(void){
int age = 10;
int &rage = age;//在定义时必须初始化,rage是age的别名
rage = 20;//修改rage相当于修改了age
cout<<"age = "<<age<<endl;
cout<<"rage = "<<rage<<endl;
int &rage2 = rage;//rage2,相当于age的另一个别名
rage2 = 30;//修改rage2相当于修改了age rage.
cout<<"age = "<<age<<endl;
cout<<"rage = "<<rage<<endl;
cout<<"rage2 = "<<rage2<<endl;
int height = 180;
rage2 = height;//rage2不是height的引用,而是将height的值赋值给rage2
cout<<"age = "<<age<<endl;
cout<<"rage = "<<rage<<endl;
cout<<"rage2 = "<<rage2<<endl;
}
引用与指针
void swapByPointer(int *a,int *b){
//把变量地址传递进来,通过操作地址值改变其内部存储的值
int temp = *a;
*a = *b;
*b = temp;
}
void swapByReference(int &a,int &b){
//通过引用交换两个变量的值,比指针更方便
int temp = a;
a = b;
b = temp;
}
void referenceAndPointer(void){
int a = 10;
int b = 20;
swapByPointer(&a, &b);
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
int a1 = 11;
int b1 = 21;
swapByReference(a1, b1);
cout<<"a1 = "<< a1 <<endl;
cout<<"b1 = "<< b1 <<endl;
}
引用的本质
引用的本质就是指针,只是编译器削弱了它的功能,所以引用就是弱化了的指针
指针的大小:
在32bit系统中一个指针占用4个字节
在64bit系统中一个指针占用8个字节
32bit系统的最大寻址空间为 0xff ff ff ff 所以指针的大小为4个字节
那么引用的本质是指针,我们看下引用占用多少个字节
struct Person {
int &age;
};
void referenceSize(void){
int age = 10;
int * ageP = &age;
//8 指针占用8个字节
cout<< "sizeof(ageP)="<<sizeof(ageP)<<endl;
int &rage = age;
//4 但此处打印的不是引用的大小,而是age的大小,因为rage是age的别名
cout<< "sizeof(rage) = "<<sizeof(rage)<<endl;
//8 &rage是一个地址,也就是指针,所以大小是8
cout<<"sizeof(&rage) = "<<sizeof(&rage)<<endl;
//那么如何获取引用占用空间的大小呢,我们通过结构体来间接获取
//8 可以看到引用的大小也8,与指针占用的空间是一致的。从侧面说明引用就是指针,只不过是弱化了的指针
cout <<"sizeof(Person) = "<<sizeof(Person)<<endl;
}
void referenceNature(void){
{
int age = 10;
int & rage = age;
/**
LCClientDemo`referenceNature:
0x100005610 <+0>: pushq %rbp
0x100005611 <+1>: movq %rsp, %rbp
0x100005614 <+4>: movl $0xa, -0x4(%rbp)
-> 0x10000561b <+11>: leaq -0x4(%rbp), %rax
0x10000561f <+15>: movq %rax, -0x10(%rbp)
0x100005623 <+19>: popq %rbp
0x100005624 <+20>: retq
*/
}
{
int age = 10;
int * page = &age;
/**
0x100005610 <+0>: pushq %rbp
0x100005611 <+1>: movq %rsp, %rbp
-> 0x100005614 <+4>: movl $0xa, -0x4(%rbp)
0x10000561b <+11>: leaq -0x4(%rbp), %rax
0x10000561f <+15>: movq %rax, -0x10(%rbp)
0x100005623 <+19>: popq %rbp
0x100005624 <+20>: retq
*/
}
/**
从上面的对比可以看出,引用生成的汇编代码与指针生成的汇编代码是一样的,说明引用的本质就是指针
*/
}
寄存器
void registerCheck(void){
__asm{
mov eax,0x0a
mov rax,0xffffff
}
/**
eax 寄存器的值是 0x0a
当对rax赋值 0xffff时,eax寄存器的值也被修改
(lldb) register read ax
ax = 0xffff
(lldb) register read eax
eax = 0x00ffffff
(lldb) register read rax
rax = 0x0000000000ffffff
(lldb)
*/
//通用寄存器
//RAX EAX AX
//RBX EBX BX
//RCX ECX CX
//RDX EDX DX
//RBP EBP BP
//RSP ESP SP
/**
|63..32|31..16|15-8|7-0|
| AH | AL|
|AX......|
|EAX............|
|RAX...................|
一般的规律:
R开头的寄存器是64bit的,占8字节
E开头的寄存器是32bit的,占4字节
*/
}
汇编指令
1、
mov dest, src
将src的内容赋值给dest,类似于dest = src
2、
[ 地址值 ]
中括号[ ]里面放的都是内存地址
3、
word是2字节,dword是4字节(double word),qword是8字节(quad word)
4、
call 函数地址
调用函数
5、
lea dest, [ 地址值 ] load effect address
将地址值赋值给dest,类似于dest = 地址值 相当于 mov dest, 地址值
6、
mov dest,dword ptr [地址值]
将地址内的内容取出赋值给dest
7、
ret
函数返回
8、
jmp 内存地址
跳转到某个内存地址去执行代码
j开头的一般都是跳转,大多数是带条件的跳转,一般跟test、cmp等指令配合使用
9、
jne jump not equal 通常与cmp在一块使用
cmp eax,dword ptr [ebp-14h]
jne 01202683
cmp 比较结果不相等 跳转到 地址01202683 执行指令
1、
add op1, op2
类似于op1 = op1 + op2
2、
sub op1, op2
类似于op1 = op1 - op2
3、
inc op
自增,类似于op = op + 1
4、
dec op
自减,类似于op = op – 1
5、
xor op1, op2
将op1和op2异或的结果赋值给op1,类似于op1 = op1 ^ op2
变量地址:一个变量的地址值,是它所有字节地址中的最小的那个地址值
void assemblyInstruct(void){
__asm{
mov eax,0xa
mov ebx,eax
add eax,0xa
}
}
行者常至,为者常成!