指针和引用

本文深入探讨了C与C++中指针和引用的区别与联系,特别是从汇编层面剖析了两者的工作原理,并解释了在实际编程中的一些常见行为差异。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

The most important difference between  pointers in C and those in C++ is that C++ is a more strongly typed language.This stands out where void * is concerne.

代码:

bird * b;

rock * r;

void * v;

v=r;

b=v;//这两个赋值在C编译器通过,在C++编译器不通过

void * 在C中表示指向任意的类型. 在C++中表示指向void类型.

 

A reference (&) is like a const pointer that is automatically dereferenced.There are certain rules when using references.

1.A reference must be initialized when it is created.(Pointers can be intialized at any time).

2.Once a reference is initialized to an object,it can not be changed to refer to another object.

(Pointers can  be pointed to another object at any time).

3.you can not have NULL reference.

把引用认为是一个对象的别名,上面规则就好理解了.

下面看一段代码的汇编来理解

#include <iostream>
using namespace std;

int* f1(int * px)
{
  (*px)++;
  return px;

}

int& g1(int & rx)
{
 rx++;
 return rx;
}

int main()
{
 int a=1;
   f1(&a);
   g1(a);
   return 0;
}

 

  1. 6:
  2. 17:   int main()
  3. 18:   {
  4. 004012E0   push        ebp
  5. 004012E1   mov         ebp,esp
  6. 004012E3   sub         esp,44h
  7. 004012E6   push        ebx
  8. 004012E7   push        esi
  9. 004012E8   push        edi
  10. 004012E9   lea         edi,[ebp-44h]
  11. 004012EC   mov         ecx,11h
  12. 004012F1   mov         eax,0CCCCCCCCh
  13. 004012F6   rep stos    dword ptr [edi]
  14. 19:       int a=1;
  15. 004012F8   mov         dword ptr [ebp-4],1//SS:[ebp-4]存放的是a变量的值
  16. 20:      f1(&a);
  17. 004012FF   lea         eax,[ebp-4]//eax存的是a变量的地址
  18. 00401302   push        eax//函数的参数入栈
  19. 00401303   call        @ILT+10(f1) (0040100f)//跟进去
  20. /*
  21. 00401308   add         esp,4
  22. 21:      g1(a);
  23. 0040130B   lea         ecx,[ebp-4]
  24. 0040130E   push        ecx
  25. 0040130F   call        @ILT+20(g1) (00401019)
  26. 00401314   add         esp,4
  27. 22:      return 0;
  28. 00401317   xor         eax,eax
  29. 23:   }
  30. 00401319   pop         edi
  31. 0040131A   pop         esi
  32. 0040131B   pop         ebx
  33. 0040131C   add         esp,44h
  34. 0040131F   cmp         ebp,esp
  35. 00401321   call        __chkesp (004081f0)
  36. 00401326   mov         esp,ebp
  37. 00401328   pop         ebp
  38. 00401329   ret
  39. */
  40. @ILT+10(?f1@@YAPAHPAH@Z):
  41. 0040100F   jmp         f1 (00401260)
  42. 4:    int* f1(int * px)
  43. 5:    {
  44. 00401260   push        ebp
  45. 00401261   mov         ebp,esp
  46. 00401263   sub         esp,40h
  47. 00401266   push        ebx
  48. 00401267   push        esi
  49. 00401268   push        edi
  50. 00401269   lea         edi,[ebp-40h]
  51. 0040126C   mov         ecx,10h
  52. 00401271   mov         eax,0CCCCCCCCh
  53. 00401276   rep stos    dword ptr [edi]
  54. 6:      (*px)++;
  55. 00401278   mov         eax,dword ptr [ebp+8]
  56. //注意这句 ebp+8指的内存里存的是先前eax的值,即a的地址.
  57. //所以操作后 eax是a的地址.
  58. 0040127B   mov         ecx,dword ptr [eax]//ecx=*px
  59. 0040127D   add         ecx,1
  60. 00401280   mov         edx,dword ptr [ebp+8]//edx=&px注意
  61. 00401283   mov         dword ptr [edx],ecx//这句改变了a的值,
  62. 7:      return px;
  63. 00401285   mov         eax,dword ptr [ebp+8]
  64. 8:
  65. 9:    }
  66. 00401288   pop         edi
  67. 00401289   pop         esi
  68. 0040128A   pop         ebx
  69. 0040128B   mov         esp,ebp
  70. 0040128D   pop         ebp
  71. 0040128E   ret
  72. 返回到main函数
  73. 00401308   add         esp,4 //移除函数参数
  74. 21:      g1(a);
  75. 0040130B   lea         ecx,[ebp-4]//注意和前面的指针调用一模一样
  76. 0040130E   push        ecx//函数参数入栈
  77. 0040130F   call        @ILT+20(g1) (00401019)跟进去
  78. @ILT+20(?g1@@YAAAHAAH@Z):
  79. 00401019   jmp         g1 (004012a0)
  80.  int& g1(int & rx)
  81. 12:   {
  82. 004012A0   push        ebp
  83. 004012A1   mov         ebp,esp
  84. 004012A3   sub         esp,40h
  85. 004012A6   push        ebx
  86. 004012A7   push        esi
  87. 004012A8   push        edi
  88. 004012A9   lea         edi,[ebp-40h]
  89. 004012AC   mov         ecx,10h
  90. 004012B1   mov         eax,0CCCCCCCCh
  91. 004012B6   rep stos    dword ptr [edi]
  92. 13:       rx++;
  93. //我们来和指针做个比较,代码一模一样
  94. /*
  95. 6:      (*px)++;
  96. 00401278   mov         eax,dword ptr [ebp+8]
  97. //注意这句 ebp+8指的内存里存的是先前eax的值,即a的地址.
  98. //所以操作后 eax是a的地址.
  99. 0040127B   mov         ecx,dword ptr [eax]//ecx=*px
  100. 0040127D   add         ecx,1
  101. 00401280   mov         edx,dword ptr [ebp+8]//edx=&px注意
  102. 00401283   mov         dword ptr [edx],ecx//这句改变了a的值,
  103. 7:      return px;
  104. 00401285   mov         eax,dword ptr [ebp+8]
  105. */
  106. 004012B8   mov         eax,dword ptr [ebp+8]
  107. 004012BB   mov         ecx,dword ptr [eax]
  108. 004012BD   add         ecx,1
  109. 004012C0   mov         edx,dword ptr [ebp+8]
  110. 004012C3   mov         dword ptr [edx],ecx
  111. 14:       return rx;
  112. 004012C5   mov         eax,dword ptr [ebp+8]
  113. 15:   }
  114. 004012C8   pop         edi
  115. 004012C9   pop         esi
  116. 004012CA   pop         ebx
  117. 004012CB   mov         esp,ebp
  118. 004012CD   pop         ebp
  119. 004012CE   ret
  120. 返回到main函数
  121. 00401314   add         esp,4//移除函数参数
  122. 22:      return 0;
  123. 00401317   xor         eax,eax
  124. 23:   }
  125. 00401319   pop         edi
  126. 0040131A   pop         esi
  127. 0040131B   pop         ebx
  128. 0040131C   add         esp,44h
  129. 0040131F   cmp         ebp,esp
  130. 00401321   call        __chkesp (004081f0)也跟进去看看
  131. 00401326   mov         esp,ebp
  132. 00401328   pop         ebp
  133. 00401329   ret
  134. _chkesp:
  135. 004081F0   jne         __chkesp+3 (004081f3)//不相等就转去处理异常了
  136. 004081F2   ret
  137. 004081F3   push        ebp
  138. 004081F4   mov         ebp,esp
  139. 004081F6   sub         esp,0
  140. 004081F9   push        eax
  141. 004081FA   push        edx
  142. 004081FB   push        ebx
  143. 004081FC   push        esi
  144. 004081FD   push        edi
  145. 004081FE   push        offset string "The value of ESP was not properl"... (004312b4)
  146. 00408203   push        offset string "" (004311b4)
  147. 00408208   push        2Ah
  148. 0040820A   push        offset string "i386//chkesp.c" (004312a4)
  149. 0040820F   push        1
  150. 00408211   call        _CrtDbgReport (0040ca70)
  151. 00408216   add         esp,14h
  152. 00408219   cmp         eax,1
  153. 0040821C   jne         __chkesp+2Fh (0040821f)
  154. 0040821E   int         3
  155. 0040821F   pop         edi
  156. 00408220   pop         esi
  157. 00408221   pop         ebx
  158. 00408222   pop         edx
  159. 00408223   pop         eax
  160. 00408224   mov         esp,ebp
  161. 00408226   pop         ebp
  162. 00408227   ret

总结:从此例汇编层来看,引用和指针是没有区别的.

 

那我就有一个地方想不明白了

#include <iostream>

using namespace std;

int main()

{

  int a=3;

  int *pa=&a;

  int &  ra=a;

 cout<<&pa<<endl;

cout<<&ra<<endl;

cout<<&a<<endl;

return 0;

}

结果:

0012FF78

0012FF7C

0012FF7C

这两个输出为什么不同?

继续跟踪汇编


1:    #include <iostream>
2:
3:    using namespace std;
4:
5:    int main()
6:
7:    {
00401250   push        ebp
00401251   mov         ebp,esp
00401253   sub         esp,54h//(54h-40h)/04h=20/4=5
00401256   push        ebx
00401257   push        esi
00401258   push        edi
00401259   lea         edi,[ebp-54h]
0040125C   mov         ecx,15h
00401261   mov         eax,0CCCCCCCCh
00401266   rep stos    dword ptr [edi]
8:
9:      int a=3;
00401268   mov         dword ptr [ebp-4],3//第一个局部变量a
10:
11:     int *pa=&a;
0040126F   lea         eax,[ebp-4]
00401272   mov         dword ptr [ebp-8],eax //第二个局部变量pa
12:
13:     int &  ra=a;
00401275   lea         ecx,[ebp-4]
00401278   mov         dword ptr [ebp-0Ch],ecx // 第三个局部变量ra


14:     int * pb=&a;
0040127B   lea         edx,[ebp-4]
0040127E   mov         dword ptr [ebp-10h],edx//第四个局部变量pb
15:           pb=&ra;
00401281   mov         eax,dword ptr [ebp-0Ch]
00401284   mov         dword ptr [ebp-10h],eax


16:     int ** pc=&pa;
00401287   lea         ecx,[ebp-8]//第五个局部变量
0040128A   mov         dword ptr [ebp-14h],ecx
17:
18:
19:   return 0;
0040128D   xor         eax,eax
20:
21:   }
0040128F   pop         edi
00401290   pop         esi
00401291   pop         ebx
00401292   mov         esp,ebp
00401294   pop         ebp
00401295   ret
总结:

搞明白了,引用其实就是一个指针,只是编译器会自动对它解析,并进行代码优化.

也就是 A reference (&) is like a const pointer that is automatically dereferenced.

所以上面的代码

 

cout<<&ra<<endl;编译器对它做了转化  cout<< &(*ra)<<endl;

<think>嗯,用户想了解C++中指针引用的区别使用场景。我先回忆一下自己学过的知识。指针引用都是用来间接访问变量的,但它们的语法用途有所不同。首先,指针是一个变量,存储的是另一个变量的内存地址,而引用是变量的别名,必须在声明时初始化,之后不能改变指向。比如,引用一旦指向某个变量,就不能再指向其他变量了,而指针可以重新赋值指向不同的地址。 然后,使用场景方面,指针更灵活,可以指向nullptr,这在某些情况下很有用,比如函数参数可选的时候。但引用必须绑定到一个对象,所以不能为空。另外,引用在函数参数传递时更简洁,不需要取地址操作,比如swap函数用引用参数的话,调用时直接传变量,而用指针的话需要取地址符号&amp;。 不过用户可能想知道更具体的区别,比如内存管理上的不同。指针需要手动管理,可能引发内存泄漏或野指针的问题,而引用因为必须初始化且不能重新绑定,相对安全一些。比如在函数参数传递时,使用引用可以避免指针的解引用操作,代码更易读。 还有,用户提供的引用资料里提到,swap函数的不同实现方式,用指针引用都能达到交换变量的目的。这说明在函数需要修改外部变量时,引用指针都可以用,但引用更简洁。另外,二级指针的例子,比如用指针指针来修改指针的指向,这种情况下引用可能无法替代,必须用指针。 可能用户还会混淆引用的底层实现,其实引用在底层通常也是通过指针实现的,但语法上更安全。此外,操作符重载函数返回时,引用可能更合适,比如返回函数内部静态变量或动态分配的内存时,可以用引用避免拷贝。 用户可能没有问,但需要补充的是,引用的不可变性,比如不能有空引用,必须在声明时初始化,而指针可以是nullptr或者未初始化的,这也是使用指针需要注意的地方。还有,在容器中存储元素时,比如STL容器,通常使用指针而不是引用,因为引用不能重新赋值,而容器可能需要改变元素的位置或值。 总结一下,回答应该包括定义、初始化、是否可为空、操作方式、内存管理、使用场景等方面的对比,以及具体的例子说明两者的区别。同时,需要结合用户提供的引用资料中的例子,比如swap函数的不同实现,二级指针的使用,来具体说明指针引用的适用情况。</think>### C++ 指针引用的区别及使用场景 #### 一、基本概念与核心区别 1. **定义与初始化** - **指针**:存储变量内存地址的变量,可独立存在且允许未初始化(但存在野指针风险)。例如: ```cpp int* p; // 未初始化,可能指向无效地址 ``` - **引用**:是变量的别名,**必须初始化**且不可重新绑定其他对象。例如: ```cpp int x = 10; int&amp; ref = x; // 正确 int&amp; ref2; // 错误!引用必须初始化 ``` 2. **是否可为空** - 指针可赋值为 `nullptr` 表示空值,例如: ```cpp int* p = nullptr; // 合法 ``` - 引用必须绑定有效对象,**不可为空**,例如以下代码会编译错误: ```cpp int&amp; ref = nullptr; // 非法! ``` 3. **操作方式** - 指针需要通过解引用操作符 `*` 访问目标值,例如: ```cpp int x = 10; int* p = &amp;x; cout << *p; // 输出10 ``` - 引用直接像普通变量一样使用,例如: ```cpp int&amp; ref = x; cout << ref; // 输出10 ``` #### 二、使用场景对比 1. **函数参数传递** - **指针**:需显式传递地址,适合需要修改原始值或处理可选参数的场景。例如: ```cpp void modify(int* ptr) { if (ptr) *ptr = 20; // 检查指针是否有效 } ``` - **引用**:语法简洁,无需取地址,适合强制要求有效参数的场景。例如: ```cpp void swap(int&amp; a, int&amp; b) { // 引用参数直接操作原变量 int tmp = a; a = b; b = tmp; } ``` 2. **动态内存管理** - **指针**:必须用于动态内存分配(如 `new` `delete`),例如: ```cpp int* arr = new int[10]; delete[] arr; ``` - **引用**:不能直接管理动态内存,但可绑定到动态分配的对象: ```cpp int* p = new int(5); int&amp; ref = *p; // 合法,但需手动释放内存 ``` 3. **多级间接访问** - **指针**:支持多级指针(如二级指针 `int**`),用于修改指针的指向。例如: ```cpp void redirect(int** pp) { *pp = new int(100); } // 修改一级指针的地址[^3] ``` - **引用**:可通过引用传递指针,但无法直接替代多级指针的功能。 4. **返回值优化** - **引用**:常用于返回函数内部静态变量或链式操作,避免拷贝开销。例如: ```cpp string&amp; getGlobalString() { static string s = "hello"; return s; // 返回引用避免复制 } ``` - **指针**:需注意返回局部变量的指针会导致未定义行为。 #### 三、性能与安全性 - **性能**引用通常由编译器实现为指针,两者效率相同[^1]。 - **安全性**引用更安全(不可为空且无需解引用),而指针需手动检查有效性。 #### 四、典型示例对比 1. **交换函数实现** - 指针版本: ```cpp void swap(int* a, int* b) { int tmp = *a; *a = *b; *b = tmp; } // 调用方式:swap(&amp;i, &amp;j); ``` - 引用版本: ```cpp void swap(int&amp; a, int&amp; b) { int tmp = a; a = b; b = tmp; } // 调用方式:swap(i, j); // 更简洁[^4] ``` #### 五、总结 | **特性** | **指针** | **引用** | |----------------|----------------------------------|----------------------------------| | 初始化 | 可延迟初始化 | 必须初始化 | | 空值 | 支持 `nullptr` | 不支持 | | 重定向 | 可修改指向 | 不可重新绑定 | | 内存管理 | 需手动处理 | 自动绑定,无直接管理职责 | | 适用场景 | 动态内存、可选参数、多级间接访问 | 函数参数、返回值优化、简化代码 |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值