指针基础 #
一、指针概念 #
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