指针与引用
1️⃣ 指针
什么是指针?
指针: 指向另外一种类型的复合类型。
指针的大小及用法?
在 64 位计算机中,指针占 8 个字节空间。
1 |
|
指针的用法
- 指向普通对象的指针
- 常量指针
- 函数指针
- 指向对象成员的指针,包括指向
对象成员函数的指针和指向对象成员变量的指针。 - this 指针:指向类的当前对象的指针常量。
什么是野指针和悬空指针?
悬空指针
- 若指针指向一块内存空间
- 当这块内存空间被释放后,该指针依然指向这块内存空间
- 此时,称该指针为”悬空指针”。
1
2
3void *p = malloc(size);
free(p);
// 此时,p 指向的内存空间已释放, p 就是悬空指针。野指针
- 不确定其指向的指针,未初始化的指针为“野指针”
1
2void *p;
// 此时 p 是“野指针”。
常量指针和指针常量的区别是什么?
- 常量指针
- 常量指针本质上是个指针,只不过这个指针指向的对象是常量。
const的位置在指针声明运算符*的左侧。- 指针指向的对象不能通过这个指针来修改,也就是说常量指针可以被赋值为变量的地址,之所以叫做常量指针,是限制了通过这个指针修改变量的值。
- 三个特点
one
1
2const int * p;
int const * p;two
1
2
3
4
5
6
7
8
9
10
11
using namespace std;
int main()
{
const int c_var = 8;
const int *p= &c_var;
*p=6; // error: assignment of read-only location'*p'
return 0;
}three
1
2
3
4
5
6
7
8
9
10
using namespace std;
int main()
{
const int c_var1= 8;
const int c_var2 = 8;
const int *p = &c_var1;
p= &c_var2;
return 0;
}
- 指针常量
- 指针常量的本质上是个常量,只不过这个常量的值是一个指针
- const 位于指针声明操作符右侧,表明该对象本身是一个常量
- 三个特点
one
1
2int var;
int * const c_p = &var;two
1
2
3
4
5
6
7
8
9
10
11
using namespace std;
int main()
{
int var,varl;
int * const c_p = &var;
c_p = &var1; // error: assignment of read-only variable 'c_p'
return 0;
}three
1
2
3
4
5
6
7
8
9
10
11
using namespace std;
int main()
{
int var = 3;
int * const c_p = &var;
*c_p = 12;
return 0;
}
指针函数和函数指针的区别是什么?
指针函数
指针函数本质是一个函数,只不过该函数的返回值是一个指针。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
using namespace std;
struct Type
{
int varl;
int var2;
};
Type* fun(int tmpl, int tmp2){
Type* t = new Type();
t->var1 = tmp1;
t->var2 = tmp2;
return t;
}
int main()
{
Type *p = fun(5,6);
return 0;
}
函数指针
函数指针本质是一个指针变量,只不过这个指针指向一个函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using namespace std;
int fun1(int tmpl,int tmp2)
{
return tmp1 * tmp2;
}
int fun2(int tmp1,int tmp2)
{
return tmp1 / tmp2;
}
int main()
{
int (*fun)(int x,int y);
fun = fun1;
cout << fun(15,5) << endl;
fun= fun2;
cout << fun(15,5) << endl;
return 0;
}
2️⃣ 指针和引用的区别
- 指针所指向的内存空间在程序运行过程中可以改变,而引用所绑定的对象一旦绑定就不能改变。(是否可变)
- 指针本身在内存中占有内存空间,引用相当于变量的别名,在内存中不占内存空间。(是否占内存)
- 指针可以为空,但是引用必须绑定对象。(是否可为空)
- 指针可以有多级,但是引用只能一级。(是否能为多级)
“引用” 的底层实现原理详解
案例引入
1 |
|
运行结果
1 | a:address->0031FD54 |
现象?
- 我们发现引用
b的地址和变量a的地址是一样的。 - 可能有人会猜想:是不是说变量
a和引用b本身就是一个东西。所以同样的,引用本身所占内存就是变量的内存。 - 首先对于这个说法,肯定是不正确的, 至于为什么不正确,我们接下来会以底层原理为大家解释。
原理
案例代码
1
2
3
4
5
6
7
8
9
10
using namespace std;
int main()
{
int x=1;
int &b=x;
return 0;
}上述代码转汇编后的汇编代码
1
2
3
4
59: int x = 1; //源代码
00401048 mov dword ptr [ebp-4],1 //反汇编代码
10: int &b = x; //源代码
0040104F lea eax,[ebp-4] //反汇编代码
00401052 mov dword ptr [ebp-8],eax//反汇编代码- 解释
上述三行代码的作用就是将 1 赋值给
x,然后将x的地址赋值给了引用b。
- 解释
底层分析
- 引用的本质就是所引用对象的地址
- 通俗点理解就是引用就是通过指针来实现的
- 引用所占的内存大小就是指针的大小。
为什么在测试代码中,引用的地址和变量的地址是一样的?
- 事实上,
b的地址我们没法通过&b获得。 - 因为编译器会将
&b解释为:&(*b) = &x,所以&b将得到&x。
- 事实上,
3️⃣指针、引用与值传递
指针、引用与值传递的区别?
- 值传递
- 值传递是指将参数的值复制一份,传递给函数或方法进行操作。
- 在值传递中,函数或方法对参数进行修改不会影响到原始的变量值。
- 指针与引用
- 指针和引用是指将参数的内存地址传递给函数或方法,使得函数或方法可以直接访问和修改原始变量的值。
- 在指针和引用中,函数或方法对参数的修改会直接反映在原始变量上。
函数传参用引用的优点是什么?
- 可以避免避免拷贝,使用引用传参可以避免对大型对象进行复制。
- 如果传递一个对象作为值参数,会触发对象的拷贝构造函数,造成额外的开销。
- 而使用引用传参,可以直接在函数中操作原始对象,避免了拷贝操作。
参数传递时,值传递、引用传递、指针传递的区别?
参数传递的三种方式
- 值传递:形参是实参的拷贝,函数对形参的所有操作不会影响实参。
- 指针传递:本质上是值传递,只不过拷贝的是指针的值,拷贝之后,实参和形参是不同的指针,,通过指针可以间接的访问指针所指向的对象,从而可以修改它所指对象的值。
- 引用传递:当形参是引用类型时,我们说它对应的实参被引用传递。
案例代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using namespace std;
void fun1(int tmp) { // 值传递
cout << &tmp << endl;
}
void fun2(int * tmp){ // 指针传递
cout << tmp << endl;
}
void fun3(int &tmp){ // 引用传递
cout << &tmp << endl;
}
int main()
{
int var = 5;
cout << "var 在主函数中的地址:" << &var << endl;
cout << "var 值传递时的地址:";
fun1(var);
cout << "var 指针传递时的地址:";
fun2(&var);
cout << "var 引用传递时的地址:";
fun3(var);
return 0;
}运行结果
1
2
3
4var 在主函数中的地址:0x23fe4c
var 值传递时的地址:0x23fe20
var 指针传递时的地址:0x23fe4c
var 引用传递时的地址:0x23fe4c
参考资料:
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 GYu的妙妙屋!
