C++基础——引用和指针的区别

C++引用和指针

使用引用之前,需要了解什么是变量,变量的定义,当你申明一个变量的时候,计算机会将指定的一块内存空间和变量名进行绑定;这个定义很简单,例如:int x = 5,其本质是将值5赋值到一块内存空间,而这个内存空间名叫做x。切记:x只是简单的一个别名而已,x不等于任何值;变量在内存中的操作其实是需要经过2个步骤的:

  1. 找出与变量名相对应的内存地址。
  2. 根据找到的地址,取出该地址对应的内存空间里面的值进行操作。

C++引用

引用一般被称为变量的别名,定义的时候必须初始化绑定一个指定的对象,且中途不可更改绑点;一旦把引用初始化为某个变量,就可以使用 引用名称 或者 变量名称 来指向变量。

引用的创建

在如下声明中“ int &ri = i; ”,& 读作引用;因此,声明可以读作 "ri 是一个初始化为 i 的整型引用":

int main() 
{
	int i = 100;
	int &ri = i;
	cout << "reference ri = " << ri << endl;

	return 0;
}

引用的实现原理

如下测试程序:

#include <iostream>
using namespace std;

struct A
{
	char& m;
};

struct B
{
	int *p;
};


int main() 
{
	cout << "sizeof(A) = " << sizeof(A) << endl;
	cout << "sizeof(B) = " << sizeof(B) << endl;

	return 0;
}

可以看到两个结构体的sizeof 输出的大小都是 4 ;只有一个引用成员对象的结构体跟只有一个指针成员对象的结构体是一样的;再来看下如下代码:

#include <iostream>
using namespace std;


int main() 
{

	int i = 100;
	int &ri = i;
	cout << "reference ri = " << ri << endl;
	
	int *pi = &i;
	std::cout << "pointer *pi = " << *pi << std::endl;

	return 0;
}

查看反汇编代码的信息如下:

通过反汇编的信息来看,指针和引用的使用的指令是一样的;那么引用和指针的具体的区别是什么?

引用和指针的区别

1. 指针是一个变量,只不过这个变量存储的是一个地址,指向内存的一个存储单元;而引用跟原来的变量实质上是同一个东西,只不过是原变量的一个别名而已;

2. 引用不可以为空,当被创建的时候,必须初始化,初始化后就不会再改变了;而指针可以是空值,可以在任何时候被初始化,指针的值在初始化后可以改变,即指向其它的存储单元;

3. 指针可以有多级,但是引用只能是一级(int **p;合法 而 int &&a是不合法的)

4. 从编译上看,程序在编译时分别将指针和引用添加到符号表上,符号表上记录的是变量名及变量所对应地址。指针变量在符号表上对应的地址值为指针变量的地址值,而引用在符号表上对应的地址值为引用对象的地址值。符号表生成后就不会再改,因此指针可以改变指向的对象(指针变量中的值可以改),而引用对象不能改。这是使用指针不安全而使用引用安全的主要原因。从某种意义上来说引用可以被认为是不能改变的指针。

5. 不存在指向空值的引用这个事实,意味着使用引用的代码效率比使用指针的要高。因为在使用引用之前不需要测试它的合法性。相反,指针则应该总是被测试,防止其为空。

指针传参与引用传参

如下测试demo:

void func_1(int n) 
{
	cout << "func_1 n ads: " << &n << endl;
	n++;
}

void func_2(int *n) 
{
	cout << "func_2 n ads: " << n << endl;
	*n = *n + 1;
}

void func_3(int &n) 
{
	cout << "func_3 n ads: " << &n << endl;
	n = n + 1;
}

void func_4(int *n) 
{
	n++;
	cout << "func_4 result : " << *n << endl;
}


int main() 
{
	int n = 10;
	cout << "实参的地址" << &n << endl;
	func_1(n);
	cout << "after func_1() n=" << n << endl;
	func_2(&n);
	cout << "after func_2() n=" << n << endl;
	func_3(n);
	cout << "after func_3() n=" << n << endl;

	int arr[3] = {1,2,3};

	int *ptr = arr;

	func_4(ptr);

	cout << "after func_4() ptr result: " << *ptr << endl;

	return 0;
}

上面测试程序的输出:

func_1() 是值传递,传递的只是实参的拷贝;

func_2() 传递的是指针,本质上也是值传递,这里传递的是指针的地址值,由于通过形参的地址(和实参的地址是同一个值)可以访问到指针所指向的值(形参和实参指向的是同一个值),所以会在函数内修改掉实参指针所指向的值;

func_3() 传递的是实参的别名,所操作即实参本身;

func_4() 传递的是指针,它所传递的是一个地址值的拷贝,所以在函数内修改指针并不会影响到函数体外的实参

值传递:形参是实参的拷贝,改变形参的值并不会影响外部实参的值。从被调用函数的角度来说,值传递是单向的(实参->形参),参数的值只能传入,不能传出。当函数内部需要修改参数,并且不希望这个改变影响调用者时,采用值传递。

指针传递:指针传递参数本质上是值传递的方式,它所传递的是一个地址值。值传递过程中,被调函数的形式参数作为被调函数的局部变量处理,即在栈中开辟了内存空间以存放由主调函数放进来的实参的值,从而成为了实参的一个副本。值传递的特点是被调函数对形式参数的任何操作都是作为局部变量进行,不会影响主调函数的实参变量的值。

 

引用传递:形参相当于是实参的“别名”,对形参的操作其实就是对实参的操作,在引用传递过程中,被调函数的形式参数虽然也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。

返回引用与返回指针

A& a(){ return *this;} 

当返回一个引用时,不会发生拷贝,引用就是当前变量的别名

A a() { return *this;}

会生成一个临时对象变量,并返回这个临时变量。

当返回一个引用时,要注意被引用的对象不能超出作用域。所以返回一个对局部变量的引用是不合法的,但是,可以返回一个对静态变量的引用。

int& func() {
   int q;
   //! return q; // 在编译时发生错误
   static int x;
   return x;     // 安全,x 在函数作用域外依然是有效的
}

const引用

  • 指向常量的引用,在引用定义语句的类型前加const,表示指向的对象是常量。也跟指针一样不能对引用指向的变量进行重新赋值操作。
  • const引用可以绑定一个常量值,而不一定是一个类型对象

const引用是一种特殊的引用,从字面意思看const引用只是限制了引用对自身引用对象的修改权限,但最重要的一点,const引用可以绑定一个常量值,而不一定是一个类型对象,这样作为参数的时候,const引用可以使用临时对象和常量值作为参数,而非只能使用一个引用对象作为参数:

 

	//int& i = 100;//error
	const int& i = 100;
	
	int a = 1;
	int b = 2;
	
	int& k = a;
	k = b;//ok

	const int & j = a;
	//j = b;//error

 

 

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值