(C/C++学习笔记) 十三. 引用

十三. 引用

● 基本概念

引用: 就相当于为变量起了一个别名(alias), △与指针不同的是它不是一个数据类型

通过引用我们可以间接访问变量,指针也能间接访问变量,但引用在使用上相对指针更安全。

因为: ① 指针可以为空值,而引用一旦创建就必须初始化;

     ② 与指针相比,引用不占用新的地址,节省内存开销,而且隐去了地址操作。引用封锁了对地址的可修改性,使得间接访问操作更安全了。指         针是低级的直接操作内存地址的机制,其功能强大但使用不慎极易产生错误。

③ 在C++语言中,指针可由整型数强制类型转换得到,处理不当就可能对系统造成极大的破坏。

 

引用的主要用途是为了描述函数的参数和返回值,特别是为了传递较大的数据变量。

对引用型变量的操作实际上就是对被引用变量的操作。当定义一个引用型变量时,需要用已存在的变量对其初始化,于是引用就被绑定在那个变量上,对于引用的改动就是对它所绑定的变量的改动,反之亦然。

 

定义一个引用型变量的语法格式为:

数据类型  & 引用变量名 = 变量名; 

 

(int&)a 等价于 *((int*)&a)

#include <iostream>

using namespace std;

void main()

{

    int a=100;

    int & ref_a =a;

    cout << "a= "<< a <<endl;

    cout << "ref_a="<< ref_a << endl;

    a=2;

    cout << "a= "<< a <<endl;

    cout << "ref_a="<< ref_a << endl;

    int b=20;

    ref_a=b;

    cout << "a= "<< a <<endl;

    cout << "ref_a="<< ref_a << endl;

    ref_a--;

    cout << "a= "<< a <<endl;

    cout << "ref_a="<< ref_a << endl;

}

当定义一个引用变量后,系统并没有为它分配内存空间refx与被引用变量x具有相同的地址,即refxx使用的是同一内存空间。对引用变量值的修改就是对被引用变量的修改,反之亦然。

例如:

    x=3;

    cout<<refx; //结果为3

    refx=5;

    cout<<x; //结果为5

 

● 引用与函数

1. 引用作为函数的参数

      当引用作为函数的形参,在进行函数调用时,进行实参与形参的结合,其结合过程相当于定义了一个形参对实参的引用。因此,在函数体内,对形参进行运算相当于对实参进行运算。

 

 

与指针相比,引用作为函数参数具有两个优点

    ① 函数体的实现比指针简单。用指针作为形参,函数体内形参要带着*参加运算;而用引用作为形参,函数体内参加运算的为形参变量。

    ② 调用函数语法简单。用指针作为形参,实参需要取变量的地址;而用引用作为形参,与简单传值调用一样,实参为变量。

2. 引用作为函数的返回值

1. 如果函数返回值类型为引用型,在函数调用时,① 若接受返回值的是一个引用变量,相当于定义了一个对返回变量的引用。②若接受返回值的是一个非引用变量,函数返回变量的值赋给接受变量。

2. 如果函数返回值类型为引用型,则要求返回值为左值。这样,函数调用式可以当作左值。

#include <iostream>

using namespace std;

int max1(int a[],int n) //求数组a[ ]中元素的最大值

{

    int t=0;

    for(int i=0;i<n;i++)

    if(a[i]>a[t]) t=i;

    return a[t]+0;

}

int& max2(int a[],int n) //求数组a[]中元素的最大值

{

    int t=0;

    for(int i=0;i<n;i++)

    if(a[i]>a[t]) t=i;

    return a[t];

}

int& sum(int a[],int n) //求数组a[]中元素的和

{

    int s=0;

    for(int i=0;i<n;i++)

    s+=a[i];

    return s;

}

 

int main()

{

    int a[10]={1,2,3,4,5,6,7,8,9,10};

    int m1=max1(a,10);

    int m2=max2(a,10);    //int型变量接受函数返回的int &型值

    int &m3=max2(a,10);

    int &m4=sum(a,10);

    cout<<"m1="<<m1<<endl;

    cout<<"m2="<<m2<<endl;

    cout<<"m3="<<m3<<endl;

    cout<<"m4="<<m4<<endl;

    m3+=10;        //通过引用变量修改返回变量的

    max2(a,10)-=100;

    cout<<sum(a,10)<<endl;

    return 0;

}

 

 

&的引用和取地址的两种不同运用

&作为引用的时候必须在定义时候就进行初始化, 例如:

int N;

int &rN = N;

若不进行初始化则会编译报错。

引用跟指针的一个重要区别就是引用一对一,绑定一个对象(地址);指针则不然,可以一对多。

 

&作为取地址用的时候要跟指针联系在一起,因为指针是用来存放地址的。取地址就是你想取某变量(形参或者实参)的地址就用&前置表示要取地址了,取的是该变量的内存地址, 例如:

int N = 100;

int *p;

p = &N;

 

int array[10] = {0};

p = &array[0];

 

● 常引用

相关知识:

1. int i(); int i=任意数,有什么区别?

如果只写int i();的话,编译器会认为这是一个名为i,不带参数,返回int的函数的声明。要初始化的话必须加个数字或者用等号。

两种初始化方式理论上不一样,int i(n);这种是直接初始化,给i分配内存的时候就直接将这个内存的内容初始化为n的值,而int i=n;这样是复制初始化,先给i分配个内存,然后判断字面值n的类型,然后分配个内存,构造一个该类型的临时变量,然后将n复制到i

实际上对普通的变量而言这两种方式的运行代价差异几乎可以不考虑,但是对于类类型,就必须考虑一些构造、析构之类的问题。

 

2. 字面值: 常量(constant)可以理解为所谓的字面值, 常量和用const修饰的变量并不是一个概念。

字面值常量(literal constant),"字面值"是指只能用它的值来称呼它,"常量"是指其值不能修改。每个字面值都有相应的类型,3.14double型,2int型。只有内置类型(built-in type)有字面值。

例如:

int a = 1234;

那么语句中的int就是数据类型,a是变量,1234是字面值。int的字面值也就是可以用来初始化int类型变量的东西。

常引用:

如果在定义引用变量时用const修饰,被定义的引用就是常引用。

定义常引用格式如下:

const 数据类型& 引用变量 = 变量名;

定义一个常引用后,就不能通过常引用更改引用的变量的值。

#include <iostream>

using namespace std;

 

main()

{

    int i (100);

    const int & r = i;    

    //r=200;

    //cout<<i<<endl; //编译不通过, 因为定义一个常引用后,就不能通过常引用更改引用的变量的值。

    i=200;

    cout<<i<<endl;

    cout<<r<<endl; //两个结果都为200, 即通过i本身可以改变i的值, 并且r的值也变成了200

}

常引用类型常用作函数的形参类型,把形参定义为常引用类型时,这样在函数体内就不能通过形参改变实参的值,保证了函数调用时实参是"安全"的。这样的形参称为只读形参。

void fun(const int& x, int& y)

{

x=y; // 错误,x不可修改

y=x;

}

△ 形参为常引用类型时,实参可以是常量、变量表达式;但如果为非常引用类型时,实参必须为左值。对void fun(const int& x, int& y), 调用fun(100,200)是错误的, 调用fun(100,a)是正确的(a为变量)

 

● 指针的引用

int*&p 是 指针的引用。 它是一个 指针 的 别名 ,一般可以当成 指针 使用。有时候,可以直接他的值,成为其他指针的引用

int&*p 是 引用的指针,这个是非法的,指针不能指向引用。引用不具有确定的存储,无法间接访问得到表示引用的存储的左值,所以干脆人为规定禁止构造指向引用的指针类型。

 

● 数组作为函数参数

//如果形参是个数组名,那么C/C++语言把这个数组当做指针变量

//如果形参是一维数组, 在定义函数时, 下面三种表达是等价的:

void func1(int array[10])        

void func1(int array[])

void func1(int *array)        //说明传给函数func1的实参应该是个指向整型变量的指针变量

//如果形参是二维数组, 下面三种表达是正确的

void func2(int array[2][3])

void func2(int array[][3])

void func2(int (*array)[3])

//数组元素作为函数参数

#include<stdio.h>

 

void show_member(int member);        /*声明函数*/

 

int main()

{

    int count[10];                    /*定义一个整型的数组*/

    int i;                            /*定义整型变量,用于循环*/

 

    for(i=0;i<10;i++)                /*进行赋值循环*/

    {

        count[i]=i;    

    }

 

 

    for(i=0;i<10;i++)                /*循环操作*/

    {

        show_member(count[i]);        /*将数组元素作为函数参数, 执行输出函数操作*/

    }

    return 0;

}

 

void show_member(int member)        /*函数定义*/

{

    printf("the member is %d ",member);        /*输出数据*/

}

 

#include<stdio.h>

 

void evaluate(int array_name[10]);        /*声明赋值函数*/

void display(int array_name[10]);        /*声明显示函数*/

 

int main()

{

    int array[10];        /*定义一个具有10个元素的整型数组*/

 

    evaluate(array);    /*调用函数进行赋值操作,将数组名作为参数; 参数不能是array[10], 否则就越界了*/

    display(array);    /*调用函数进行赋值操作,将数组名作为参数*/

    return 0;

}

 

void evaluate(int array_name[10])    //进行数组元素的赋值

{

    int i;

    for(i=0;i<10;i++)

    {

        array_name[i]=i;

    }

}

 

void display(int array_name[10])    //数组元素的显示

{

    int i;

    for(i=0;i<10;i++)

    {

        printf("the member number is %d ",array_name[i]);

    }

}

 

 

● 引用的规则

1)引用被创建的同时必须被初始化(指针则可以在任何时候被初始化)。

2)不能有NULL引用,引用必须与合法的存储单元关联(指针则可以是NULL)。

3)一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象)。

 

● 引用和指针的异同

相同点:

★都是地址的概念; 指针指向一块内存,它的内容是所指内存的地址;而引用则是某块内存的 别名。

不同点:

★指针是一个实体,而引用仅是个别名;

★引用只能在定义时被初始化一次,之后不可变;指针可变;引用"从一而终",指针可以"见异思迁";

★引用不能为空(NULL),指针可以为空;

★"sizeof 引用"得到的是所指向的变量(对象)的大小,而"sizeof 指针"得到的是指针本身的大小; ●指针和引用的自增(++)运算意义不一样;

★引用是类型安全的,而指针不是 (引用比指针多了类型检查

 

原文地址:https://www.cnblogs.com/ArrozZhu/p/8377928.html