指针基础 #

一、指针概念 #

1.1 什么是指针 #

指针是存储内存地址的变量,通过指针可以直接操作内存:

zig
const std = @import("std");

pub fn main() void {
    var value: i32 = 42;
    const ptr: *i32 = &value;
    
    std.debug.print("value = {}\n", .{value});
    std.debug.print("ptr points to {}\n", .{ptr.*});
    
    ptr.* = 100;
    std.debug.print("value after modification = {}\n", .{value});
}

1.2 取地址与解引用 #

zig
const std = @import("std");

pub fn main() void {
    var x: i32 = 10;
    
    // 取地址
    const ptr: *i32 = &x;
    
    // 解引用
    const value = ptr.*;
    
    std.debug.print("x = {}\n", .{x});
    std.debug.print("ptr = {*}\n", .{ptr});
    std.debug.print("ptr.* = {}\n", .{value});
}

二、单指针 (*T) #

2.1 基本用法 #

单指针 *T 指向单个值:

zig
const std = @import("std");

pub fn main() void {
    var num: i32 = 42;
    const ptr: *i32 = #
    
    ptr.* = 100;
    std.debug.print("num = {}\n", .{num});
}

2.2 指针作为函数参数 #

zig
const std = @import("std");

fn increment(ptr: *i32) void {
    ptr.* += 1;
}

pub fn main() void {
    var value: i32 = 10;
    increment(&value);
    std.debug.print("value = {}\n", .{value});
}

2.3 const 指针 #

zig
const std = @import("std");

pub fn main() void {
    var value: i32 = 42;
    
    // 可修改指向值的指针
    const mut_ptr: *i32 = &value;
    mut_ptr.* = 100;
    
    // 只读指针
    const const_ptr: *const i32 = &value;
    // const_ptr.* = 200;  // 编译错误
    
    std.debug.print("value = {}\n", .{value});
}

三、多指针 ([*]T) #

3.1 基本用法 #

多指针 [*]T 指向多个连续值(类似 C 指针):

zig
const std = @import("std");

pub fn main() void {
    var arr = [_]i32{ 1, 2, 3, 4, 5 };
    const ptr: [*]i32 = &arr;
    
    std.debug.print("ptr[0] = {}\n", .{ptr[0]});
    std.debug.print("ptr[1] = {}\n", .{ptr[1]});
    std.debug.print("ptr[2] = {}\n", .{ptr[2]});
}

3.2 指针运算 #

zig
const std = @import("std");

pub fn main() void {
    var arr = [_]i32{ 10, 20, 30, 40, 50 };
    var ptr: [*]i32 = &arr;
    
    std.debug.print("ptr[0] = {}\n", .{ptr[0]});
    
    ptr += 2;  // 指针前进
    std.debug.print("ptr[0] after += 2 = {}\n", .{ptr[0]});
    
    ptr -= 1;  // 指针后退
    std.debug.print("ptr[0] after -= 1 = {}\n", .{ptr[0]});
}

3.3 多指针与切片的区别 #

zig
const std = @import("std");

pub fn main() void {
    var arr = [_]i32{ 1, 2, 3, 4, 5 };
    
    // 多指针:无长度信息
    const multi_ptr: [*]i32 = &arr;
    std.debug.print("multi_ptr type: {}\n", .{@TypeOf(multi_ptr)});
    
    // 切片:有长度信息
    const slice: []i32 = arr[0..];
    std.debug.print("slice type: {}\n", .{@TypeOf(slice)});
    std.debug.print("slice.len = {}\n", .{slice.len});
}

四、切片指针 ([]T) #

4.1 切片作为指针 #

切片本质上是一个包含指针和长度的结构:

zig
const std = @import("std");

pub fn main() void {
    var arr = [_]i32{ 1, 2, 3, 4, 5 };
    const slice: []i32 = arr[0..];
    
    std.debug.print("slice.len = {}\n", .{slice.len});
    std.debug.print("slice.ptr type = {}\n", .{@TypeOf(slice.ptr)});
    
    // 修改元素
    slice[0] = 100;
    std.debug.print("arr[0] = {}\n", .{arr[0]});
}

4.2 切片与函数 #

zig
const std = @import("std");

fn sumSlice(slice: []const i32) i32 {
    var total: i32 = 0;
    for (slice) |item| {
        total += item;
    }
    return total;
}

fn doubleSlice(slice: []i32) void {
    for (slice) |*item| {
        item.* *= 2;
    }
}

pub fn main() void {
    var arr = [_]i32{ 1, 2, 3, 4, 5 };
    
    std.debug.print("Sum: {}\n", .{sumSlice(&arr)});
    
    doubleSlice(&arr);
    std.debug.print("Doubled: {any}\n", .{arr});
}

五、可选指针 #

5.1 可选指针 (?*T) #

zig
const std = @import("std");

fn findItem(id: u32) ?*i32 {
    var items = [_]i32{ 10, 20, 30 };
    if (id < 3) {
        return &items[id];
    }
    return null;
}

pub fn main() void {
    if (findItem(1)) |ptr| {
        std.debug.print("Found: {}\n", .{ptr.*});
    } else {
        std.debug.print("Not found\n", .{});
    }
}

5.2 可选指针的内存布局 #

可选指针使用 0 表示 null,不需要额外空间:

zig
const std = @import("std");

pub fn main() void {
    std.debug.print("*i32 size: {}\n", .{@sizeOf(*i32)});
    std.debug.print("?*i32 size: {}\n", .{@sizeOf(?*i32)});
    
    std.debug.print("[*]i32 size: {}\n", .{@sizeOf([*]i32)});
    std.debug.print("?[*]i32 size: {}\n", .{@sizeOf(?[*]i32)});
}

六、指针安全 #

6.1 对齐 #

Zig 强制指针类型对齐:

zig
const std = @import("std");

pub fn main() void {
    var value: i32 = 42;
    
    // 默认对齐
    const ptr: *i32 = &value;
    
    // 指定对齐
    const aligned_ptr: *align(4) i32 = &value;
    
    std.debug.print("ptr = {}\n", .{ptr.*});
    std.debug.print("aligned_ptr = {}\n", .{aligned_ptr.*});
}

6.2 allowzero #

使用 allowzero 允许零地址指针:

zig
const std = @import("std");

pub fn main() void {
    const ptr: *allowzero i32 = @ptrFromInt(0);
    
    if (@intFromPtr(ptr) == 0) {
        std.debug.print("Pointer is zero\n", .{});
    }
}

6.3 volatile #

使用 volatile 标记硬件访问:

zig
const std = @import("std");

pub fn main() void {
    var hardware_reg: u32 = 0;
    const reg: *volatile u32 = &hardware_reg;
    
    reg.* = 0xFF;
    const value = reg.*;
    
    std.debug.print("Register value: {}\n", .{value});
}

七、指针转换 #

7.1 类型转换 #

zig
const std = @import("std");

pub fn main() void {
    var value: u32 = 0x12345678;
    
    // 转换为字节指针
    const bytes: *const [4]u8 = @ptrCast(&value);
    
    std.debug.print("Bytes: {x:0>2} {x:0>2} {x:0>2} {x:0>2}\n", .{
        bytes[0], bytes[1], bytes[2], bytes[3],
    });
}

7.2 指针与整数转换 #

zig
const std = @import("std");

pub fn main() void {
    var value: i32 = 42;
    
    // 指针转整数
    const addr: usize = @intFromPtr(&value);
    std.debug.print("Address: {x}\n", .{addr});
    
    // 整数转指针
    const ptr: *i32 = @ptrFromInt(addr);
    std.debug.print("Value: {}\n", .{ptr.*});
}

八、实战示例 #

8.1 链表节点 #

zig
const std = @import("std");

const Node = struct {
    value: i32,
    next: ?*Node,
};

pub fn main() void {
    var node3 = Node{ .value = 3, .next = null };
    var node2 = Node{ .value = 2, .next = &node3 };
    var node1 = Node{ .value = 1, .next = &node2 };
    
    var current: ?*Node = &node1;
    while (current) |node| {
        std.debug.print("{} -> ", .{node.value});
        current = node.next;
    }
    std.debug.print("null\n", .{});
}

8.2 缓冲区操作 #

zig
const std = @import("std");

fn copyBytes(dest: [*]u8, src: [*]const u8, len: usize) void {
    var i: usize = 0;
    while (i < len) : (i += 1) {
        dest[i] = src[i];
    }
}

pub fn main() void {
    const source = "Hello, World!";
    var buffer: [20]u8 = undefined;
    
    copyBytes(&buffer, source, source.len);
    buffer[source.len] = 0;
    
    std.debug.print("Copied: {s}\n", .{@as([*:0]u8, @ptrCast(&buffer))});
}

九、总结 #

指针类型要点:

类型 说明
*T 单指针
[*]T 多指针
[]T 切片
?*T 可选指针
*const T 只读指针

下一步,让我们学习结构体!

最后更新:2026-03-27