(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游戏编程>书上光灯一节的一个例子改编的. 从这个例子可以学习到二次曲面的参数设置,程序中提供了两个画球的函数,一个是用三角形画出来的,一个是二次曲面构成的. 你 ...
随机推荐
- react 为元素添加自定义事件监听器
https://zhenyong.github.io/react/tips/dom-event-listeners.html class MovieItem extends React.Compone ...
- 网络量化——Quantized Convolutional Neural Networks for Mobile Devices
论文地址:https://arxiv.org/abs/1512.06473 源码地址:https://github.com/jiaxiang-wu/quantized-cnn 1. 主要思想 这篇文章 ...
- hdoj上的一题和程序设计第二次作业的拓展-人见人爱a+b
hdoj上一道有意思的题目,题目: 人见人爱a+b 敲的也蛮快的,大概十分钟左右就AC了.代码如下: 人见人爱a+b #include<stdio.h> int main() { int ...
- Java 文件夹递归遍历
import java.io.File; public class Demo1 { public static void main(String[] args) { File dir=new File ...
- Java中的RTTI
RTTI可以帮助我们在运行时识别对象和类的信息. 一般传统的RTTI有三种实现方式: 1. 向上转型或向下转型(upcasting and downcasting),在java中,向下转型(父类转成子 ...
- Windows上玩转TensorFlow(一)
Windows上TensorFlow的安装和环境搭建: 1.安装Python 3.5.2 2.通过Pip3安装TensorFlow CPU版 https://www.tensorflow.org/in ...
- STL_iterator返回值
1. iterator的类型 有 单向的/双向的/可以随意移动的... 2. 一些 容器/算法 的返回值 是 iterator类型的,如何确定 返回的 iterator是什么类型的? 3.
- grafana 批量添加图表
利用grafana做监控展示特别方便,而且界面还很有科技感,一般的使用都是自己手动添加图表,或者使用别人提供好的模板. 在一种情况下就比较尴尬了,我有100个实例的内存数据想展示,如果都放在一个tab ...
- 96D - Police Stations
96D - Police Stations 思路:bfs,从所有的警察局开始bfs,因为bfs的深度一样,而且题目给的树保证满足条件,所以不用考虑深度. 如果搜索到一个点a,他的下一个点b已经被搜索过 ...
- Vue.js 响应式原理
1. Vue2.x 基于 Object.defineProperty 方法实现响应式(Vue3 将采用 Proxy) Object.defineProperty(obj, prop, descript ...




