指针和引用

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

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

void funRef(int &ref){
    ref++;
}

int main(){
    //定义int类型变量
    int var = 0x41;
    //int指针变量,初始化为变量var的地址
    int *pnVar = &var;
    //取出指针pcVar指向的地址内容并显示
    char *pcVar = (char*)&var;
    printf("%s",pcVar);

    //引用作为参数,即把var的地址作为参数
    funRef(var);
    
    int age = 10;
    // *p就是age的别名
    int *p = &age;
    *p = 30;
    // ref就是age的别名
    int &ref = age;
    ref = 30;

    return 0;
}

◼ 注意点

  • 引用相当于是变量的别名(基本数据类型、枚举、结构体、类、指针、数组等,都可以有引用)

  • 对引用做计算,就是对引用所指向的变量做计算

int main() {
   //cout << sizeof(Student) << endl;

   int age = 10;

   // *p就是age的别名
   int *p = &age;
   *p = 30;

   // ref就是age的别名
   int &ref = age;
   ref += 30;
   cout<< age << endl;
 }

60

  • 在定义的时候就必须初始化,一旦指向了某个变量,就不可以再改变,“从一而终

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

void test() {
   int age = 10;

   int height = 20;

   // 定义了一个age的引用,ref相当于是age的别名
   int &ref = age;
   int &ref1 = ref;
   int &ref2 = ref1;

   ref += 10;
   ref1 += 10;
   ref2 += 10;

   cout << age << endl;
}

40

◼ 引用存在的价值之一:比指针更安全、函数返回值可以被赋值

比指针更安全

引用在开发中的使用Demo

//void swap(int *v1, int *v2) {
//  int tmp = *v1;
//  *v1 = *v2;
//  *v2 = tmp;
//}
/*
后面还可以再用别的值调换吗?不是说引用“从一而终”吗
*/

void swap(int &v1, int &v2) {
   int tmp = v1;
   v1 = v2;
   v2 = tmp;
}

void test2() {
   int a = 10;
   int b = 20;
   // swap(&a, &b);
   swap(a, b);
   cout << "a = " << a << ", b = " << b << endl;

   int c = 2;
   int d = 3;
   swap(c, d);
   cout << "c = " << c << ", d = " << d << endl;
}

a = 20, b = 10

c = 3, d = 2

double &refD = age;
类型不匹配

引用的本质

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

int age = 10;

// *p就是age的别名
int *p = &age;
*p = 30;

// ref就是age的别名
int &ref = age;
ref += 30;
cout << sizeof(p) << endl;
cout << sizeof(ref) << endl;

8 指针的sizeof 大小和运行环境有关系,我现在用的macos 64位编译,所以一个指针大小为 8
4 这里为什么输出来为4 其实输出的是sizeof(int) 的大小 int在C++中,4位大小

◼ 一个引用占用一个指针的大小

struct Student {
    int age;
};

cout << sizeof(Student) << endl;

4

struct Student {
    int *age;
};
cout << sizeof(Student) << endl;

8

struct Student {
   int &age;
};
cout << sizeof(Student) << endl;

8

侧面证明了一个引用占用一个指针的大小

使用汇编来窥探引用的本质

汇编代码
_p$ = -12                                         ; size = 4
_ref$ = -8                                          ; size = 4
_age$ = -4                                          ; size = 4
_main   PROC
        push    ebp
        mov     ebp, esp
        sub     esp, 12                             ; 0000000cH
        mov     DWORD PTR _age$[ebp], 10      ; 0000000aH
        lea     eax, DWORD PTR _age$[ebp]
        mov     DWORD PTR _p$[ebp], eax
        mov     ecx, DWORD PTR _p$[ebp]
        mov     DWORD PTR [ecx], 30                 ; 0000001eH
        lea     edx, DWORD PTR _age$[ebp]
        mov     DWORD PTR _ref$[ebp], edx
        mov     eax, DWORD PTR _ref$[ebp]
        mov     ecx, DWORD PTR [eax]
        add     ecx, 30                             ; 0000001eH
        mov     edx, DWORD PTR _ref$[ebp]
        mov     DWORD PTR [edx], ecx
        xor     eax, eax
        mov     esp, ebp
        pop     ebp
        ret     0
_main   ENDP
汇编代码

从汇编语言来看,引用和指针生成的汇编代码一致,所以说 引用的本质就是指针

看一下指针地址赋值的过程

◼ lea dest, [ 地址值 ]

  • 将地址值赋值给dest,类似于dest = 地址值
int main(){
    int age = 3;
    // *p就是age的别名
    int *p = &age;
    *p = 5;
}
指针地址赋值
// eax == ebp-0Ch,存放着age的地址值
008519C9  lea         eax,[ebp-0Ch]  

// ebp-18h是指针变量p的地址值
// 将age的地址值存放到指针变量p所在的存储空间
// int *p = &age;
008519CC  mov         dword ptr [ebp-18h],eax  

lea 是没有单位的,move设计到把地址的值取出来,所以是需要单位的 dword 表示4个字节

所以看指针汇编的标志性代码是 lea

引用地址赋值

再看把指针修改为引用 ,汇编代码完全一样, 所以说引用的本质就是指针

引用相当于是变量的别名(基本数据类型、枚举、结构体、类、指针、数组等,都可以有引用

  • 结构体的引用
//结构体也可以是引用类型
struct Date {
   int year;
   int month;
   int day;
};

Date d = {2011, 1, 5};

Date &ref = d;
ref.day = 2014;
  • 指针的引用
int age = 10;
int *p = &age;
int *&ref = p;
*ref = 30;
  • 数组的引用
int array[] = {1, 2, 3};
int (&ref)[3] = array;
int *p;
    // 指针数组,数组里面可以存放3个int *
    int *arr1[3] = {p, p, p};
// 用于指向数组的指针
int (*arr2)[3];//数组指针
int (&ref)[3] = array;
int (*arr2)[3];

不存在【引用的引用、指向引用的指针、引用数组】

引用的引用

常引用(Const Reference)

◼ 引用可以被const修饰,这样就无法通过引用修改数据了,可以称为常引用

  • const必须写在&符号的左边,才能算是常引用
  1. 常量指针是指指向常量的指针,顾名思义,就是指针指向的是常量,即,它不能指向变量,它指向的内容不能被改变,不能通过指针来修改它指向的内容,但是指针自身不是常量,它自身的值可以改变, 从而指向另一个常量
  2. 指针常量是指指针本身是常量。它指向的地址是不可改变的,但地址里的内容可以通过指针改变。它指向的地址将伴其一生,直到生命周期结束。有一点需要注意的是,指针常量在定义时必须同时赋初值。
int height = 20;
int age = 10;
// p2可以修改指向,不可以利用p2间接修改所指向的变量
int const *p2 = &age;
p2 = &height;
// *p2 = 30;
// ref1不能修改指向,但是可以通过ref1间接修改所指向的变量
const int &ref1 = age;
const int *p = &age
//ref1 = 30;
//*p = 30;
  • 'const' qualifier may not be applied to a reference
int & const ref3 = age;
image-20210327195723638

加深记忆记住三句话:

指针和 const 谁在前先读谁 ;

*象征着地址,const象征着内容;

谁在前面谁就不允许改变。例如 int const p1 = &b 常量指针 const在前,所以内容不能修改,即 (p1 = 30)错误

int const p2 = &c 指针常量 (指针)在前,所以指针**不能修改 (p2=&d)错误

◼ const引用的特点

  • const引用可以指向临时数据(常量、表达式、函数返回值等)

Non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'

image-20210327201338747
const int &refError = 30;
int func() {
   return 8;
}
const int &ref = func();//函数返回值
  • const引用可以指向不同类型的数据

Non-const lvalue reference to type 'double' cannot bind to a value of unrelated type 'int'

image-20210327201748830
int age = 10;
const double &ref = age;
  • const引用作为函数参数时(此规则也适用于const指针)

✓ 可以接受const和非const实参(非const引用,只能接受非const实参)

✓ 可以跟非const引用构成重载

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

void test2() {
   // 非const实参
   int a = 10;
   int b = 20;
   sum(a, b);

   // const实参
   const int c = 10;
   const int d = 20;
   sum(c, d);

   sum(10, 20);
}

sum(int &v1, int &v2)
sum(const int &v1, const int &v2)
sum(const int &v1, const int &v2)

◼ 当常引用指向了不同类型的数据时,会产生临时变量,即引用指向的并不是初始化时的那个变量

int age = 10;

const int &rage = age;

age = 30;

cout << "age is "<< age << endl;
cout << "rage is "<< rage << endl;

age is 30
rage is 30

int age = 10;

const long &rage = age;

age = 30;

cout << "age is "<< age << endl;
cout << "rage is "<< rage << endl;

age is 30
rage is 10

汇编代码
_rage$ = -12                                            ; size = 4
_$S1$ = -8                                          ; size = 4
_age$ = -4                                          ; size = 4
_main   PROC
        push    ebp
        mov     ebp, esp
        sub     esp, 12                             ; 0000000cH
        mov     DWORD PTR _age$[ebp], 10      ; 0000000aH
        mov     eax, DWORD PTR _age$[ebp]
        mov     DWORD PTR _$S1$[ebp], eax
        lea     ecx, DWORD PTR _$S1$[ebp]
        mov     DWORD PTR _rage$[ebp], ecx
        mov     DWORD PTR _age$[ebp], 30      ; 0000001eH
        xor     eax, eax
        mov     esp, ebp
        pop     ebp
        ret     0
_main   ENDP

产生了临时变量 _$S1$

mov eax, DWORD PTR _age$[ebp] 把age的值放入寄存器 eax

mov DWORD PTR _$S1$[ebp], eax eax的值放入_$S1所在的内存地址

lea ecx, DWORD PTR _$S1$[ebp]把$S1的内存地址给ecx

mov DWORD PTR _rage$[ebp], ecx把ecx存的值放入_rage地址中

从这段汇编代码可以看出 常引用指向了不同类型的数据时,会产生临时变量

数组的引用

◼ 常见的2种写法

   // 数组名arr其实是数组的地址,也是数组首元素的地址
   // 数组名arr可以看做是指向数组首元素的指针(int *)
int arr[] = {1, 2, 3};
int (&ref)[3] = arr;
int * const &ref2 = arr;
  • 数组名arr可以看做是指向数组首元素的指针(int *)
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,386评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,142评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,704评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,702评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,716评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,573评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,314评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,230评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,680评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,873评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,991评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,706评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,329评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,910评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,038评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,158评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,941评论 2 355

推荐阅读更多精彩内容