c++ 代码的野指针问题
- 原理
-
- 类似vector 数据结构,内部指向堆内存的指针会因为扩容,导致堆内存指针修改位置。原有的堆内存被释放
-
- 如果在扩容前保存vector 内部对象指针,扩容后指针就会指向一块被释放的堆内存
-
- 后续分配新变量的堆内存的时候,可能会重用被释放的堆内存
-
- 代码
#include <iostream>
#include <vector>
using namespace std;
int main()
{
// 创建一个 vector,初始插入 3 个元素
std::vector<int> v = {1, 2, 3};
std::cout << "初始状态:\n";
std::cout << "size: " << v.size() << ", capacity: " << v.capacity() << "\n"; // size=3, capacity 可能为 3(取决于编译器)
std::cout << "元素地址:" << &v[0] << " (v[0] = " << v[0] << ")\n\n";
// 保存指向第一个元素的外部指针(此时指向原内存)
int *ptr = &v[0];
std::cout << "外部指针初始指向:" << ptr << " (值:" << *ptr << ")\n\n";
// 插入第 4 个元素,触发扩容(因为原 capacity 为 3,无法容纳 4 个元素)
v.push_back(4);
std::cout << "插入第 4 个元素后:\n";
std::cout << "size: " << v.size() << ", capacity: " << v.capacity() << "\n"; // capacity 可能变为 6(GCC 通常扩容为原容量的 2 倍)
std::cout << "新元素地址:" << &v[0] << " (v[0] = " << v[0] << ")\n\n";
// 尝试通过原指针访问(此时 ptr 已成为悬挂指针)
std::cout << "访问悬挂指针:\n";
std::cout << "指针地址:" << ptr << ",值:" << *ptr << "\n"; // 未定义行为!可能输出垃圾值、崩溃或程序异常
// 新变量分配堆内存
std::vector<int> v2 = {4, 5};
std::cout << "\n新元素v2地址:" << &v2[0] << " (v2[0] = " << v2[0] << ")\n\n";
std::cout << "悬挂指针指向的内存,被新分配之后,访问悬挂指针:\n";
std::cout << "指针地址:" << ptr << ",值:" << *ptr << "\n";
return 0;
}
- 输出结果如下:
ljl@ljl-lenovo:dangling$ g++ main.cpp -o main
ljl@ljl-lenovo:dangling$ ./main
初始状态:
size: 3, capacity: 3
元素地址:0x614c9d3332b0 (v[0] = 1)
外部指针初始指向:0x614c9d3332b0 (值:1)
插入第 4 个元素后:
size: 4, capacity: 6
新元素地址:0x614c9d3336e0 (v[0] = 1)
访问悬挂指针:
指针地址:0x614c9d3332b0,值:348771123
新元素v2地址:0x614c9d3332b0 (v2[0] = 4)
悬挂指针指向的内存,被新分配之后,访问悬挂指针:
指针地址:0x614c9d3332b0,值:4
rust 解决野指针问题,会在编译器就发现它
fn main() {
let mut v = vec![1, 2, 3];
let elem_ref = &v[0]; // 获取元素引用
// 编译错误!无法在持有元素引用时修改 Vec(会导致引用失效)
v.push(4);
println!("{}", elem_ref); // 若允许,此处的 elem_ref 已失效
}
在编译错误如下
ljl@ljl-lenovo:dangling$ cargo build
Compiling dangling v0.1.0 (/mnt/d/work/rust/ex/dangling)
error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
--> main.rs:6:5
|
3 | let elem_ref = &v[0]; // 获取元素引用
| - immutable borrow occurs here
...
6 | v.push(4);
| ^^^^^^^^^ mutable borrow occurs here
7 |
8 | println!("{}", elem_ref); // 若允许,此处的 elem_ref 已失效
| -------- immutable borrow later used here