在 Zig 中,引用本身(即指针变量)通常存储在栈上,但它指向的数据可以位于堆或栈上。
pub fn main() void {
var x: i32 = 42; // x 存储在栈上
const ptr = &x; // ptr 变量(引用)本身存储在栈上
std.debug.print("指针地址: {*}\\n", .{ptr});
}
在这个例子中:
x 是一个整数,存储在栈上ptr 是一个指向 x 的指针变量,它本身也存储在栈上ptr 的值是 x 的内存地址pub fn example() void {
var local_var: i32 = 100; // 栈上分配
const ref = &local_var; // 引用指向栈上的数据
// 危险:不要返回指向局部变量的引用!
// return ref; // 这会导致悬垂指针!
}
pub fn createOnHeap(allocator: std.mem.Allocator) !*i32 {
// 在堆上分配内存
const ptr = try allocator.create(i32);
ptr.* = 200; // 解引用并赋值
// 安全:堆内存会持续存在,直到手动释放
return ptr;
}
| 概念 | 存储位置 | 生命周期管理 |
|---|---|---|
| 引用变量本身 | 栈上 | 自动(随作用域结束而销毁) |
| 指向栈的数据 | 栈上 | 自动(随作用域结束而销毁) |
| 指向堆的数据 | 堆上 | 手动(需要显式释放) |
const std = @import("std");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
// 情况1:引用指向栈上的数据
{
var stack_value: i32 = 10;
const stack_ref = &stack_value; // 引用存储在栈上,指向栈数据
std.debug.print("栈引用值: {}\\n", .{stack_ref.*});
} // stack_value 和 stack_ref 在这里自动销毁
// 情况2:引用指向堆上的数据
{
const heap_ref = try allocator.create(i32); // 引用存储在栈上,指向堆数据
heap_ref.* = 20;
std.debug.print("堆引用值: {}\\n", .{heap_ref.*});
allocator.destroy(heap_ref); // 必须手动释放!
}
// 情况3:引用作为函数参数(仍然在栈上)
const value: i32 = 30;
printValue(&value); // 传递指向栈上数据的引用
}
fn printValue(ptr: *const i32) void {
// ptr 参数本身在栈上(作为函数参数)
std.debug.print("函数参数引用值: {}\\n", .{ptr.*});
}
理解引用存储位置的重要性: