JHHK

欢迎来到我的个人网站
行者常至 为者常成

4、引用的本质

目录

引用

在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
    }
}

行者常至,为者常成!





R
Valine - A simple comment system based on Leancloud.