• 4、【C++基础】引用和指针


    一、C++引用(Reference)

    引用(Reference)是C++语言相对于C语言的又一个扩充,是C++常用的一个重要内容之一。类似于指针,只是在声明的时候用"&"取代了"*"。

    1、引用的相关概念

    引用是别名,在声明时必须初始化,在实际代码中主要用作函数的形参
      (1)&在此不是求地址运算,而是起标识作用。
      (2)类型标识符是指目标变量的类型。
      (3)声明引用时,必须同时对其进行初始化。
      (4)引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,且不能再把该引用名作为其他变量名的别名。
      (5)声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。故:对引用求地址,就是对目标变量求地址。
      (6)不能建立数组的引用。因为数组是一个由若干个元素所组成的集合,所以无法建立一个数组的别名。

    2、引用的应用

    引用可以看做是被引用对象的一个别名,在声明引用时,必须同时对其进行初始化。引用的声明方法如下:

    类型标识符 &引用名 = 被引用对象

    【示例】

    1 int a = 10;
    2 int  &b = a;
    3 cout << a << " " << b << endl;
    4 cout << &a << " " << &b << endl;

    在本例中,变量b就是变量a的引用,程序运行结果如下:

    1      10  10
    2      0x64fe68 0x64fe68

    从这段程序中我们可以看出变量a和变量b都是指向同一地址的,也即变量b是变量a的另一个名字,也可以理解为0x64fe68空间拥有两个名字:a和b。由于引用和原始变量都是指向同一地址的,因此通过引用也可以修改原始变量中所存储的变量值。

    【示例】

    1 int a = 10;
    2 int &b = a;
    3 b = 20;
    4 cout << a << " " << b << endl;

    如果我们不希望通过引用来改变原始变量的值时,我们可以按照如下的方式声明引用:

    const 类型标识符 & 引用名 = 被引用的变量名

    这种引用方式成为常引用。
    【示例】

    1 int a = 10;
    2 const int &b = a;
    3 b = 20;   //compile error
    4 a = 20;

    1) 函数引用参数
      如果我们在声明或定义函数的时候将函数的形参指定为引用,则在调用该函数时会将实参直接传递给形参,而不是将实参的拷贝传递给形参。如此一来,如果在函数体中修改了该参数,则实参的值也会被修改。这跟函数的普通传值调用还是有区别的。

    C函数的参数传递
        按值传递,按值传递如果传递很大的数据项,赋值数据将导致较长的执行时间
    C++函数的参数传递
        按引用传递,避免复制大量数据的开销,可以提高性能
    

     引用和指针的差别
        (1)指针是个变量,可以把它再赋值成指向别处的地址,建立引用时必须进行初始化并且绝不会在关联其他不同的变量,由于指针也是变量,所以可以有指针变量的引用。

    1 int *a = nullptr;
    2 int * &ptr = a;           //表示int*的引用ptr初始化为a
    3 int b = 8;
    4 ptr = &b;                 //ok, ptr是a的别名,是一个指针
    5 void &a = 3;         ---------          注意这是不合法的
    6 //void只是在语法上相当于一个类型,本质上不是类型,但是没有任何一个变量或对象,其类型为void

        (2)不能遍历引用的数组

    1 int a[10] = {0};
    2 int &ra[10]  =  a; //error不能建立一个引用类型的数组

       (3)没有引用的指针和引用的引用

    1 int a;
    2 int &ra = a;
    3 int & *ptr = &ra;      // error企图定义一个引用的指针

        (4)有空指针没有空引用,每一个引用都是有效的
    传递引用给函数与传递指针的效果一样,用引用作为参数比使用指针更有清晰的语法

     1 void swap(int &x, int &y);        // 引用作为参数
     2     
     3 void swap(int &x, int &y)         // 函数实现几乎和原来一样
     4 {
     5     int temp = x;
     6     x = y;
     7     y = temp;
     8 }
     9     
    10 void swap(int *x, int *y)
    11 {
    12      int z = *x;
    13     *x = *y;
    14     *y = z;
    15 }
     1 //函数参数示例
     2 #include <iostream>
     3 using namespace std;
     4 
     5 void  foo(int val)
     6 {
     7     val = 10;
     8 }
     9 
    10 void bar(int &val)
    11 {
    12     val = 10;
    13 }
    14 
    15 void zoo(int *pval)
    16 {
    17     *pval = 10;
    18 }
    19 
    20 int main()
    21 {
    22     int a = 1;
    23     int b = 1;
    24     int c = 1;
    25     foo(a);
    26     bar(b);
    27     zoo(&c);
    28     cout << a << " " << b << " " << c << endl;
    29     
    30     return 0;
    31 }

      使用引用作为参数和返回值给函数的意义,函数只能返回一个值。如果程序需要从函数返回两个值怎么办,解决这个问题的办法之一是引用给函数传递两个参数,然后由函数往目标中填入正确的值,函数返回值时,要生成一个值的副本。而引用返回值时,不生成值的副本,所以提高了效率。

    1 int result = 0;
    2 int  &func(int  r)      // 返回引用
    3 {
    4     result = r * r;
    5     return result;
    6 }

        注意:如果返回不在作用域内的变量或者对象的引用就有问题了。
        这与返回一个局部作用域指针的性质一样严重。

     1 int  &func(int r)
     2 {
     3     int result = 0;
     4     result = r * r;
     5     return result ;             // 返回局部变量的引用
     6 }
     7 
     8 int main()
     9 {
    10     int  &val  =  func(5);     // error返回的引用是个局部变量
    11     return 0;
    12 }

    二、指针

    1 int  p=12;
    2 int *q;
    3 int *q=&p;

    这里p为int类型的变量,&p就是p的内存地址,*p是一个int类型的变量(是一个值),q为指针是地址,int *q=&p;把p的地址赋给了指针q,所以*q就等于p的值=12,而q=&p,因为指针本身也是变量,所以&q就是指针q的内存地址。

    这里有一点要注意:

    1、不能写成int *q=p;会报错int类型的值不能初始化int类型的实体。因为没有给int类型分配内存地址,可以先用new 分配内存然后再赋值,例如:

    int *q=new int;
    *q=p;

    有了内存地址,然后再赋值就不会报错了。当然这里的q就不等同于&p了,是一个新地址。

    还可以写成:

    int *q;
    q=&p;

    反正就是要先分配内存地址,才能赋值。

    【实例】

     1 #include <iostream>
     2 #include <stdlib.h>
     3 using namespace std;
     4 
     5 int main()
     6 {
     7     int a = 6;
     8     int b = 4;
     9     cout << "a = " << a;
    10     cout << " and a address = " << &a << endl;
    11     
    12     int *p_b;
    13     p_b = &b;
    14     cout << "p_b:" << *p_b<<endl;
    15     cout << "*p_b+1:" << *p_b + 1 << endl;
    16 
    17     int d = 1001;
    18     int *pt = new int;
    19     *pt = 1001;
    20     cout << "d:" << d <<" and d adress:"<<&d<< endl;
    21     cout << "*pt:" << *pt << "pt:" << pt <<"&pt:"<<&pt<< endl;
    22     
    23     
    24     int e = 12;
    25     int *p_e=&e;
    26     int *q_e=new int;//给q_e分配新内存
    27     *q_e=e;
    28     cout << "e:" << e << " *q_e:" << *q_e << " q_e:" << q_e << " &q_e:" << &q_e << "&e:" << &e << endl;
    29     cout << "e:" << e << " *p_e:" << *p_e << " p_e:" << p_e << " &p_e:" << &p_e <<  "&e:" << &e << endl;
    30     
    31     e = e + 3;
    32     cout << "&(e+3):" << &e << endl;
    33     system("pause");
    34     return 0;
    35 }
  • 相关阅读:
    Java 在线/离线 文档
    Java集合框架全解
    【LeetCode】204.计数质数
    深入SpringMvc
    SpringMvc基础
    SSM整合
    Spring注解
    SpringAop编程
    2路插入排序
    Matplotlib绘图库简要介绍
  • 原文地址:https://www.cnblogs.com/Long-w/p/8970523.html
Copyright © 2020-2023  润新知