int* p //声明指针变量
p //获取所存储的地址
*p //获取p存储的地址所指向的值
int& r = p //声明引用
&r //获取r变量的内存地址
通过 & + 变量名获取变量的内存地址(数组可以通过数组名也可通过&数组名[0]获取地址,因为C++将数组名解释为首个元素的内存地址),知道数据的内存地址后,需要访问这个地址才能修改或读取里面的值
通过dereference解引用运算符* + 指针变量取值。需要注意指针已被赋值,指向有效的内存地址。
使用指针时*的位置很重要,必要时可添加括号来明确语义
指针
现在讨论的是原始指针(raw)不是智能指针(Smart)
定义:指针是一个变量,存储的是另一个变量的内存地址,只是一串整数。通过指针可以间接访问内存。
为什么需要区别指针变量与普通变量:告知编译器存的是int的值还是另一个变量的地址,任何语言都有这个需求,只是将其包装为引用
指针的类型
指针的类型不影响指针本身的大小和存储一串地址的本质,指针变量所占的内存空间只由系统的位数决定,与指针类型无关。sizeof(指针变量)计算的是指针变量所占用的内存大小,不管指针指向的是什么类型的数据,指针P本身所占的大小只与系统是32位还是64位有关,32位是4,64位是8
指针的类型只是在读写內存里的数据时有意义
- 决定在解引用时引用多少个字节
- 指针+1-1操作时,内存跳过几个字节
不同类型的指针用来存放对应类型变量的地址
- char/int/float/double 类型的指针是为了存放对应类型变量的地址;
- void* 空指针,可以指向任意类型的数据
const
在左,值不变。const
在右,指针不变
空指针
是一个特殊的指针值NuLL(非有效的内存地址,但其存在是允许的),用于初始化指针变量,避免指针指向未知的内存区域。注意空指针不是void*
int* ptr = NULL;
野指针 Dangling Pointer
指向无效内存地址的指针,访问野指针会导致未定义行为(Undefined Behavior),可能导致程序崩溃、数据损坏,有以下引发原因:
- 指针指向的内存被释放(例如通过
free
或delete
),但指针本身没有被置为NULL
int* ptr = new int(10); // 动态分配内存
delete ptr; // 释放内存
// ptr 现在是一个野指针,因为它仍然指向已释放的内存
指针本身可以存储在栈上,但它可以指向栈上的数据,也可以指向堆上的数据
如果你直接定义一个 int
变量,例如 int x = 10;
,那么这个 int
变量是存储在栈上的。栈上的变量生命周期与其作用域绑定,当作用域结束时(例如函数返回),栈上的变量会被自动销毁。
如果你使用 new
动态分配内存,例如 int* ptr = new int(10);
,那么这个 int
是存储在堆上的。堆上的变量生命周期由程序员控制,必须显式释放(通过 delete
),否则会导致内存泄漏
- 指针指向一个局部变量,但该变量已经离开了其作用域(例如函数返回后)。
int* getLocalPointer() {
int x = 10;
return &x; // 返回局部变量的地址
}
int* ptr = getLocalPointer();
// ptr 现在是一个野指针,因为 x 已经离开了作用域
ptr
指向的内存是在堆上分配的堆内存的生命周期不受函数作用域的限制 :在程序运行时,堆内存的分配和释放是由程序员显式控制的,而不是由变量的作用域自动管理的。即使分配堆内存的函数已经执行完毕(即函数的作用域结束),堆内存仍然存在,直到程序员显式释放它(例如通过 delete
或 free
)。
x
是函数 getLocalPointer
中的局部变量,存储在栈上。当函数 getLocalPointer
返回时,x
的作用域结束,栈上的内存会被自动释放。函数返回 &x
,即局部变量 x
的地址。由于 x
的作用域已经结束,x
的内存已经被释放,因此返回的指针 &x
指向的内存是无效的。
建议:
- 在释放内存后,将指针置为
NULL
或nullptr
。
int* ptr = new int(10);
delete ptr;
ptr = nullptr; // 置空指针,避免野指针
- 避免返回局部变量的地址
int* getValidPointer() {
int* ptr = new int(10); // 动态分配内存
return ptr; // 返回指向堆内存的指针
}
- 使用智能指针(如
std::unique_ptr
或std::shared_ptr
)可以自动管理内存,避免野指针问题
#include <memory>
std::unique_ptr<int> ptr = std::make_unique<int>(10);
// 不需要手动释放内存,智能指针会自动管理
- 在使用指针之前,检查指针是否为
NULL
或nullptr
if (ptr != nullptr) {
// 安全使用指针
}
引用
引用是一个变量的别名,本身并不是一个变量,所以必须在初始化时绑定到一个变量。比如有一个变量int a,如果想给他起个别名b,int &b = a
对于引用,没有什么是指针不能做的,但是引用能让代码看起来更简单,引用只是指针的语法糖