指针安全 #
一、内存对齐 #
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 = ®s;
// 写入控制寄存器
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