C++中指针和引用的区别(超详细)

article/2025/10/3 0:56:50

指针和引用主要有以下区别:

  1. 引用必须被初始化,但是不分配存储空间。指针不声明时初始化,在初始化的时候需要分配存储空间。
  2. 引用初始化后不能被改变,指针可以改变所指的对象。
  3. 不存在指向空值的引用,但是存在指向空值的指针

注意:引用作为函数参数时,会引发一定的问题,因为让引用作参数,目的就是想改变这个引用所指向地址的内容,而函数调用时传入的是实参,看不出函数的参数是正常变量,还是引用,因此可能引发错误。所以使用时一定要小心谨慎。

 

 

从概念上讲。指针从本质上讲就是存放变量地址的一个变量,在逻辑上是独立的,它可以被改变,包括其所指向的地址的改变和其指向的地址中所存放的数据的改变。

而引用是一个别名,它在逻辑上不是独立的,它的存在具有依附性,所以引用必须在一开始就被初始化,而且其引用的对象在其整个生命周期中是不能被改变的(自始至终只能依附于同一个变量)。

C++中,指针和引用经常用于函数的参数传递,然而,指针传递参数和引用传递参数是有本质上的不同的:

  • 指针传递参数本质上是 值传递的方式,它所传递的是一个地址值。值传递过程中,被调函数的形式参数作为被调函数的局部变量处理,即在中开辟了内存空间以存放由主调函数放进来的 实参的值,从而成为了实参的一个副本。值传递的特点是被调函数对形式参数的任何操作都是作为局部变量进行,不会影响主调函数的实参变量的值。
  • 而在引用传递过程中, 被调函数的形式参数虽然也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间 接寻址,即通过栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。
  • 引用传递和指针传递是 不同的,虽然它们都是在被调函数栈空间上的一个局部变量,但是任何对于引用参数的处理都会通过一个间接寻址的方式操作到主调函数中的相关变量。而对于指针 传递的参数,如果改变被调函数中的指针地址,它将影响不到主调函数的相关变量。如果想通过指针参数传递来改变主调函数中的相关变量,那就得使用指向指针的 指针,或者指针引用。

为了进一步加深大家对指针和引用的区别,下面我从编译的角度来阐述它们之间的区别:

程序在编译时分别将指 针和引用添加到符号表上,符号表上记录的是变量名及变量所对应地址。指针变量在符号表上对应的地址值为指针变量的地址值,而引用在符号表上对应的地址值为 引用对象的地址值。符号表生成后就不会再改,因此指针可以改变其指向的对象(指针变量中的值可以改),而引用对象则不能修改。

最后,总结一下指针和引用的相同点和不同点:

★相同点:

●都是地址的概念;

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

★不同点:

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

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

引用没有const,指针有constconst的指针不可变

●引用不能为空,指针可以为空

●“sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身的大小;

●指针和引用的自增(++)运算意义不一样;

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

 

 

 

 

这几天看重温了下《高质量C/C++编程指南》和 《More Effective C++》对于里面的引用和指针觉得写得很精辟,同时在网上也找了些别人写的总结,引用过来大家分享下。

    虽然使用引用和指针都可以间接访问另一个值,但他们之间有两个重要区别

  • 引用总是指向某个对象,定义引用没有初始化是错误的。
  • 赋值行为的差异,给引用赋值修改的是该引用所关联的对象的值,而并不是使引用与另一个对象关联。引用一经初始化,就始终指向同一个特定对象。


★ 相同点:


    1. 都是地址的概念;

    指针指向一块内存,它的内容是所指内存的地址;引用是某块内存的别名。


★ 区别:


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

    2. 引用使用时无需解引用(*),指针需要解引用;

    3. 引用只能在定义时被初始化一次,之后不可变;指针可变;

    引用“从一而终” ^_^

    4. 引用没有 const,指针有 const,const 的指针不可变;

    5. 引用不能为空,指针可以为空;

    6. “sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身(所指向的变量或对象的地址)的大小;

    typeid(T) == typeid(T&) 恒为真,sizeof(T) == sizeof(T&) 恒为真,但是当引用作为成员时,其占用空间与指针相同(没找到标准的规定)。

    7. 指针和引用的自增(++)运算意义不一样;


 ★ 联系


    1. 引用在语言内部用指针实现(如何实现?)。

    2. 对一般应用而言,把引用理解为指针,不会犯严重语义错误。引用是操作受限了的指针(仅容许取内容操作)。


★《高质量C/C++编程指南》6.6


    引用是C++中的概念,初学者容易把引用和指针混淆一起。一下程序中,n 是m 的一个引用(reference),m是被引用物(referent)。

  1. int m;  
  2. int &n = m;  

    n 相当于m 的别名(绰号),对n 的任何操作就是对m 的操作。例如有人名叫王小毛,他的绰号是“三毛”。说“三毛”怎么怎么的,其实就是对王小毛说三道四。所以n 既不是m 的拷贝,也不是指向m 的指针,其实n就是m 它自己。

   

复制代码
引用的一些规则如下:1)引用被创建的同时必须被初始化(指针则可以在任何时候被初始化)。(2)不能有NULL 引用,引用必须与合法的存储单元关联(指针则可以是NULL)。(3)一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象)。
复制代码

 

    以下示例程序中,k 被初始化为i 的引用。语句k = j 并不能将k 修改成为j 的引用,只是把k 的值改变成为6.由于k 是i 的引用,所以i 的值也变成了6.

  1. int i = 5;  
  2. int j = 6;  
  3. int &k = i;  
  4. k = j; // k 和i 的值都变成了6;  

    上面的程序看起来象在玩文字游戏,没有体现出引用的价值。引用的主要功能是传递函数的参数和返回值。C++语言中,函数的参数和返回值的传递方式有三种:值传递、指针传递和引用传递

    以下是“值传递”的示例程序。由于Func1 函数体内的x是外部变量n 的一份拷贝,改变x 的值不会影响n, 所以n 的值仍然是0.

  1. void Func1(int x)  
  2. {  
  3.     x = x + 10;  
  4. }  
  5. int n = 0;  
  6. Func1(n);  
  7. cout << “n = ” << n << endl;// n = 0  

    以下是“指针传递”的示例程序。由于Func2 函数体内的x 是指向外部变量n 的指针,改变该指针的内容将导致n 的值改变,所以n 的值成为10.

  1. void Func2(int *x)  
  2. {  
  3.     (* x) = (* x) + 10;  
  4. }  
  5. ⋯  
  6. int n = 0;  
  7. Func2(&n);  
  8. cout << “n = ” << n << endl; // n = 10  

    以下是“引用传递”的示例程序。由于Func3 函数体内的x 是外部变量n 的引用,x和n 是同一个东西,改变x 等于改变n,所以n 的值成为10.

  1. void Func3(int &x)  
  2. {  
  3.     x = x + 10;  
  4. }  
  5. //...  
  6. int n = 0;  
  7. Func3(n);  
  8. cout << “n = ” << n << endl; // n = 10  

对比上述三个示例程序,会发现“引用传递”的性质象“指针传递”,而书写方式象“值传递”。实际上“引用”可以做的任何事情“指针”也都能够做,为什么还要“引用”

    这东西?

    答案是“用适当的工具做恰如其分的工作”。

    指针能够毫无约束地操作内存中的如何东西,尽管指针功能强大,但是非常危险。

    就象一把刀,它可以用来砍树、裁纸、修指甲、理发等等,谁敢这样用?

    如果的确只需要借用一下某个对象的“别名”,那么就用“引用”,而不要用“指针”,以免发生意外。比如说,某人需要一份证明,本来在文件上盖上公章的印子就行了,如果把取公章的钥匙交给他,那么他就获得了不该有的权利。

  

★条款一:指针与引用的区别


  指针与引用看上去完全不同(指针用操作符’*’’->’,引用使用操作符’.’),但是它们似乎有相同的功能。指针与引用都是让你间接引用其他对象。你如何决定在什么时候使用指针,在什么时候使用引用呢?

  首先,要认识到在任何情况下都不能用指向空值的引用。一个引用必须总是指向 某些对象。因此如果你使用一个变量并让它指向一个对象,但是该变量在某些时候也可能不指向任何对象,这时你应该把变量声明为指针,因为这样你可以赋空值给 该变量。相反,如果变量肯定指向一个对象,例如你的设计不允许变量为空,这时你就可以把变量声明为引用。

  但是,请等一下,你怀疑地问,这样的代码会产生什么样的后果?

  1. char *pc = 0; // 设置指针为空值  
  2. char& rc = *pc; // 让引用指向空值  

  这是非常有害的,毫无疑问。结果将 是不确定的(编译器能产生一些输出,导致任何事情都有可能发生),应该躲开写出这样代码的人除非他们同意改正错误。如果你担心这样的代码会出现在你的软件 里,那么你最好完全避免使用引用,要不然就去让更优秀的程序员去做。我们以后将忽略一个引用指向空值的可能性。

  因为引用肯定会指向一个对象,在C里,引用应被初始化。

  1. string& rs; // 错误,引用必须被初始化  
  2. strings("xyzzy");  
  3. string&rs = s; // 正确,rs指向s  
  4.   
  5. 指针没有这样的限制。  
  6. string*ps; // 未初始化的指针  
  7.          // 合法但危险  

  不存在指向空值的引用这个事实意味着使用引用的代码效率比使用指针的要高。因为在使用引用之前不需要测试它的合法性。

  1. void printDouble(const double& rd)  
  2. {  
  3.  cout<< rd; // 不需要测试rd,它  
  4. }       // 肯定指向一个double值  
  5.   
  6. 相反,指针则应该总是被测试,防止其为空:  
  7. void printDouble(const double *pd)  
  8. {  
  9.  if (pd)  
  10.  {// 检查是否为NULL  
  11.   cout<< *pd;  
  12.  }  
  13. }  

  指针与引用的另一个重要的不同是指针可以被重新赋值以指向另一个不同的对象。但是引用则总是指向在初始化时被指定的对象,以后不能改变。

  1. strings1("Nancy");  
  2. strings2("Clancy");  
  3.  string& rs = s1; // rs 引用 s1  
  4. string *ps= &s1; // ps 指向 s1  
  5. rs = s2; // rs 仍旧引用s1  
  6.        // 但是s1的值现在是"Clancy"  
  7.  ps = &s2; // ps 现在指向 s2;// s1 没有改变  

总的来说,在以下情况下你应该使用 指针: 一是你考虑到存在不指向任何对象的可能(在这种情况下,你能够设置指针为空); 二是你需要能够在不同的时刻指向不同的对象(在这种情况下,你能改变 指针的指向)。如果总是指向一个对象并且一旦指向一个对象后就不会改变指向,那么你应该使用引用。 还有一种情况,就是当你重载某个操作符时,你应该使用引用。最普通的例子是操作符[]。这个操作符典型的用法是返回一个目标对象,其能被赋值。

 


  1. vector<int>v(10); //建立整形向量(vector),大小为10  
  2.                  //向量是一个在标准C库中的一个模板(见条款35)   
  3. v[5] = 10; // 这个被赋值的目标对象就是操作符[]返回的值  

  如果操作符[]返回一个指针,那么后一个语句就得这样写:

  1. *v[5] = 10;  

  但是这样会使得v看上去象是一个向量指针。因此你会选择让操作符返回一个引用。(这有一个有趣的例外,参见条款30

当你知道你必须指向一个对象并且不想改变其指向时,或者在重载操作符并为防止不必要的语义误解时,你不应该使用指针。而在除此之外的其他情况下,则应使用指针

 

 

C++ const引用详解

 

 

(1)       在实际的程序中,引用主要被用做函数的形式参数--通常将类对象传递给一个函数.引用必须初始化但是用对象的地址初始化引用是错误的,我们可以定义一个指针引用。

 

1 int ival = 1092;
2 int &re = ival;   //ok
3 int &re2 = &ival;   //错误
4 int *pi = &ival;
5 int *&pi2 = pi;   //ok

 

(2)       一旦引用已经定义,它就不能再指向其他的对象.这就是为什么它要被初始化的原因。

 

(3)       const引用可以用不同类型的对象初始化(只要能从一种类型转换到另一种类型即可),也可以是不可寻址的值,如文字常量。例如

 

1 double dval = 3.14159;
2 //下3行仅对const引用才是合法的
3 const int &ir = 1024;
4 const int &ir2 = dval;
5 const double &dr = dval + 1.0;

上面,同样的初始化对于非const引用是不合法的,将导致编译错误。原因有些微妙,需要适当做些解释。

引用在内部存放的是一个对象的地址,它是该对象的别名。对于不可寻址的值,如文字常量,以及不同类型的对象,编译器为了实现引用,必须生成一个临时对象,引用实际上指向该对象,但用户不能访问它。

 

例如:

1 double dval = 23;
2 const int &ri = dval; 

编译器将其转换为:

1 int tmp = dval; // double -> int
2 const int &ri = tmp; 

同理:上面代码

 

1 double dval = 3.14159;
2 //下3行仅对const引用才是合法的
3 const int &ir = 1024;
4 const int &ir2 = dval;
5 const double &dr = dval + 1.0; 

内部转化为:

 

复制代码
 1 double dval = 3.14159;
 2 //不可寻址,文字常量
 3 int tmp1 = 1024;
 4 const int &ir = tmp1;
 5 
 6 //不同类型
 7 int tmp2 = dval;//double -> int
 8 const int &ir2 = tmp2;
 9 
10 //另一种情况,不可寻址
11 double tmp3 = dval + 1.0;
12 const double &dr = tmp3;
复制代码

 

(4)       不允许非const引用指向需要临时对象的对象或值,即,编译器产生临时变量的时候引用必须为const!!!!切记!!

 

复制代码
 1 int iv = 100;
 2 int *&pir = &iv;//错误,非const引用对需要临时对象的引用
 3 int *const &pir = &iv;//ok
 4 const int ival = 1024;
 5 int *&pi_ref = &ival;    //错误,非const引用是非法的
 6 
 7 const int *&pi_ref = &ival;   //错误,需要临时变量,且引用的是指针,而pi_ref是一个非常量指针
 8 
 9 const int * const &pi_ref = &ival;  //正确
10 
11 //补充
12 const int *p = &ival;
13 const int *&pi_ref = p;  //正确 
复制代码

 

(5)       ********对于const int *const & pi_ref = &iva; 具体的分析如下:*********

1.不允许非const引用指向需要临时对象的对象或值

 

int a = 2;
int &ref1 = a;// OK.有过渡变量。
const int &ref2 = 2;// OK.编译器产生临时变量,需要const引用 

2.地址值是不可寻址的值

int * const &ref3 = &a;   // OK; 

    3.于是,用const对象的地址来初始化一个指向指针的引用

 

const int b = 23;
const int *p = &b;
const int *& ref4 = p;
const int *const & ref5 = &b;   //OK 

const引用的语义到底是什么?

最后,我们可能仍然不明白const引用的这个const的语义是什么

 

const引用表示,试图通过此引用去(间接)改变其引用的对象的值时,编译器会报错!

 

这并意味着,此引用所引用的对象也因此变成const类型了。我们仍然可以改变其指向对象的值,只是不通过引用

 

下面是一个简单的例子:

 

复制代码
 1 #include <iostream>
 2 using namespace std;
 3 
 4 int main()
 5  {
 6      int val = 1024;
 7      const int &ir = val;     
 8      val++;
 9 
10      //ir++;
11     cout << val << " " << ir << endl;
12 
13      return 0;
14 }
复制代码

 

其中第10行,如果我们通过ir来改变val的值,编译时会出错。但是我们仍然可以通过val直接改变其值(9)

 

总结:const引用只是表明,保证不会通过此引用间接的改变被引用的对象!

 另外,const既可以放到类型前又可以放到类型后面,放类型后比较容易理解:

string const *t1;
const string *t1;
typedef string* pstring;string s;
const pstring cstr1 = &s;就出错了
//但是放在类型后面不会出错:
pstring const cstr2 = &s;

 

参考:http://blog.csdn.net/wangqiulin123456/article/details/8464418

        http://blog.csdn.net/k2eats/article/details/2541790?reload


http://chatgpt.dhexx.cn/article/1Wrta1gn.shtml

相关文章

c++:指针和引用的区别

目录 前言&#xff1a; 1、引用概念上是定义一个变量的别名&#xff0c;而指针是存储一个变量的地址。 2、引用在定义时必须要初始化&#xff0c;但是指针没有要求。 3、引用在初始化时引用一个实体后&#xff0c;就不能再引用其他实体&#xff0c;因为其本质是一个指针常量…

C++中指针和引用的区别

1.指针和引用的定义和性质区别&#xff1a; &#xff08;1&#xff09;指针&#xff1a;指针是一个变量&#xff0c;只不过这个变量存储的是一个地址&#xff0c;指向内存的一个存储单元&#xff1b;而引用跟原来 的变量实质上是同一个东西&#xff0c;只不过是原变量的一个别…

C++中的指针与引用

写在前面 指针和引用形式上很好区别&#xff0c;但是他们似乎有相同的功能,都能够直接引用对象&#xff0c;对其进行直接的操作。但是什么时候使用指针&#xff1f;什么时候使用引用呢&#xff1f;这两者很容易混淆,在此我详细介绍一下指针和引用&#xff0c;力争将最真实的一…

引用和指针概念及区别

一、引用和指针 指针&#xff1a;指针是一个特殊的变量&#xff0c;它里面存储的的数值为内存里的一个地址&#xff0c;通过*访问内存地址所指向的值 引用&#xff1a;引用不是新定义一个变量&#xff0c;而是给已存在变量取了一个别名&#xff0c;编译器不会为引用变量开辟内…

01背包.动态规划.c语言实现

二维dp数组01背包 确定dp数组以及下标的含义 使用二维数组&#xff0c;即dp[i][j] 表示从下标为[0-i]的物品里任意取&#xff0c;放进容量为j的背包&#xff0c;价值总和最大是多少 2.递推公式&#xff1a;dp[i][j]dp[i-1][j-weight]value[i] dp[i][j]dp[i-1][j] 3.数组初始…

【动态规划】背包问题

注意&#xff1a;for j in range(1&#xff0c;m1):是枚举所有的情况&#xff0c;不用一一判断放入物品后背包容量减少后j的变化。为什么从1开始&#xff0c;因为0已经写出来了&#xff0c;即dp[i-1][j]dp[i-1][j-0*a[i-1]]0*v[i-1]。 01背包无价值 dp定义&#xff1a;前i项物…

动态规划——背包问题(01背包问题)

动态规划——背包问题&#xff08;01背包问题&#xff09;01背包问题(求最大价值)&#xff1a;问题优化01背包问题(求方案数)&#xff1a; 动态规划——背包问题&#xff08;01背包问题&#xff09; 01背包问题(求最大价值)&#xff1a; 有N件物品和一个最多能背重量为W的背包…

动态规划:关于01背包问题 I

动态规划&#xff1a;关于01背包问题&#xff0c;你该了解这些&#xff01; 对于面试的话&#xff0c;其实掌握01背包&#xff0c;和完全背包&#xff0c;就够用了&#xff0c;最多可以再来一个多重背包。 如果这几种背包&#xff0c;分不清&#xff0c;我这里画了一个图&…

c++ 动态规划-01背包

动态规划 - 01背包问题 1.使用递归遍历(穷举)求解&#xff1a; 01背包问题&#xff1a;给定 n 种物品和一个重量(容量)(限定条件)为 w 的背包,物品 i 的重量是 wi,其价值为 vi。(每种物品只有一个)问:如何选择装入背包的物品,使得装入背包中的物品的价值最大。 //VC6.0------…

动态规划——01背包问题(C++实现)

题目描述&#xff1a; 解题思路&#xff1a; 整体思路&#xff1a; 利用动态规划&#xff0c;其目的就是将原问题分解成几个子问题&#xff0c;通过求解简单的子问题&#xff0c;把原问题给解决&#xff0c;就比如斐波那契数列方程&#xff1a; f[i]f[i-1]f[i-2]; 动态规划的…

动态规划总结三01背包问题

01背包问题一般是利用动态规划进行解题的&#xff0c;这里通过leetcode1049来讲解01背包的解题思路以及如何对01背包应用题目转换和理清思路 01背包问题&#xff1a; 这里借用学习公众号代码随想录的一张图来说明背包问题的种类 对于面试的话&#xff0c;其实掌握01背包&…

dp 动态规划 01背包问题 Python

参考学习网址&#xff1a; https://www.bilibili.com/video/av33930433?fromsearch&seid10637513335818789097 https://www.cnblogs.com/anzhengyu/p/11408466.html 问题描述&#xff1a; 给定 n 件物品&#xff0c;物品的重量为 w[i]&#xff0c;物品的价值为 v[i]。现…

Python3使用动态规划处理01背包问题

文章目录 视频教程讲解题目介绍题解1&#xff1a;二维列表题解2&#xff1a;一维列表&#xff08;滚动数组&#xff09;延伸阅读 视频教程讲解 【Python算法系列】动态规划2-01背包问题&完全背包问题【Python算法实战】背包问题 题目介绍 原题链接&#xff1a;NC145 01背…

算法 动态规划 01背包问题

01背包 问题分析代码实现从前往后拿&#xff0c;递归实现非递归实现非递归实现&#xff0c;自上向下填充 允接上一文章内容&#xff1a; 算法 动态规划: link. 问题分析 按照普通思维&#xff0c;首先想到应该为贪心算法&#xff0c;也就是计算每个物品重量价值比&#xff0c;…

【动态规划】01背包问题

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位喜欢写作&#xff0c;计科专业大二菜鸟 &#x1f3e1;个人主页&#xff1a;starry陆离 &#x1f552;首发日期&#xff1a;2022年5月16日星期一 &#x1f30c;上期文章&#xff1a;【动态规划】最长上升子序列 &#x1f4…

01背包问题:图表法带你快速理解动态规划解决01背包问题 附C++源码

0-1背包问题 所谓0-1背包问题&#xff0c;也就是给你一个重量为M的背包和n种物品&#xff0c;每种物品有一定的重量和价值&#xff0c;在每种物品均可装入背包1次或不装入&#xff08;不能仅装入物品的一部分&#xff09;且不超过背包载重量的前提下&#xff0c;问你怎样选择物…

动态规划——0/1背包问题(全网最细+图文解析)

✨动态规划——0/1背包问题(全网最细图文解析) 作者介绍: &#x1f393;作者:青花瓷✨ &#x1f440;作者的Gitee:代码仓库 &#x1f4cc;系列文章推荐: ✨1.数据结构与算法—算法篇之动态规划(一) ✨2.【Java刷题特辑第一章】——【点进来花两把游戏的时间学习晚上睡觉都踏实了…

【动态规划】01背包问题(通俗易懂,超基础讲解)

问题描述 有n个物品&#xff0c;它们有各自的体积和价值&#xff0c;现有给定容量的背包&#xff0c;如何让背包里装入的物品具有最大的价值总和&#xff1f; 为方便讲解和理解&#xff0c;下面讲述的例子均先用具体的数字代入&#xff0c;即&#xff1a;eg&#xff1a;numbe…

java事务和分布式事务详解

目录 事务问题 1 Java事务的类型 2 spring事务实现源码分析 事务问题 面试经常会问到分布式锁、分布式事务、SOA 服务化、分布式系统等业务、架构的问题和解决方案&#xff0c;工作中接触的业务方面事关金融&#xff0c;也需要解决一些类似的业务问题&#xff0c;所以总结…

JAVA事务配置总结

JAVA事务配置总结 使用hibernate&#xff1a;1.本地事务动态数据源单sessionFactory 这种情况属于大部分项目配置&#xff0c;在这里不多谈 2.全局事务动态数据源单sessionFactory 数据库分库分表时使用&#xff0c;已解决了不同库中相同名字的表相同ID的数据同时出现在同一…