C ++中的指针variables和引用variables之间有什么区别?

我知道引用是语法糖,所以代码更容易读写。

但有什么区别?


下面的答案和链接摘要:

  1. 指针可以重新分配任意次数,而绑定后不能重新引用引用。
  2. 指针可以指向任何地方( NULL ),而引用总是指向一个对象。
  3. 你不能像引用指针那样使用引用的地址。
  4. 没有“引用算术”(但是你可以把一个引用指向的对象的地址作为指针并且在&obj + 5做指针运算)。

澄清一个误解:

C ++标准非常小心,以避免编译器如何实现引用,但每个C ++编译器都将引用实现为指针。 也就是说,一个声明如:

 int &ri = i; 

如果没有完全优化 ,则分配与指针相同的存储量,并将i的地址放入该存储中。

所以,一个指针和一个引用都占用相同数量的内存。

作为基本规则,

  • 在函数参数和返回types中使用引用来定义有用和自我logging的接口。
  • 使用指针来实现algorithm和数据结构。

有趣的阅​​读:

  • 我最喜欢的C ++ FAQ lite 。
  • 参考与指针 。
  • 参考文献介绍 。
  • 引用和常量 。
  1. 指针可以重新分配:

     int x = 5; int y = 6; int *p; p = &x; p = &y; *p = 10; assert(x == 5); assert(y == 10); 

    引用不能,并且必须在初始化时分配:

     int x = 5; int y = 6; int &r = x; 
  2. 一个指针在堆栈上有自己的内存地址和大小(在x86上是4个字节),而引用共享相同的内存地址(与原始variables),但也占用堆栈上的一些空间。 由于引用具有与原始variables本身相同的地址,因此将引用看作同一variables的另一个名称是安全的。 注意:指针指向的内容可以在堆栈或堆上。 同上参考。 我在这个陈述中的主张并不是指针必须指向堆栈。 指针只是一个保存内存地址的variables。 这个variables在堆栈上。 由于引用在堆栈上有自己的空间,并且地址与它引用的variables相同。 更多关于堆栈vs堆 。 这意味着有一个编译器不会告诉你的引用的真实地址。

     int x = 0; int &r = x; int *p = &x; int *p2 = &r; assert(p == p2); 
  3. 你可以有指向指针的指针,提供额外的间接级别。 而引用仅提供一个间接的级别。

     int x = 0; int y = 0; int *p = &x; int *q = &y; int **pp = &p; pp = &q;//*pp = q **pp = 4; assert(y == 4); assert(x == 0); 
  4. 指针可以直接指定nullptr ,而引用不能。 如果你足够努力,而且你知道如何,你可以使一个引用nullptr的地址。 同样,如果你足够努力,你可以有一个指针的引用,然后该引用可以包含nullptr

     int *p = nullptr; int &r = nullptr; <--- compiling error 
  5. 指针可以遍历一个数组,你可以使用++转到指针指向的下一个项目, + 4转到第5个元素。 这不pipe指针指向的对象的大小如何。

  6. 指针需要用*来取消引用的内存位置,而引用可以直接使用。 指向类/结构体的指针使用->来访问它的成员,而引用使用一个.

  7. 指针是一个保存内存地址的variables。 无论如何实现引用,引用的内存地址都与其引用的项目相同。

  8. 引用不能被塞进一个数组,而指针可以是(用户@litb提到的)

  9. Const引用可以绑定到临时对象。 指针不能(不是没有间接的):

     const int &x = int(12); //legal C++ int *y = &int(12); //illegal to dereference a temporary. 

    这使得const&更安全的使用参数列表等等。

什么是C ++参考( 对于C程序员

一个引用可以被认为是一个常量指针 (不要和指向一个常量值的指针混淆),也就是说编译器会为你应用*运算符。

所有引用必须用非空值初始化,否则编译将失败。 这是不可能得到一个引用的地址 – 地址运算符将返回引用值的地址 – 也不可能对引用进行运算。

C程序员可能不喜欢C ++引用,因为当发生间接性时,或者如果通过值或指针传递参数而不查看函数签名,它将不再显而易见。

C ++程序员可能不喜欢使用指针,因为它们被认为是不安全的 – 尽pipe除了最微不足道的情况之外,引用实际上并不比常量指针更安全 – 缺乏自动间接的方便,并带有不同的语义内涵。

考虑一下C ++ FAQ中的下列语句:

尽pipe通常使用底层汇编语言中的地址来实现引用,但请不要将引用视为指向对象的有趣的指针。 参考对象。 它不是指向对象的指针,也不是对象的副本。 这对象。

但是如果一个参考文献真的是这个客体的话,怎么会有悬而未决的参考? 在非托pipe语言中,引用不可能比指针更“安全” – 通常不可能跨越范围边界可靠地别名化值!

为什么我认为C ++引用是有用的

从C背景来看,C ++引用可能看起来像一个有点愚蠢的概念,但仍应该在可能的情况下使用它们来代替指针:自动间接方便,引用在处理RAII时特别有用 – 但不是因为任何安全优点,而是因为他们使写作惯用代码不那么尴尬。

RAII是C ++的核心概念之一,但它与复制语义的非平凡交互。 通过引用传递对象避免了这些问题,因为不涉及复制。 如果在语言中不存在引用,则必须使用指针,而使用起来更麻烦,因此违反了语言devise原则,即最佳实践解决scheme应该比替代scheme更容易。

如果你想成为一个迂腐的人,你可以用一个你不能用指针做的引用来做一件事:扩​​展一个临时对象的生命周期。 在C ++中,如果将const引用绑定到临时对象,则该对象的生命周期将成为引用的生命周期。

 std::string s1 = "123"; std::string s2 = "456"; std::string s3_copy = s1 + s2; const std::string& s3_reference = s1 + s2; 

在这个例子中,s3_copy复制了连接结果的临时对象。 实质上s3_reference成为临时对象。 这实际上是一个临时对象的引用,它现在具有与引用相同的生命周期。

如果你在没有const情况下尝试这个,它将会失败。 你不能将一个非const引用绑定到一个临时对象,你也不能把它的地址作为这个事情。

你忘了最重要的部分:

使用指针的成员访问使用->
成员访问与引用使用.

foo.bar 明显优于foo->bar ,就像vi 明显优于Emacs 🙂

与stream行的观点相反,可能有一个NULL的引用。

 int * p = NULL; int & r = *p; r = 1; // crash! (if you're lucky) 

当然,用一个参考资料做起来要困难得多,但是如果你pipe理它,那么你就会把你的头发撕掉,试图find它。 在C ++中引用不是固有的安全!

从技术上讲,这是一个无效的引用 ,而不是空引用。 C ++不支持空引用作为您可能在其他语言中find的概念。 还有其他types的无效引用。 任何无效的引用引起未定义行为的幽灵,就像使用无效指针一样。

在赋值给引用之前,实际的错误是在NULL指针的解引用中。 但我不知道任何编译器会在这种情况下产生任何错误 – 错误传播到代码中的一个点。 这就是使这个问题如此阴险的原因。 大多数情况下,如果取消引用NULL指针,则会在该位置崩溃,并且不需要太多debugging即可解决问题。

我上面的例子是简短的和人为的。 这是一个更现实的例子。

 class MyClass { ... virtual void DoSomething(int,int,int,int,int); }; void Foo(const MyClass & bar) { ... bar.DoSomething(i1,i2,i3,i4,i5); // crash occurs here due to memory access violation - obvious why? } MyClass * GetInstance() { if (somecondition) return NULL; ... } MyClass * p = GetInstance(); Foo(*p); 

我想重申,获取空引用的唯一方法是通过格式错误的代码,一旦你有了它,你会得到未定义的行为。 检查空引用是毫无意义的; 例如,你可以尝试if(&bar==NULL)...但编译器可能优化语句不存在! 一个有效的引用永远不能为NULL,所以从编译器的angular度来看,比较总是假的,并且可以自由地将if子句作为死代码去除 – 这是未定义行为的本质。

避免麻烦的正确方法是避免取消引用NULL指针来创build引用。 这是一个自动的方法来完成这个。

 template<typename T> T& ref(T* p) { if (p == NULL) throw std::invalid_argument(std::string("NULL reference")); return *p; } MyClass * p = GetInstance(); Foo(ref(p)); 

除了语法糖之外,引用是一个const指针(不是const指针指针,const指针)。 当你声明参考variables时,你必须确定它所指的是什么,并且以后你不能改变它。

实际上,一个引用并不像一个指针。

编译器保存variables的“引用”,将名称与内存地址相关联; 这是编译时将任何variables名称转换为内存地址的工作。

当你创build一个引用时,你只能告诉编译器你为指针variables指定了另一个名字; 这就是为什么引用不能“指向空”,因为一个variables不能,而不是。

指针是variables; 它们包含一些其他variables的地址,或者可以为null。 重要的是一个指针有一个值,而一个引用只有一个它引用的variables。

现在对实际代码进行一些解释:

 int a = 0; int& b = a; 

在这里,你不是创build另一个variables指向a ; 你只是添加另一个名字的内存内容持有的价值。 这个内存现在有两个名字ab ,可以用任何名字来寻址。

 void increment(int& n) { n = n + 1; } int a; increment(a); 

当调用一个函数时,编译器通常为要复制的参数生成内存空间。 函数签名定义了应该创build的空间,并给出了应该用于这些空间的名称。 声明一个参数作为引用只是告诉编译器使用inputvariables的内存空间,而不是在方法调用期间分配一个新的内存空间。 说你的函数直接操作在调用范围中声明的variables可能看起来很奇怪,但要记住,在执行编译代码的时候,没有更多的范围。 只有简单的平面内存,你的function代码可以操纵任何variables。

现在可能有些情况下编译器在编译时可能无法知道引用,例如使用externvariables。 所以引用可能会或可能不会被实现为底层代码中的指针。 但在我给你的例子中,很可能不会用指针来实现。

引用与指针非常相似,但是它们专门用来优化编译器。

  • 引用的devise使得编译器能够更容易地跟踪哪个引用别名使用哪个variables。 两个主要特点非常重要:没有“参考算术”,也没有重新分配参考。 这些允许编译器确定哪些引用在编译时别名了哪些variables。
  • 引用允许引用没有内存地址的variables,比如编译器select寄存器的地址。 如果你把一个局部variables的地址,编译器很难把它放在一个寄存器中。

举个例子:

 void maybeModify(int& x); // may modify x in some way void hurtTheCompilersOptimizer(short size, int array[]) { // This function is designed to do something particularly troublesome // for optimizers. It will constantly call maybeModify on array[0] while // adding array[1] to array[2]..array[size-1]. There's no real reason to // do this, other than to demonstrate the power of references. for (int i = 2; i < (int)size; i++) { maybeModify(array[0]); array[i] += array[1]; } } 

一个优化编译器可能会意识到,我们正在访问一个[0]和[1]相当一堆。 它会喜欢优化algorithm:

 void hurtTheCompilersOptimizer(short size, int array[]) { // Do the same thing as above, but instead of accessing array[1] // all the time, access it once and store the result in a register, // which is much faster to do arithmetic with. register int a0 = a[0]; register int a1 = a[1]; // access a[1] once for (int i = 2; i < (int)size; i++) { maybeModify(a0); // Give maybeModify a reference to a register array[i] += a1; // Use the saved register value over and over } a[0] = a0; // Store the modified a[0] back into the array } 

为了做这样的优化,需要certificate在调用过程中没有任何东西可以改变array [1]。 这很容易做到。 我永远不会less于2,所以数组[我]永远不能引用数组[1]。 maybeModify()被赋予a0作为参考(别名数组[0])。 因为没有“引用”algorithm,编译器只需要certificateMaybeModify不会得到x的地址,并且已经certificate什么都不会改变array [1]。

它还必须certificate,将来的调用不可能读/写一个[0],而我们在a0中有一个临时寄存器副本。 这通常是微不足道的,因为在很多情况下,引用永远不会像类实例那样存储在永久结构中。

现在用指针做同样的事情

 void maybeModify(int* x); // May modify x in some way void hurtTheCompilersOptimizer(short size, int array[]) { // Same operation, only now with pointers, making the // optimization trickier. for (int i = 2; i < (int)size; i++) { maybeModify(&(array[0])); array[i] += array[1]; } } 

行为是一样的; 只是现在certificate可能修改数组[1]是很困难的,因为我们已经给它一个指针了; 猫在袋子外面。 现在必须做更多的困难的certificate:一个对MaybeModify的静态分析,certificate它永远不会写入&x + 1。它还必须certificate它永远不会保存一个可以引用array [0]的指针,这只是如棘手。

现代编译器在静态分析中越来越好,但总是很好的帮助他们并使用引用。

当然,除了这样聪明的优化之外,编译器确实会在需要时将引用转换为指针。

引用永远不能为NULL

尽pipe引用和指针都用于间接访问另一个值,但引用和指针之间有两个重要的区别。 首先是引用总是指向一个对象:定义一个引用而不初始化它是一个错误。 赋值的行为是第二​​个重要区别:赋值给引用会改变引用绑定的对象; 它不会重新引用另一个对象。 一旦初始化,引用总是指向相同的基础对象。

考虑这两个程序片段。 首先,我们将一个指针分配给另一个:

 int ival = 1024, ival2 = 2048; int *pi = &ival, *pi2 = &ival2; pi = pi2; // pi now points to ival2 

分配之后,ival,pi处理的对象保持不变。 该分配会更改pi的值,使其指向不同的对象。 现在考虑一个分配两个引用的类似程序:

 int &ri = ival, &ri2 = ival2; ri = ri2; // assigns ival2 to ival 

这个赋值改变了ival,ri引用的值,而不是引用本身。 赋值后,两个引用仍然引用它们的原始对象,这些对象的值现在也是相同的。

如果你不熟悉以抽象的或者甚至是学术的方式学习计算机语言,那么这种语义上的差别可能会显得很深奥。

在最高层次上,引用的思想是它们是透明的“别名”。 你的计算机可能会使用一个地址来使它们工作,但是你不应该担心这一点:你应该把它们当作现有对象的“另一个名字”,语法反映了这一点。 它们比指针更严格,所以当你要创build一个悬挂的引用时,编译器可以更可靠地发出警告,而不是当你要创build一个悬挂的指针时。

除此之外,指针和引用当然有一些实际的区别。 使用它们的语法显然是不同的,你不能“重新定位”引用,引用虚无,或者指向引用。

引用是另一个variables的别名,而指针保存variables的内存地址。 通常使用引用作为函数参数,以便传递的对象不是副本,而是对象本身。

  void fun(int &a, int &b); // A common usage of references. int a = 0; int &b = a; // b is an alias for a. Not so common to use. 

这是基于教程 。 所写的内容更清楚:

 >>> The address that locates a variable within memory is what we call a reference to that variable. (5th paragraph at page 63) >>> The variable that stores the reference to another variable is what we call a pointer. (3rd paragraph at page 64) 

Simply to remember that,

 >>> reference stands for memory location >>> pointer is a reference container (Maybe because we will use it for several times, it is better to remember that reference.) 

What's more, as we can refer to almost any pointer tutorial, a pointer is an object that is supported by pointer arithmetic which makes pointer similar to an array.

Look at the following statement,

 int Tom(0); int & alias_Tom = Tom; 

alias_Tom can be understood as an alias of a variable (different with typedef , which is alias of a type ) Tom . It is also OK to forget the terminology of such statement is to create a reference of Tom .

It doesn't matter how much space it takes up since you can't actually see any side effect (without executing code) of whatever space it would take up.

On the other hand, one major difference between references and pointers is that temporaries assigned to const references live until the const reference goes out of scope.

例如:

 class scope_test { public: ~scope_test() { printf("scope_test done!\n"); } }; ... { const scope_test &test= scope_test(); printf("in scope\n"); } 

将打印:

 in scope scope_test done! 

This is the language mechanism that allows ScopeGuard to work.

A reference to a pointer is possible in C++, but the reverse is not possible means a pointer to a reference isn't possible. A reference to a pointer provides a cleaner syntax to modify the pointer. Look at this example:

 #include<iostream> using namespace std; void swap(char * &str1, char * &str2) { char *temp = str1; str1 = str2; str2 = temp; } int main() { char *str1 = "Hi"; char *str2 = "Hello"; swap(str1, str2); cout<<"str1 is "<<str1<<endl; cout<<"str2 is "<<str2<<endl; return 0; } 

And consider the C version of the above program. In C you have to use pointer to pointer (multiple indirection), and it leads to confusion and the program may look complicated.

 #include<stdio.h> /* Swaps strings by swapping pointers */ void swap1(char **str1_ptr, char **str2_ptr) { char *temp = *str1_ptr; *str1_ptr = *str2_ptr; *str2_ptr = temp; } int main() { char *str1 = "Hi"; char *str2 = "Hello"; swap1(&str1, &str2); printf("str1 is %s, str2 is %s", str1, str2); return 0; } 

Visit the following for more information about reference to pointer:

  • C++: Reference to Pointer
  • Pointer-to-Pointer and Reference-to-Pointer

As I said, a pointer to a reference isn't possible. Try the following program:

 #include <iostream> using namespace std; int main() { int x = 10; int *ptr = &x; int &*ptr1 = ptr; } 

A reference is not another name given to some memory. It's a immutable pointer that is automatically de-referenced on usage. Basically it boils down to:

 int& j = i; 

It internally becomes

 int* const j = &i; 

There is one fundamental difference between pointers and references that I didn't see anyone had mentioned: references enable pass-by-reference semantics in function arguments. Pointers, although it is not visible at first do not: they only provide pass-by-value semantics. This has been very nicely described in this article .

Regards, &rzej

At the risk of adding to confusion, I want to throw in some input, I'm sure it mostly depends on how the compiler implements references, but in the case of gcc the idea that a reference can only point to a variable on the stack is not actually correct, take this for example:

 #include <iostream> int main(int argc, char** argv) { // Create a string on the heap std::string *str_ptr = new std::string("THIS IS A STRING"); // Dereference the string on the heap, and assign it to the reference std::string &str_ref = *str_ptr; // Not even a compiler warning! At least with gcc // Now lets try to print it's value! std::cout << str_ref << std::endl; // It works! Now lets print and compare actual memory addresses std::cout << str_ptr << " : " << &str_ref << std::endl; // Exactly the same, now remember to free the memory on the heap delete str_ptr; } 

Which outputs this:

 THIS IS A STRING 0xbb2070 : 0xbb2070 

If you notice even the memory addresses are exactly the same, meaning the reference is successfully pointing to a variable on the heap! Now if you really want to get freaky, this also works:

 int main(int argc, char** argv) { // In the actual new declaration let immediately de-reference and assign it to the reference std::string &str_ref = *(new std::string("THIS IS A STRING")); // Once again, it works! (at least in gcc) std::cout << str_ref; // Once again it prints fine, however we have no pointer to the heap allocation, right? So how do we free the space we just ignorantly created? delete &str_ref; /*And, it works, because we are taking the memory address that the reference is storing, and deleting it, which is all a pointer is doing, just we have to specify the address with '&' whereas a pointer does that implicitly, this is sort of like calling delete &(*str_ptr); (which also compiles and runs fine).*/ } 

Which outputs this:

 THIS IS A STRING 

Therefore a reference IS a pointer under the hood, they both are just storing a memory address, where the address is pointing to is irrelevant, what do you think would happen if I called std::cout << str_ref; AFTER calling delete &str_ref? Well, obviously it compiles fine, but causes a segmentation fault at runtime because it's no longer pointing at a valid variable, we essentially have a broken reference that still exists (until it falls out of scope), but is useless.

In other words, a reference is nothing but a pointer that has the pointer mechanics abstracted away, making it safer and easier to use (no accidental pointer math, no mixing up '.' and '->', etc.), assuming you don't try any nonsense like my examples above 😉

Now regardless of how a compiler handles references, it will always have some kind of pointer under the hood, because a reference must refer to a specific variable at a specific memory address for it to work as expected, there is no getting around this (hence the term 'reference').

The only major rule that's important to remember with references is that they must be defined at the time of declaration (with the exception of a reference in a header, in that case it must be defined in the constructor, after the object it's contained in is constructed it's too late to define it).

Remember, my examples above are just that, examples demonstrating what a reference is, you would never want to use a reference in those ways! For proper usage of a reference there are plenty of answers on here already that hit the nail on the head

I use references unless I need either of these:

  • Null pointers can be used as a sentinel value, often a cheap way to avoid function overloading or use of a bool.

  • You can do arithmetic on a pointer. For example, p += offset;

This program might help in comprehending the answer of the question. This is a simple program of a reference "j" and a pointer "ptr" pointing to variable "x".

 #include<iostream> using namespace std; int main() { int *ptr=0, x=9; // pointer and variable declaration ptr=&x; // pointer to variable "x" int & j=x; // reference declaration; reference to variable "x" cout << "x=" << x << endl; cout << "&x=" << &x << endl; cout << "j=" << j << endl; cout << "&j=" << &j << endl; cout << "*ptr=" << *ptr << endl; cout << "ptr=" << ptr << endl; cout << "&ptr=" << &ptr << endl; getch(); } 

Run the program and have a look at the output and you'll understand.

Also, spare 10 minutes and watch this video: https://www.youtube.com/watch?v=rlJrrGV0iOg

Another difference is that you can have pointers to a void type (and it means pointer to anything) but references to void are forbidden.

 int a; void * p = &a; // ok void & p = a; // forbidden 

I can't say I'm really happy with this particular difference. I would much prefer it would be allowed with the meaning reference to anything with an address and otherwise the same behavior for references. It would allow to define some equivalents of C library functions like memcpy using references.

Both references and pointers can be used to change local variables of one function inside another function. Both of them can also be used to save copying of big objects when passed as arguments to functions or returned from functions, to get efficiency gain. Despite above similarities, there are following differences between references and pointers.

References are less powerful than pointers

1)一旦创build了一个引用,以后就不能引用另一个对象; 它不能被重新安装。 这通常是用指针来完成的。

2)引用不能为NULL。 指针通常被设置为NULL来表示它们没有指向任何有效的东西。

3)声明时必须初始化一个引用。 指针没有这样的限制

Due to the above limitations, references in C++ cannot be used for implementing data structures like Linked List, Tree, etc. In Java, references don't have above restrictions, and can be used to implement all data structures. References being more powerful in Java, is the main reason Java doesn't need pointers.

参考文献更安全,更易于使用:

1)更安全:由于引用必须被初始化,像野指针这样的通配符不可能存在。 仍然有可能引用不引用有效的位置

2)易于使用:引用不需要解引用操作符来访问值。 它们可以像正常variables一样使用。 只有在申报时才需要'&'运算符。 而且,对象引用的成员可以使用点运算符('。')来访问,与需要使用箭头运算符( – >)来访问成员的指针不同。

Together with the above reasons, there are few places like copy constructor argument where pointer cannot be used. Reference must be used pass the argument in copy constructor. Similarly references must be used for overloading some operators like ++ .

Also, a reference that is a parameter to a function that is inlined may be handled differently than a pointer.

 void increment(int *ptrint) { (*ptrint)++; } void increment(int &refint) { refint++; } void incptrtest() { int testptr=0; increment(&testptr); } void increftest() { int testref=0; increment(testref); } 

Many compilers when inlining the pointer version one will actually force a write to memory (we are taking the address explicitly). However, they will leave the reference in a register which is more optimal.

Of course, for functions that are not inlined the pointer and reference generate the same code and it's always better to pass intrinsics by value than by reference if they are not modified and returned by the function.

Maybe some metaphors will help; In the context of your desktop screenspace –

  • A reference requires you to specify an actual window.
  • A pointer requires the location of a piece of space on screen that you assure it will contain zero or more instances of that window type.

Another interesting use of references is to supply a default argument of a user-defined type:

 class UDT { public: UDT() : val_d(33) {}; UDT(int val) : val_d(val) {}; virtual ~UDT() {}; private: int val_d; }; class UDT_Derived : public UDT { public: UDT_Derived() : UDT() {}; virtual ~UDT_Derived() {}; }; class Behavior { public: Behavior( const UDT &udt = UDT() ) {}; }; int main() { Behavior b; // take default UDT u(88); Behavior c(u); UDT_Derived ud; Behavior d(ud); return 1; } 

The default flavor uses the 'bind const reference to a temporary' aspect of references.

The difference is that non-constant pointer variable(not to be confused with a pointer to constant) may be changed at some time during program execution, requires pointer semantics to be used(&,*) operators, while references can be set upon initialization only(that's why you can set them in constructor initializer list only, but not somehow else) and use ordinary value accessing semantics. Basically references were introduced to allow support for operators overloading as I had read in some very old book. As somebody stated in this thread – pointer can be set to 0 or whatever value you want. 0(NULL, nullptr) means that the pointer is initialized with nothing. It is an error to dereference null pointer. But actually the pointer may contain a value that doesn't point to some correct memory location. References in their turn try not to allow a user to initialize a reference to something that cannot be referenced due to the fact that you always provide rvalue of correct type to it. Although there are a lot of ways to make reference variable be initialized to a wrong memory location – it is better for you not to dig this deep into details. On machine level both pointer and reference work uniformly – via pointers. Let's say in essential references are syntactic sugar. rvalue references are different to this – they are naturally stack/heap objects.

Difference between pointer and reference

A pointer can be initialized to 0 and a reference not. In fact, a reference must also refer to an object, but a pointer can be the null pointer:

 int* p = 0; 

But we can't have int& p = 0; and also int& p=5 ;

In fact to do it properly, we must have declared and defined an object at the first then we can make a reference to that object, so the correct implementation of the previous code will be:

 Int x = 0; Int y = 5; Int& p = x; Int& p1 = y; 

Another important point is that is we can make the declaration of the pointer without initialization however no such thing can be done in case of reference which must make a reference always to variable or object. However such use of a pointer is risky so generally we check if the pointer is actually is pointing to something or not. In case of a reference no such check is necessary, because we know already that referencing to an object during declaration is mandatory.

Another difference is that pointer can point to another object however reference is always referencing to the same object, let's take this example:

 Int a = 6, b = 5; Int& rf = a; Cout << rf << endl; // The result we will get is 6, because rf is referencing to the value of a. rf = b; cout << a << endl; // The result will be 5 because the value of b now will be stored into the address of a so the former value of a will be erased 

Another point: When we have a template like an STL template such kind of a class template will always return a reference, not a pointer, to make easy reading or assigning new value using operator []:

 Std ::vector<int>v(10); // Initialize a vector with 10 elements V[5] = 5; // Writing the value 5 into the 6 element of our vector, so if the returned type of operator [] was a pointer and not a reference we should write this *v[5]=5, by making a reference we overwrite the element by using the assignment "=" 

I feel like there is yet another point that hasn't been covered here.

Unlike the pointers, references are syntactically equivalent to the object they refer to, ie any operation that can be applied to an object works for a reference, and with the exact same syntax (the exception is of course the initialization).

While this may appear superficial, I believe this property is crucial for a number of C++ features, for example:

  • Templates . Since template parameters are duck-typed, syntactic properties of a type is all that matters, so often the same template can be used with both T and T& .
    (or std::reference_wrapper<T> which still relies on an implicit cast to T& )
    Templates that cover both T& and T&& are even more common.

  • Lvalues . Consider the statement str[0] = 'X'; Without references it would only work for c-strings ( char* str ). Returning the character by reference allows user-defined classes to have the same notation.

  • Copy constructors . Syntactically it makes sense to pass objects to copy constructors, and not pointers to objects. But there is just no way for a copy constructor to take an object by value – it would result in a recursive call to the same copy constructor. This leaves references as the only option here.

  • Operator overloads . With references it is possible to introduce indirection to an operator call – say, operator+(const T& a, const T& b) while retaining the same infix notation. This also works for regular overloaded functions.

These points empower a considerable part of C++ and the standard library so this is quite a major property of references.

I always decide by this rule from C++ Core Guidelines:

Prefer T* over T& when "no argument" is a valid option

There is a very important non-technical different between pointers and references: A argument passed to a function by pointer is much more visible than an argument passed to a function by non-const reference. 例如:

 void fn1(std::string s); void fn2(const std::string& s); void fn3(std::string& s); void fn4(std::string* s); void bar() { std::string x; fn1(x); // Cannot modify x fn2(x); // Cannot modify x (without const_cast) fn3(x); // CAN modify x! fn4(&x); // Can modify x (but is obvious about it) } 

Back in C, a call that looks like fn(x) can only be pass by value, so it definitely cannot modify x ; to modify an argument you would need to pass a pointer fn(&x) . So if an argument wasn't preceeded by an & you knew it would not be modified. (The converse, & means modified, was not true because you would sometimes have to pass large read-only structures by const pointer.)

Some argue that this is such a useful feature when reading code, that pointer parameters should always be used for modifiable parameters rather than non- const references, even if the function never expects a nullptr . That is, those people argue that function signatures like fn3() above should not be allowed. Google's C++ style guidelines are an example of this.