(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具有相同的地址,即refx与x使用的是同一内存空间。对引用变量值的修改就是对被引用变量的修改,反之亦然。 例如: 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修饰的变量并不是一个概念。 是int型。只有内置类型(built-in type)有字面值。 例如: 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; 。 } |
常引用类型常用作函数的形参类型,把形参定义为常引用类型时,这样在函数体内就不能通过形参改变实参的值,保证了函数调用时实参是"安全"的。这样的形参称为只读形参。 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\n",member); /*输出数据*/ } |
#include<stdio.h> void evaluate(int array_name[10]); /*声明赋值函数*/ void display(int array_name[10]); /*声明显示函数*/ int main() { 个元素的整型数组*/ 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\n",array_name[i]); } } |
● 引用的规则
)引用被创建的同时必须被初始化(指针则可以在任何时候被初始化)。 )不能有NULL引用,引用必须与合法的存储单元关联(指针则可以是NULL)。 )一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象)。 |
● 引用和指针的异同
相同点: ★都是地址的概念; 指针指向一块内存,它的内容是所指内存的地址;而引用则是某块内存的 别名。 不同点: ★指针是一个实体,而引用仅是个别名; ★引用只能在定义时被初始化一次,之后不可变;指针可变;引用"从一而终",指针可以"见异思迁"; ★引用不能为空(NULL),指针可以为空; ★"sizeof 引用"得到的是所指向的变量(对象)的大小,而"sizeof 指针"得到的是指针本身的大小; ●指针和引用的自增(++)运算意义不一样; ★引用是类型安全的,而指针不是 (引用比指针多了类型检查 |
(C/C++学习笔记) 十三. 引用的更多相关文章
- java之jvm学习笔记十三(jvm基本结构)
java之jvm学习笔记十三(jvm基本结构) 这一节,主要来学习jvm的基本结构,也就是概述.说是概述,内容很多,而且概念量也很大,不过关于概念方面,你不用担心,我完全有信心,让概念在你的脑子里变成 ...
- python3.4学习笔记(十三) 网络爬虫实例代码,使用pyspider抓取多牛投资吧里面的文章信息,抓取政府网新闻内容
python3.4学习笔记(十三) 网络爬虫实例代码,使用pyspider抓取多牛投资吧里面的文章信息PySpider:一个国人编写的强大的网络爬虫系统并带有强大的WebUI,采用Python语言编写 ...
- Go语言学习笔记十三: Map集合
Go语言学习笔记十三: Map集合 Map在每种语言中基本都有,Java中是属于集合类Map,其包括HashMap, TreeMap等.而Python语言直接就属于一种类型,写法上比Java还简单. ...
- Vue学习笔记十三:Vue+Bootstrap+vue-resource从接口获取数据库数据
目录 前言 SpringBoot提供后端接口 Entity类 JPA操作接口 配置文件 数据库表自动映射,添加数据 写提供数据的接口 跨域问题 前端修改 效果图 待续 前言 Vue学习笔记九的列表案例 ...
- java jvm学习笔记十三(jvm基本结构)
欢迎装载请说明出处:http://blog.csdn.net/yfqnihao 这一节,主要来学习jvm的基本结构,也就是概述.说是概述,内容很多,而且概念量也很大,不过关于概念方面,你不用担心,我完 ...
- python 学习笔记十三 JQuery(进阶篇)
jQuery 是一个 JavaScript 库. jQuery 极大地简化了 JavaScript 编程. 安装jQuery 有两个版本的 jQuery 可供下载: Production versio ...
- JavaScript权威设计--Window对象(简要学习笔记十三)
1.Window对象是所有客户端JavaScript特性和API的主要接入点. Window对象中的一个重要属性是document,它引用Document对象. JavaScript程序可以通过Doc ...
- Oracle学习笔记十三 触发器
简介 触发器是当特定事件出现时自动执行的存储过程,特定事件可以是执行更新的DML语句和DDL语句,触发器不能被显式调用. 触发器的功能: 1.自动生成数据 2.自定义复杂的安全权限 3.提供审计和 ...
- SharpGL学习笔记(十三) 光源例子:环绕二次曲面球体的光源
这是根据徐明亮<OpenGL游戏编程>书上光灯一节的一个例子改编的. 从这个例子可以学习到二次曲面的参数设置,程序中提供了两个画球的函数,一个是用三角形画出来的,一个是二次曲面构成的. 你 ...
随机推荐
- 分布式系统一致性协议--2PC,3PC
分布式系统中最重要的一块,一致性协议,其中就包括了大名鼎鼎的Paxos算法. 2PC与3PC 在分布式系统中,每一个机器节点虽然能够明确知道自己在进行事务操作过程中的结果是成功或是失败,但是却无法直接 ...
- JavaScript:正则表达式 分组2
继续上一篇的写,这篇复杂点. 分组+范围 var reg=/([abcd]bc)/g; var str="abcd bbcd cbcd dbcd"; console.log(str ...
- [BZOJ4244]邮戳拉力赛
Description IOI铁路是由N+2个站点构成的直线线路.这条线路的车站从某一端的车站开始顺次标号为0...N+1. 这条路线上行驶的电车分为上行电车和下行电车两种,上行电车沿编号增大方向行驶 ...
- mysql-5.7.20-winx64.zip Zip版、解压版MySQL安装
1. zip下载地址: https://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-5.7.20-winx64.zip 2.官方文档位置: http:// ...
- 【Tomcat部署】Linux环境部署war包到tomcat
以turbine为例. 一.部署 1.下载或者生成war包(从maven上下载war包,并改名字为turbine.war) 2.将turbine.war拷贝到$TOMCAT_HOME/webapps中 ...
- 学习mybatis-3 step by step 篇三
动态 SQL if choose (when, otherwise) trim (where, set) foreach 动态 SQL 通常要做的事情是有条件地包含 where 子句的一部分.比如: ...
- C#中正确使用enum做Key的姿势
C#中自定义enum,然后将其作为Dictionary的Key,通常的做法如下: using System; using System.Text; using System.Collections.G ...
- C#对config配置文件的管理
应用程序配置文件,对于asp.net是 web.config,对于WINFORM程序是App.Config(ExeName.exe.config). 配置文件,对于程序本身来说,就是基础和依据,其本质 ...
- pip 源 替换国内源
网上收集来的pip源地址: 阿里云 http://mirrors.aliyun.com/pypi/simple/中国科技大学 https://pypi.mirrors.ustc.edu.cn/simp ...
- android------2018 年初值得关注的 16 个新 Android 库和项目
1. transitioner Transitioner 是一个为两个拥有嵌入子视图的视图之间提供简便.动态且可调整的动画效果的库.它纯 100% 使用 Kotlin 编写而成,使用 MIT 许可,且 ...