指针安全 #

一、内存对齐 #

1.1 对齐概念 #

每种类型都有特定的对齐要求:

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

pub fn main() void {
    std.debug.print("u8 alignment: {}\n", .{@alignOf(u8)});
    std.debug.print("u16 alignment: {}\n", .{@alignOf(u16)});
    std.debug.print("u32 alignment: {}\n", .{@alignOf(u32)});
    std.debug.print("u64 alignment: {}\n", .{@alignOf(u64)});
}

1.2 指针对齐 #

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

pub fn main() void {
    var value: u32 = 42;
    
    // 默认对齐
    const ptr1: *u32 = &value;
    
    // 显式指定对齐
    const ptr2: *align(@alignOf(u32)) u32 = &value;
    
    std.debug.print("ptr1 alignment: {}\n", .{@alignOf(@TypeOf(ptr1))});
    std.debug.print("ptr2 alignment: {}\n", .{@alignOf(@TypeOf(ptr2))});
}

1.3 自定义对齐 #

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

const AlignedStruct = extern struct {
    a: u8 align(16),
    b: u32 align(8),
};

pub fn main() void {
    std.debug.print("AlignedStruct.a alignment: {}\n", .{@alignOf(@TypeOf(AlignedStruct.a))});
    std.debug.print("AlignedStruct.b alignment: {}\n", .{@alignOf(@TypeOf(AlignedStruct.b))});
    std.debug.print("AlignedStruct size: {}\n", .{@sizeOf(AlignedStruct)});
}

二、空指针检查 #

2.1 可选指针 #

Zig 使用可选类型处理可能为空的指针:

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

fn getItem(index: usize) ?*i32 {
    var items = [_]i32{ 10, 20, 30 };
    if (index < items.len) {
        return &items[index];
    }
    return null;
}

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

2.2 allowzero #

特殊情况下允许零地址:

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

pub fn main() void {
    // 普通指针不能为零
    // var ptr: *i32 = null;  // 编译错误
    
    // allowzero 允许零地址
    const ptr: *allowzero i32 = @ptrFromInt(0);
    
    std.debug.print("Pointer address: {}\n", .{@intFromPtr(ptr)});
}

2.3 空指针解引用保护 #

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

pub fn main() void {
    const ptr: ?*i32 = null;
    
    // 安全解包
    if (ptr) |p| {
        std.debug.print("Value: {}\n", .{p.*});
    } else {
        std.debug.print("Pointer is null\n", .{});
    }
    
    // 使用 orelse
    const value = (ptr orelse return).*;
    _ = value;
}

三、volatile 指针 #

3.1 硬件寄存器访问 #

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

const UART = struct {
    var data: u8 = 0;
    var status: u8 = 0;
};

pub fn main() void {
    // volatile 防止编译器优化
    const data_reg: *volatile u8 = &UART.data;
    const status_reg: *volatile u8 = &UART.status;
    
    // 写入数据
    data_reg.* = 'A';
    
    // 读取状态
    _ = status_reg.*;
    
    std.debug.print("UART access simulated\n", .{});
}

3.2 内存映射 I/O #

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

const HardwareRegisters = extern struct {
    control: u32 align(4),
    status: u32 align(4),
    data: u32 align(4),
};

pub fn main() void {
    var regs: HardwareRegisters = .{
        .control = 0,
        .status = 0,
        .data = 0,
    };
    
    const reg_ptr: *volatile HardwareRegisters = &regs;
    
    // 写入控制寄存器
    reg_ptr.control = 0x01;
    
    // 读取状态寄存器
    const status = reg_ptr.status;
    
    std.debug.print("Status: {}\n", .{status});
}

四、指针边界检查 #

4.1 数组边界检查 #

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

pub fn main() void {
    var arr = [_]i32{ 1, 2, 3, 4, 5 };
    
    // 安全访问
    const value = arr[2];
    std.debug.print("arr[2] = {}\n", .{value});
    
    // 边界检查(Debug 模式会 panic)
    // const out_of_bounds = arr[10];  // panic
}

4.2 切片边界检查 #

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

pub fn main() void {
    var arr = [_]i32{ 1, 2, 3, 4, 5 };
    const slice = arr[1..4];
    
    std.debug.print("slice.len = {}\n", .{slice.len});
    
    // 安全访问
    std.debug.print("slice[0] = {}\n", .{slice[0]});
    
    // 边界检查
    // std.debug.print("slice[10] = {}\n", .{slice[10]});  // panic
}

五、指针类型安全 #

5.1 类型转换 #

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

pub fn main() void {
    var value: u32 = 0x12345678;
    
    // 安全的类型转换
    const byte_ptr: *u8 = @ptrCast(&value);
    std.debug.print("First byte: {x}\n", .{byte_ptr.*});
    
    // 使用 alignCast 调整对齐
    const aligned_ptr: *align(1) u32 = @alignCast(@ptrCast(byte_ptr));
    std.debug.print("Aligned value: {x}\n", .{aligned_ptr.*});
}

5.2 指针别名 #

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

pub fn main() void {
    var value: i32 = 42;
    
    // 指针别名
    const ptr1: *i32 = &value;
    const ptr2: *i32 = &value;
    
    ptr1.* = 100;
    std.debug.print("ptr2.* = {}\n", .{ptr2.*});
}

六、安全实践 #

6.1 使用切片代替裸指针 #

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

// 好:使用切片
fn processSlice(data: []const u8) void {
    for (data) |byte| {
        std.debug.print("{x} ", .{byte});
    }
    std.debug.print("\n", .{});
}

// 避免:使用裸指针
fn processPointer(data: [*]const u8, len: usize) void {
    for (0..len) |i| {
        std.debug.print("{x} ", .{data[i]});
    }
    std.debug.print("\n", .{});
}

pub fn main() void {
    const data = "Hello";
    processSlice(data);
    processPointer(data, data.len);
}

6.2 使用可选类型 #

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

// 好:使用可选类型
fn findItem(id: u32) ?*Item {
    if (id == 0) return null;
    return &items[id];
}

// 避免:使用 allowzero
fn findItemBad(id: u32) *allowzero Item {
    if (id == 0) return @ptrFromInt(0);
    return &items[id];
}

const Item = struct { value: i32 };
var items = [_]Item{ .{ .value = 10 }, .{ .value = 20 } };

pub fn main() void {
    if (findItem(1)) |item| {
        std.debug.print("Item value: {}\n", .{item.value});
    }
}

七、总结 #

指针安全要点:

特性 说明
对齐 align(N)
可选指针 ?*T
allowzero 允许零地址
volatile 硬件访问
边界检查 Debug 模式

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

最后更新:2026-03-27