@@ -10,7 +10,7 @@ outline: deep
1010
1111** 取地址** :通过 ` & ` 符号来获取某个变量所对应的内存地址,如 ` &integer ` 就是获取变量 ` integer ` 的内存地址。
1212
13- 与 C 不同,Zig 中的指针类型有多种,主要是对指向的元素做了区分 ,便于更好地使用。下图展示了它们指向元素的不同:
13+ 与 C 不同,Zig 中的指针类型要分为两种(一种是单项指针,一种是多项指针),它们主要是对指向的元素做了区分 ,便于更好地使用。下图展示了它们指向元素的不同:
1414
1515![ pointer representation] ( /picture/basic/pointer-representation.svg )
1616
@@ -36,23 +36,32 @@ zig 本身支持指针运算(加减操作),但有一点需要注意:最
3636
3737<<<@/code/release/pointer.zig#single_pointer
3838
39+ 单项指针本身支持以下操作:
40+
41+ - 解引用语法 ` ptr.* `
42+ - 切片语法 ` ptr[0..1] `
43+ - 指针减法 ` ptr - ptr `
44+
3945:::info 🅿️ 提示
4046
41- 函数指针略有特殊:` const Call2Op = *const fn (a: i8, b: i8) i8; `
47+ 函数指针略有特殊:
48+
49+ <<<@/code/release/pointer.zig#fn_pointer
4250
4351:::
4452
4553## 多项指针
4654
47- 多项指针指向位置数量的多个元素 。
55+ 多项指针指向未知数量的多个元素 。
4856
4957多项指针的类型为:` [*]T ` ,` T ` 是所指向内存区域的类型,且该类型必须具有明确的大小(这意味着它不能是 [ ` anyopaque ` ] ( https://ziglang.org/documentation/master/#toc-C-Type-Primitives ) 和其他任意[ 不透明类型] ( https://ziglang.org/documentation/master/#opaque ) )。
5058
5159解引用方法支持以下几种:
5260
5361- 索引语法 ` ptr[i] `
54- - 切片语法 ` ptr[start..end] `
55- - 指针运算 ` ptr + x ` ,` ptr - x `
62+ - 切片语法 ` ptr[start..end] ` 和 ` ptr[start..] `
63+ - 指针运算 ` ptr + int ` , ` ptr - int `
64+ - 指针减法 ` ptr - ptr `
5665
5766<<<@/code/release/pointer.zig#multi_pointer
5867
@@ -62,9 +71,21 @@ zig 本身支持指针运算(加减操作),但有一点需要注意:最
6271
6372` *[N]T ` :这是指向一个数组的单项指针,数组的长度为 N。也可以将其理解为指向 N 个元素的指针。
6473
74+ 支持这些语法:
75+
76+ - 索引语法:` array_ptr[i] `
77+ - 切片语法:` array_ptr[start..end] `
78+ - ` len ` 属性:` array_ptr.len `
79+ - 指针减法:` array_ptr - array_ptr `
80+
6581` []T ` :这是切片,相当于一个胖指针,包含了一个类型为 ` [*]T ` 的指针和一个长度。
6682
67- 数组指针的类型中就包含了长度信息,而切片中则实际存储着长度。数组指针和切片的长度都可以通过 ` len ` 属性来获取。
83+ 支持这些语法:
84+
85+ - 索引语法:` slice[i] `
86+ - 切片语法:` slice[start..end] `
87+ - ` len ` 属性:` slice.len `
88+ 数组指针的类型中就包含了长度信息,而切片中则实际存储着长度。数组指针和切片的长度都可以通过 ` len ` 属性来获取。
6889
6990:::details 示例
7091
@@ -122,6 +143,20 @@ zig 本身支持指针运算(加减操作),但有一点需要注意:最
122143
123144其中 ` [*] const u8 ` 可以看作是 C 中的 ` * const char ` ,这是因为在 C 语言中一个普通的指针也可以指向一个数组,zig 仅仅是单独把这种令人迷惑的行为单独作为一个语法而已!
124145
146+ ## 指针和整数互转
147+
148+ [ ` @ptrFromInt ` ] ( https://ziglang.org/documentation/master/#ptrFromInt ) 可以将整数地址转换为指针,[ ` @intFromPtr ` ] ( https://ziglang.org/documentation/master/#intFromPtr ) 可以将指针转换为整数:
149+
150+ <<<@/code/release/pointer.zig#ptr2int
151+
152+ ## 指针强制转换
153+
154+ 内置函数 [ ` @ptrCast ` ] ( https://ziglang.org/documentation/master/#ptrCast ) 可以将将指针的元素类型转换为另一种类型,也就是不同类型的指针强制转换。
155+
156+ 一般情况下,应当尽量避免使用 ` @ptrCast ` ,这会创建一个新的指针,根据通过它的加载和存储操作,可能导致无法检测的非法行为。
157+
158+ <<<@/code/release/pointer.zig#ptr_cast
159+
125160## 额外特性
126161
127162以下的是指针的额外特性,初学者可以直接略过以下部分,等到你需要时再来学习即可!
@@ -142,7 +177,7 @@ zig 本身支持指针运算(加减操作),但有一点需要注意:最
142177
143178> 如果你不知道内存对齐的含义是什么,那么本节内容你可以跳过了,等到你需要时再来查看!
144179
145- 每种类型都有一个对齐方式——数个字节 ,这样,当从内存加载或存储该类型的值时,内存地址必须能被该数字整除。我们可以使用 ` @alignOf ` 找出任何类型的内存对齐大小。
180+ 每种类型都有一个对齐方式——也就是数个字节 ,这样,当从内存加载或存储该类型的值时,内存地址必须能被该数字整除。我们可以使用 ` @alignOf ` 找出任何类型的内存对齐大小。
146181
147182内存对齐大小取决于 CPU 架构,但始终是 2 的幂,并且小于 1 << 29。
148183::: info
@@ -168,6 +203,51 @@ zig 本身支持指针运算(加减操作),但有一点需要注意:最
168203
169204:::
170205
206+ 如果有一个指针或切片,它的对齐很小,但我们知道它实际上有一个更大的对齐,那么使用 [ ` @alignCast ` ] ( https://ziglang.org/documentation/master/#alignCast ) 让其 ` align ` 更大。在运行时是无操作的,但会额外加入一个 [ 安全检查] ( https://ziglang.org/documentation/master/#Incorrect-Pointer-Alignment ) :
207+
208+ > 例如这段代码就是错误的,不会被正常执行
209+
210+ ``` zig
211+ const std = @import("std");
212+
213+ test "pointer alignment safety" {
214+ var array align(4) = [_]u32{ 0x11111111, 0x11111111 };
215+ const bytes = std.mem.sliceAsBytes(array[0..]);
216+ try std.testing.expect(foo(bytes) == 0x11111111);
217+ }
218+ fn foo(bytes: []u8) u32 {
219+ const slice4 = bytes[1..5];
220+ const int_slice = std.mem.bytesAsSlice(u32, @as([]align(4) u8, @alignCast(slice4)));
221+ return int_slice[0];
222+ }
223+ ```
224+
225+ ``` sh
226+ $ zig test test_incorrect_pointer_alignment.zig
227+ 1/1 test_incorrect_pointer_alignment.test.pointer alignment safety...thread 958173 panic: incorrect alignment
228+ /home/ci/actions-runner/_work/zig-bootstrap/zig/doc/langref/test_incorrect_pointer_alignment.zig:10:68: 0x1048962 in foo (test)
229+ const int_slice = std.mem.bytesAsSlice(u32, @as([]align(4) u8, @alignCast(slice4)));
230+ ^
231+ /home/ci/actions-runner/_work/zig-bootstrap/zig/doc/langref/test_incorrect_pointer_alignment.zig:6:31: 0x104880f in test.pointer alignment safety (test)
232+ try std.testing.expect(foo(bytes) == 0x11111111);
233+ ^
234+ /home/ci/actions-runner/_work/zig-bootstrap/out/host/lib/zig/compiler/test_runner.zig:214:25: 0x10efab9 in mainTerminal (test)
235+ if (test_fn.func()) | _| {
236+ ^
237+ /home/ci/actions-runner/_work/zig-bootstrap/out/host/lib/zig/compiler/test_runner.zig:62:28: 0x10e7ead in main (test)
238+ return mainTerminal ();
239+ ^
240+ /home/ci/actions-runner/_work/zig-bootstrap/out/host/lib/zig/std/start.zig:647:22: 0x10e7430 in posixCallMainAndExit (test)
241+ root.main ();
242+ ^
243+ /home/ci/actions-runner/_work/zig-bootstrap/out/host/lib/zig/std/start.zig:271:5: 0x10e6ffd in _start (test)
244+ asm volatile (switch (native_arch) {
245+ ^
246+ ??? :? :? : 0x0 in ??? (??? )
247+ error: the following test command crashed:
248+ /home/ci/actions-runner/_work/zig-bootstrap/out/zig-local-cache/o/608e4a8451ecb0974638281c85927599/test --seed=0x9bc870fd
249+ ` ` `
250+
171251# ## 零指针
172252
173253零指针实际上是一个未定义的错误行为([Pointer Cast Invalid Null](https://ziglang.org/documentation/master/# Pointer-Cast-Invalid-Null)),但是当我们给指针增加上 `allowzero` 修饰符后,它就变成合法的行为了!
@@ -185,3 +265,7 @@ zig 本身支持指针运算(加减操作),但有一点需要注意:最
185265只要代码不依赖于未定义的内存布局,那么指针也可以在编译期发挥作用!
186266
187267<<< @/code/release/pointer.zig#comptime_pointer
268+
269+ 只要指针从未被取消引用,Zig 就能够保留 ` comptime` 代码中的内存地址:
270+
271+ <<< @/code/release/pointer.zig#comp_pointer
0 commit comments