mirror of https://github.com/zigcc/zig-course
Compare commits
28 Commits
v_25_02_22
...
main
Author | SHA1 | Date |
---|---|---|
![]() |
f29e75485e | |
![]() |
27cb21c464 | |
![]() |
f660fa507a | |
![]() |
d8583a4cca | |
![]() |
d17fa29016 | |
![]() |
2d69582631 | |
![]() |
0e899dc3c4 | |
![]() |
13b3b6b5f3 | |
![]() |
cfc9d415ef | |
![]() |
75b25178c7 | |
![]() |
1b64748024 | |
![]() |
4f66c62f5b | |
![]() |
d207f3b9a3 | |
![]() |
3879de3da0 | |
![]() |
af5dfdcbab | |
![]() |
a09a235748 | |
![]() |
343b47ad2e | |
![]() |
03b55d6031 | |
![]() |
6ecf949669 | |
![]() |
0516f48e97 | |
![]() |
aa47ec8878 | |
![]() |
6cc966d511 | |
![]() |
e6063e9b8b | |
![]() |
12147b12df | |
![]() |
d0efc2cf21 | |
![]() |
c281392fe2 | |
![]() |
bc2bfcd738 | |
![]() |
8443e04d64 |
|
@ -4,6 +4,8 @@
|
|||
>
|
||||
> Zig 是一种通用的编程语言和工具链,用于维护健壮、最优和可重用的软件
|
||||
|
||||

|
||||
|
||||
## 参与贡献
|
||||
|
||||
欢迎各位志同道合的“道友”参与贡献本文档,并一起壮大 zig 中文社区!
|
||||
|
|
|
@ -10,6 +10,7 @@ pub fn build(b: *Build) void {
|
|||
12 => @import("build/0.12.zig").build(b),
|
||||
13 => @import("build/0.13.zig").build(b),
|
||||
14 => @import("build/0.14.zig").build(b),
|
||||
15 => @import("build/0.15.zig").build(b),
|
||||
else => @compileError("unknown zig version"),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,9 +22,9 @@ pub fn build(b: *Build) void {
|
|||
// open dir
|
||||
var dir =
|
||||
std.fs.openIterableDirAbsolute(full_path, .{}) catch |err| {
|
||||
log.err("open 11 path failed, err is {}", .{err});
|
||||
std.os.exit(1);
|
||||
};
|
||||
log.err("open 11 path failed, err is {}", .{err});
|
||||
std.os.exit(1);
|
||||
};
|
||||
defer dir.close();
|
||||
|
||||
// make a iterate for path
|
||||
|
|
|
@ -6,7 +6,7 @@ const log = std.log.scoped(.For_0_13_0);
|
|||
|
||||
const args = [_][]const u8{ "zig", "build" };
|
||||
|
||||
const version = "release";
|
||||
const version = "13";
|
||||
|
||||
const relative_path = "course/code/" ++ version;
|
||||
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
const std = @import("std");
|
||||
const Build = std.Build;
|
||||
const ChildProcess = std.process.Child;
|
||||
|
||||
const log = std.log.scoped(.For_0_14_0);
|
||||
|
||||
const args = [_][]const u8{ "zig", "build" };
|
||||
|
||||
const version = "15";
|
||||
|
||||
const relative_path = "course/code/" ++ version;
|
||||
|
||||
pub fn build(b: *Build) void {
|
||||
// get target and optimize
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
var lazy_path = b.path(relative_path);
|
||||
|
||||
const full_path = lazy_path.getPath(b);
|
||||
|
||||
// open dir
|
||||
var dir = std.fs.openDirAbsolute(full_path, .{ .iterate = true }) catch |err| {
|
||||
log.err("open 14 path failed, err is {}", .{err});
|
||||
std.process.exit(1);
|
||||
};
|
||||
defer dir.close();
|
||||
|
||||
// make a iterate for release ath
|
||||
var iterate = dir.iterate();
|
||||
|
||||
while (iterate.next()) |val| {
|
||||
if (val) |entry| {
|
||||
// get the entry name, entry can be file or directory
|
||||
const output_name = std.mem.trimRight(u8, entry.name, ".zig");
|
||||
if (entry.kind == .file) {
|
||||
|
||||
// connect path
|
||||
const path = std.fs.path.join(b.allocator, &[_][]const u8{ relative_path, entry.name }) catch |err| {
|
||||
log.err("fmt path for examples failed, err is {}", .{err});
|
||||
std.process.exit(1);
|
||||
};
|
||||
|
||||
// build exe
|
||||
const exe = b.addExecutable(.{
|
||||
.name = output_name,
|
||||
.root_source_file = b.path(path),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
exe.linkLibC();
|
||||
|
||||
// add to default install
|
||||
b.installArtifact(exe);
|
||||
|
||||
// build test
|
||||
const unit_tests = b.addTest(.{
|
||||
.root_source_file = b.path(path),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
// add to default install
|
||||
b.getInstallStep().dependOn(&b.addRunArtifact(unit_tests).step);
|
||||
} else if (entry.kind == .directory) {
|
||||
|
||||
// build child process
|
||||
var child = ChildProcess.init(&args, b.allocator);
|
||||
|
||||
// build cwd
|
||||
const cwd = std.fs.path.join(b.allocator, &[_][]const u8{
|
||||
full_path,
|
||||
entry.name,
|
||||
}) catch |err| {
|
||||
log.err("fmt path for examples failed, err is {}", .{err});
|
||||
std.process.exit(1);
|
||||
};
|
||||
|
||||
// open entry dir
|
||||
const entry_dir = std.fs.openDirAbsolute(cwd, .{}) catch unreachable;
|
||||
entry_dir.access("build.zig", .{}) catch {
|
||||
log.err("not found build.zig in path {s}", .{cwd});
|
||||
std.process.exit(1);
|
||||
};
|
||||
|
||||
// set child cwd
|
||||
// this api maybe changed in the future
|
||||
child.cwd = cwd;
|
||||
|
||||
// spawn and wait child process
|
||||
_ = child.spawnAndWait() catch unreachable;
|
||||
}
|
||||
} else {
|
||||
// Stop endless loop
|
||||
break;
|
||||
}
|
||||
} else |err| {
|
||||
log.err("iterate examples_path failed, err is {}", .{err});
|
||||
std.process.exit(1);
|
||||
}
|
||||
}
|
10
bun.lock
10
bun.lock
|
@ -6,8 +6,8 @@
|
|||
"@giscus/vue": "^3.1.1",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.13.5",
|
||||
"prettier": "^3.5.1",
|
||||
"@types/node": "^22.14.0",
|
||||
"prettier": "^3.5.3",
|
||||
"vitepress": "^1.6.3",
|
||||
"vitepress-export-pdf": "^1.0.0",
|
||||
},
|
||||
|
@ -204,7 +204,7 @@
|
|||
|
||||
"@types/mdurl": ["@types/mdurl@2.0.0", "", {}, "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg=="],
|
||||
|
||||
"@types/node": ["@types/node@22.13.5", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-+lTU0PxZXn0Dr1NBtC7Y8cR21AJr87dLLU953CWA6pMxxv/UDc7jYAY90upcrie1nRcD6XNG5HOYEDtgW5TxAg=="],
|
||||
"@types/node": ["@types/node@22.14.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw=="],
|
||||
|
||||
"@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="],
|
||||
|
||||
|
@ -548,7 +548,7 @@
|
|||
|
||||
"preact": ["preact@10.20.1", "", {}, "sha512-JIFjgFg9B2qnOoGiYMVBtrcFxHqn+dNXbq76bVmcaHYJFYR4lW67AOcXgAYQQTDYXDOg/kTZrKPNCdRgJ2UJmw=="],
|
||||
|
||||
"prettier": ["prettier@3.5.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-hPpFQvHwL3Qv5AdRvBFMhnKo4tYxp0ReXiPn2bxkiohEX6mBeBwEpBSQTkD458RaaDKQMYSp4hX4UtfUTA5wDw=="],
|
||||
"prettier": ["prettier@3.5.3", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw=="],
|
||||
|
||||
"progress": ["progress@2.0.3", "", {}, "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="],
|
||||
|
||||
|
@ -654,7 +654,7 @@
|
|||
|
||||
"unbzip2-stream": ["unbzip2-stream@1.4.3", "", { "dependencies": { "buffer": "^5.2.1", "through": "^2.3.8" } }, "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg=="],
|
||||
|
||||
"undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
"undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
|
||||
|
||||
"unicode-trie": ["unicode-trie@0.3.1", "", { "dependencies": { "pako": "^0.2.5", "tiny-inflate": "^1.0.0" } }, "sha512-WgVuO0M2jDl7hVfbPgXv2LUrD81HM0bQj/bvLGiw6fJ4Zo8nNFnDrA0/hU2Te/wz6pjxCm5cxJwtLjo2eyV51Q=="],
|
||||
|
||||
|
|
|
@ -33,4 +33,9 @@ export default defineConfig({
|
|||
],
|
||||
["meta", { name: "theme-color", content: "#ffffff" }],
|
||||
],
|
||||
markdown: {
|
||||
image: {
|
||||
lazyLoading: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -99,6 +99,10 @@ export default [
|
|||
text: "联合类型",
|
||||
link: "/basic/union",
|
||||
},
|
||||
{
|
||||
text: "零位类型",
|
||||
link: "/basic/zero-type",
|
||||
},
|
||||
{
|
||||
text: "流程控制",
|
||||
collapsed: true,
|
||||
|
@ -147,31 +151,44 @@ export default [
|
|||
text: "内存管理",
|
||||
link: "/advanced/memory_manage",
|
||||
},
|
||||
{
|
||||
text: "异步",
|
||||
link: "/advanced/async",
|
||||
},
|
||||
|
||||
{
|
||||
text: "编译期",
|
||||
link: "/advanced/comptime",
|
||||
},
|
||||
{
|
||||
text: "包管理",
|
||||
link: "/advanced/package_management",
|
||||
text: "反射",
|
||||
link: "/advanced/reflection",
|
||||
},
|
||||
{
|
||||
text: "异步",
|
||||
link: "/advanced/async",
|
||||
},
|
||||
{
|
||||
text: "汇编",
|
||||
link: "/advanced/assembly",
|
||||
},
|
||||
{
|
||||
text: "原子操作",
|
||||
link: "/advanced/atomic",
|
||||
},
|
||||
{
|
||||
text: "与 C 交互",
|
||||
link: "/advanced/interact-with-c",
|
||||
},
|
||||
{
|
||||
text: "未定义行为",
|
||||
link: "/advanced/undefined_behavior",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "工程化",
|
||||
items: [
|
||||
{
|
||||
text: "包管理",
|
||||
link: "/engineering/package_management",
|
||||
},
|
||||
{
|
||||
text: "构建系统",
|
||||
link: "/engineering/build-system",
|
||||
|
@ -180,34 +197,9 @@ export default [
|
|||
text: "单元测试",
|
||||
link: "/engineering/unit-test",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "更多",
|
||||
items: [
|
||||
{
|
||||
text: "反射",
|
||||
link: "/more/reflection",
|
||||
},
|
||||
{
|
||||
text: "零位类型",
|
||||
link: "/more/zero-type",
|
||||
},
|
||||
{
|
||||
text: "原子操作",
|
||||
link: "/more/atomic",
|
||||
},
|
||||
{
|
||||
text: "未定义行为",
|
||||
link: "/more/undefined_behavior",
|
||||
},
|
||||
{
|
||||
text: "风格指南",
|
||||
link: "/more/style_guide",
|
||||
},
|
||||
{
|
||||
text: "杂项",
|
||||
link: "/more/miscellaneous",
|
||||
link: "/engineering/style_guide",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -225,13 +217,14 @@ export default [
|
|||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "0.12.0 升级指南",
|
||||
link: "/update/upgrade-0.12.0",
|
||||
text: "0.14.0 升级指南",
|
||||
link: "/update/upgrade-0.14.0",
|
||||
},
|
||||
{
|
||||
text: "0.12.0 版本说明",
|
||||
link: "/update/0.12.0-description",
|
||||
text: "0.14.0 版本说明",
|
||||
link: "/update/0.14.0-description",
|
||||
},
|
||||
|
||||
{
|
||||
text: "0.13.0 升级指南",
|
||||
link: "/update/upgrade-0.13.0",
|
||||
|
@ -240,6 +233,14 @@ export default [
|
|||
text: "0.13.0 版本说明",
|
||||
link: "/update/0.13.0-description",
|
||||
},
|
||||
{
|
||||
text: "0.12.0 升级指南",
|
||||
link: "/update/upgrade-0.12.0",
|
||||
},
|
||||
{
|
||||
text: "0.12.0 版本说明",
|
||||
link: "/update/0.12.0-description",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
const version = "0.13.0";
|
||||
const version = "0.14.0";
|
||||
|
||||
export { version };
|
||||
|
|
|
@ -8,6 +8,9 @@ outline: deep
|
|||
|
||||
目前 zig 提供了一些内建函数来进行原子操作,并且提供了 `std.atomic` 命名空间来实现内存排序、原子数据结构。
|
||||
|
||||
> [!TIP]
|
||||
> 该部分内容更适合在单片机或者某些系统级组件开发上使用,常规使用可以使用 `std.Thread` 命名空间下的类型,包含常规的 `Mutex`,`Condition`,`ResetEvent`,`WaitGroup`等等。
|
||||
|
||||
## 内建函数
|
||||
|
||||
在讲述下列的内建函数前,我们需要了解一下前置知识:
|
||||
|
@ -109,16 +112,6 @@ outline: deep
|
|||
|
||||
强原子的比较与交换操作,如果目标指针是给定值,那么赋值为参数的新值,并返回 null,否则仅读取值返回。
|
||||
|
||||
### [`@fence`](https://ziglang.org/documentation/master/#fence)
|
||||
|
||||
函数原型:
|
||||
|
||||
```zig
|
||||
@fence(order: AtomicOrder) void
|
||||
```
|
||||
|
||||
用于创建一个内存屏障,防止某些类型的内存重新排序,具体细节可以查看内存屏障的相关信息。
|
||||
|
||||
## `std.atomic` 包
|
||||
|
||||
### 原子数据结构
|
|
@ -10,10 +10,10 @@ outline: deep
|
|||
|
||||
事实上,zig 本身的标准库为我们提供了多种内存分配模型:
|
||||
|
||||
1. [`GeneralPurposeAllocator`](https://ziglang.org/documentation/master/std/#std.heap.general_purpose_allocator.GeneralPurposeAllocator)
|
||||
2. [`FixedBufferAllocator`](https://ziglang.org/documentation/master/std/#std.heap.FixedBufferAllocator)
|
||||
3. [`ArenaAllocator`](https://ziglang.org/documentation/master/std/#std.heap.arena_allocator.ArenaAllocator)
|
||||
4. [`HeapAllocator`](https://ziglang.org/documentation/master/std/#std.heap.HeapAllocator)
|
||||
1. [`DebugAllocator`](https://ziglang.org/documentation/master/std/#std.heap.debug_allocator.DebugAllocator)
|
||||
2. [`SmpAllocator`](https://ziglang.org/documentation/master/std/#std.heap.SmpAllocator)
|
||||
3. [`FixedBufferAllocator`](https://ziglang.org/documentation/master/std/#std.heap.FixedBufferAllocator)
|
||||
4. [`ArenaAllocator`](https://ziglang.org/documentation/master/std/#std.heap.arena_allocator.ArenaAllocator)
|
||||
5. [`c_allocator`](https://ziglang.org/documentation/master/std/#std.heap.c_allocator)
|
||||
6. [`page_allocator`](https://ziglang.org/documentation/master/std/#std.heap.page_allocator)
|
||||
7. [`StackFallbackAllocator`](https://ziglang.org/documentation/master/std/#std.heap.StackFallbackAllocator)
|
||||
|
@ -28,10 +28,7 @@ outline: deep
|
|||
|
||||
- `std.testing.FailingAllocator`
|
||||
- `std.testing.allocator`
|
||||
- `std.heap.LoggingAllocator`
|
||||
- `std.heap.LogToWriterAllocator`
|
||||
- `std.heap.SbrkAllocator`
|
||||
- `std.heap.ScopedLoggingAllocator`
|
||||
|
||||
:::
|
||||
|
||||
|
@ -41,13 +38,31 @@ outline: deep
|
|||
|
||||
:::
|
||||
|
||||
## `GeneralPurposeAllocator`
|
||||
## `DebugAllocator`
|
||||
|
||||
这是一个通用的分配器,当你需要动态内存时,并且还不知道自己应该用什么分配器模型,用这个准没错!
|
||||
这是一个用于调试的分配器,现阶段适用于在调试模式下使用该分配器,它的性能并不高!
|
||||
|
||||
这个分配器的目的不是为了性能,而是为了安全,它支持线程安全,安全检查,检查是否存在泄露等特性,这些特性均可手动配置是否开启。
|
||||
|
||||
<<<@/code/release/memory_manager.zig#GeneralPurposeAllocator
|
||||
<<<@/code/release/memory_manager.zig#DebugAllocator
|
||||
|
||||
## `SmpAllocator`
|
||||
|
||||
专为 `ReleaseFast` 优化设计的分配器,启用多线程。
|
||||
|
||||
这个分配器是一个单例;它使用全局状态,并且整个过程只应实例化一个。
|
||||
|
||||
设计思路:
|
||||
|
||||
1. 每个线程都有独立的空闲列表(freelist),但是当线程退出时,这些数据必须是可回收的。由于我们无法直接得知线程何时退出,所以偶尔需要一个线程尝试回收其他线程的资源。
|
||||
|
||||
2. 超过特定大小的内存分配会直接通过内存映射(memory mapped)实现,且不存储分配元数据。这种机制之所以可行,是因为实现中禁止了将分配从小类别转移到大类别(反之亦然)的大小调整。
|
||||
|
||||
3. 每个分配器操作都会通过线程局部变量检查线程标识符,以确定要访问全局状态中的哪个元数据,并尝试获取其锁。通常情况下,这个操作会在没有竞争的情况下成功,除非另一个线程被分配了相同的 ID。如果发生这种竞争情况,线程会移动到下一个线程元数据槽位并重复尝试获取锁的过程。
|
||||
|
||||
4. 通过将线程局部元数据数组限制为与 CPU 数量相同的大小,确保了随着线程的创建和销毁,它们会循环使用整个空闲列表集合。
|
||||
|
||||
<<<@/code/release/memory_manager.zig#SmpAllocator
|
||||
|
||||
## `FixedBufferAllocator`
|
||||
|
||||
|
@ -67,14 +82,6 @@ outline: deep
|
|||
|
||||
<<<@/code/release/memory_manager.zig#ArenaAllocator
|
||||
|
||||
## `HeapAllocator`
|
||||
|
||||
这是一个依赖 windows 特性的分配器模型,故仅可在 windows 下可用。
|
||||
|
||||
关于这个模型的更多信息,可以参考这里[https://learn.microsoft.com/en-us/windows/win32/api/heapapi/](https://learn.microsoft.com/en-us/windows/win32/api/heapapi/)
|
||||
|
||||
<<<@/code/release/memory_manager.zig#HeapAllocator
|
||||
|
||||
## `c_allocator`
|
||||
|
||||
这是纯粹的 C 的 `malloc`,它会直接尝试调用 C 库的内存分配,使用它需要在 `build.zig` 中添加上 `linkLibC` 功能:
|
||||
|
@ -85,7 +92,7 @@ outline: deep
|
|||
|
||||
它还有一个变体:[`raw_c_allocator`](https://ziglang.org/documentation/master/std/#std.heap.raw_c_allocator)。
|
||||
|
||||
这两者的区别仅仅是 `c_allocator` 可能会调用 `alloc_aligned `而不是 `malloc` ,会优先使用 `malloc_usable_size` 来进行一些检查。
|
||||
这两者的区别仅仅是 `c_allocator` 可能会调用 `alloc_aligned`而不是 `malloc` ,会优先使用 `malloc_usable_size` 来进行一些检查。
|
||||
|
||||
而 `raw_c_allocator` 则是完全只使用 `malloc`。
|
||||
|
||||
|
@ -128,4 +135,4 @@ outline: deep
|
|||
|
||||
待添加,当前你可以通过实现 `Allocator` 接口来实现自己的分配器。为了做到这一点,必须仔细阅读 [`std/mem.zig`](https://github.com/ziglang/zig/blob/master/lib/std/mem.zig) 中的文档注释,然后提供 `allocFn` 和 `resizeFn`。
|
||||
|
||||
有许多分配器示例可供查看以获取灵感。查看 [`std/heap.zig`](https://github.com/ziglang/zig/blob/master/lib/std/heap.zig) 和 [`std.heap.GeneralPurposeAllocator`](https://github.com/ziglang/zig/blob/master/lib/std/heap/general_purpose_allocator.zig)
|
||||
有许多分配器示例可供查看以获取灵感。查看 [`std/heap.zig`](https://github.com/ziglang/zig/blob/master/lib/std/heap.zig) 和 [`std.heap.DebugAllocator`](https://github.com/ziglang/zig/blob/master/lib/std/heap/debug_allocator.zig)
|
||||
|
|
|
@ -24,6 +24,12 @@ outline: deep
|
|||
|
||||
关于[越界问题](https://ziglang.org/documentation/master/#Index-out-of-Bounds),zig 在编译期和运行时均有完整的越界保护和完善的堆栈错误跟踪。
|
||||
|
||||
### 解构数组
|
||||
|
||||
我们在变量声明的章节提到了,数组可以结构,再来回忆一下:
|
||||
|
||||
<<<@/code/release/array.zig#deconstruct
|
||||
|
||||
### 多维数组
|
||||
|
||||
多维数组(矩阵)实际上就是嵌套数组,我们很容易就可以创建一个多维数组出来:
|
||||
|
|
|
@ -44,7 +44,7 @@ outline: deep
|
|||
|
||||
## 枚举推断
|
||||
|
||||
枚举也支持让 zig 编译器自动进行推断,即在已经知道枚举的类型情况下仅使用字段来指定枚举的值:
|
||||
枚举也支持让 zig 编译器自动进行推断(使用结果位置语义),即在已经知道枚举的类型情况下仅使用字段来指定枚举的值:
|
||||
|
||||
<<<@/code/release/enum.zig#enum_reference
|
||||
|
||||
|
@ -56,7 +56,7 @@ zig 允许我们不列出所有的枚举值,未列出枚举值可以使用 `_`
|
|||
|
||||
:::info 🅿️ 提示
|
||||
|
||||
`@enumFromInt` 能够将整数转换为枚举值。但需要注意,如果所选枚举类型中没有表示该整数的值,就会导致[未定义行为](../../more/undefined_behavior#无效枚举转换)。
|
||||
`@enumFromInt` 能够将整数转换为枚举值。但需要注意,如果所选枚举类型中没有表示该整数的值,就会导致[未定义行为](../../advanced/undefined_behavior#无效枚举转换)。
|
||||
|
||||
如果目标枚举类型是非详尽枚举,那么除了涉及 `@intCast` 相关的安全检查之外,`@enumFromInt` 始终能够得到有效的枚举值。
|
||||
|
||||
|
@ -68,7 +68,7 @@ zig 允许我们不列出所有的枚举值,未列出枚举值可以使用 `_`
|
|||
|
||||
::: info 🅿️ 提示
|
||||
|
||||
此部分内容并非是初学者需要掌握的内容,它涉及到 zig 本身的类型系统和 [编译期反射](../../more/reflection#构建新的类型),可以暂且跳过!
|
||||
此部分内容并非是初学者需要掌握的内容,它涉及到 zig 本身的类型系统和 [编译期反射](../../advanced/reflection#构建新的类型),可以暂且跳过!
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
@ -12,6 +12,12 @@ outline: deep
|
|||
|
||||
## anyopaque
|
||||
|
||||
`anyopaque` 是一个比较特殊的类型,代表可以接受任何类型的 `opaque`(由于 `opaque` 拥有不同的变量/常量声明和方法的定义,故是不同的类型),常用于与 C 交互的函数中,相当于是 C 的 `void` 类型!
|
||||
`anyopaque` 是一个比较特殊的类型,代表可以接受任何类型的 `opaque`(由于 `opaque` 拥有不同的变量/常量声明和方法的定义,故是不同的类型),常用于与 C 交互的函数中,可以当作 C 的 `void` 类型来使用,一般用于类型擦除指针!
|
||||
|
||||
:::info 🅿️ 提示
|
||||
|
||||
需要注意,`void` 的已知大小为 0 字节,而 `anyopaque` 大小未知,但非零。
|
||||
|
||||
:::
|
||||
|
||||
TODO: 添加更多关于该类型使用的示例和说明!
|
||||
|
|
|
@ -10,7 +10,7 @@ outline: deep
|
|||
|
||||
**取地址**:通过 `&` 符号来获取某个变量所对应的内存地址,如 `&integer` 就是获取变量 `integer` 的内存地址。
|
||||
|
||||
与 C 不同,Zig 中的指针类型有多种,主要是对指向的元素做了区分,便于更好地使用。下图展示了它们指向元素的不同:
|
||||
与 C 不同,Zig 中的指针类型要分为两种(一种是单项指针,一种是多项指针),它们主要是对指向的元素做了区分,便于更好地使用。下图展示了它们指向元素的不同:
|
||||
|
||||

|
||||
|
||||
|
@ -36,23 +36,32 @@ zig 本身支持指针运算(加减操作),但有一点需要注意:最
|
|||
|
||||
<<<@/code/release/pointer.zig#single_pointer
|
||||
|
||||
单项指针本身支持以下操作:
|
||||
|
||||
- 解引用语法 `ptr.*`
|
||||
- 切片语法 `ptr[0..1]`
|
||||
- 指针减法 `ptr - ptr`
|
||||
|
||||
:::info 🅿️ 提示
|
||||
|
||||
函数指针略有特殊:`const Call2Op = *const fn (a: i8, b: i8) i8;`
|
||||
函数指针略有特殊:
|
||||
|
||||
<<<@/code/release/pointer.zig#fn_pointer
|
||||
|
||||
:::
|
||||
|
||||
## 多项指针
|
||||
|
||||
多项指针指向位置数量的多个元素。
|
||||
多项指针指向未知数量的多个元素。
|
||||
|
||||
多项指针的类型为:`[*]T`,`T`是所指向内存区域的类型,且该类型必须具有明确的大小(这意味着它不能是 [`anyopaque`](https://ziglang.org/documentation/master/#toc-C-Type-Primitives) 和其他任意[不透明类型](https://ziglang.org/documentation/master/#opaque))。
|
||||
|
||||
解引用方法支持以下几种:
|
||||
|
||||
- 索引语法 `ptr[i]`
|
||||
- 切片语法 `ptr[start..end]`
|
||||
- 指针运算 `ptr + x`,`ptr - x`
|
||||
- 切片语法 `ptr[start..end]` 和 `ptr[start..]`
|
||||
- 指针运算 `ptr + int`, `ptr - int`
|
||||
- 指针减法 `ptr - ptr`
|
||||
|
||||
<<<@/code/release/pointer.zig#multi_pointer
|
||||
|
||||
|
@ -62,9 +71,21 @@ zig 本身支持指针运算(加减操作),但有一点需要注意:最
|
|||
|
||||
`*[N]T`:这是指向一个数组的单项指针,数组的长度为 N。也可以将其理解为指向 N 个元素的指针。
|
||||
|
||||
支持这些语法:
|
||||
|
||||
- 索引语法:`array_ptr[i]`
|
||||
- 切片语法:`array_ptr[start..end]`
|
||||
- `len` 属性:`array_ptr.len`
|
||||
- 指针减法:`array_ptr - array_ptr`
|
||||
|
||||
`[]T`:这是切片,相当于一个胖指针,包含了一个类型为 `[*]T` 的指针和一个长度。
|
||||
|
||||
数组指针的类型中就包含了长度信息,而切片中则实际存储着长度。数组指针和切片的长度都可以通过 `len` 属性来获取。
|
||||
支持这些语法:
|
||||
|
||||
- 索引语法:`slice[i]`
|
||||
- 切片语法:`slice[start..end]`
|
||||
- `len` 属性:`slice.len`
|
||||
数组指针的类型中就包含了长度信息,而切片中则实际存储着长度。数组指针和切片的长度都可以通过 `len` 属性来获取。
|
||||
|
||||
:::details 示例
|
||||
|
||||
|
@ -122,6 +143,20 @@ zig 本身支持指针运算(加减操作),但有一点需要注意:最
|
|||
|
||||
其中 `[*] const u8` 可以看作是 C 中的 `* const char`,这是因为在 C 语言中一个普通的指针也可以指向一个数组,zig 仅仅是单独把这种令人迷惑的行为单独作为一个语法而已!
|
||||
|
||||
## 指针和整数互转
|
||||
|
||||
[`@ptrFromInt`](https://ziglang.org/documentation/master/#ptrFromInt) 可以将整数地址转换为指针,[`@intFromPtr`](https://ziglang.org/documentation/master/#intFromPtr) 可以将指针转换为整数:
|
||||
|
||||
<<<@/code/release/pointer.zig#ptr2int
|
||||
|
||||
## 指针强制转换
|
||||
|
||||
内置函数 [`@ptrCast`](https://ziglang.org/documentation/master/#ptrCast) 可以将将指针的元素类型转换为另一种类型,也就是不同类型的指针强制转换。
|
||||
|
||||
一般情况下,应当尽量避免使用 `@ptrCast`,这会创建一个新的指针,根据通过它的加载和存储操作,可能导致无法检测的非法行为。
|
||||
|
||||
<<<@/code/release/pointer.zig#ptr_cast
|
||||
|
||||
## 额外特性
|
||||
|
||||
以下的是指针的额外特性,初学者可以直接略过以下部分,等到你需要时再来学习即可!
|
||||
|
@ -142,7 +177,7 @@ zig 本身支持指针运算(加减操作),但有一点需要注意:最
|
|||
|
||||
> 如果你不知道内存对齐的含义是什么,那么本节内容你可以跳过了,等到你需要时再来查看!
|
||||
|
||||
每种类型都有一个对齐方式——数个字节,这样,当从内存加载或存储该类型的值时,内存地址必须能被该数字整除。我们可以使用 `@alignOf` 找出任何类型的内存对齐大小。
|
||||
每种类型都有一个对齐方式——也就是数个字节,这样,当从内存加载或存储该类型的值时,内存地址必须能被该数字整除。我们可以使用 `@alignOf` 找出任何类型的内存对齐大小。
|
||||
|
||||
内存对齐大小取决于 CPU 架构,但始终是 2 的幂,并且小于 1 << 29。
|
||||
:::info
|
||||
|
@ -168,6 +203,51 @@ zig 本身支持指针运算(加减操作),但有一点需要注意:最
|
|||
|
||||
:::
|
||||
|
||||
如果有一个指针或切片,它的对齐很小,但我们知道它实际上有一个更大的对齐,那么使用 [`@alignCast`](https://ziglang.org/documentation/master/#alignCast) 让其 `align` 更大。在运行时是无操作的,但会额外加入一个 [安全检查](https://ziglang.org/documentation/master/#Incorrect-Pointer-Alignment):
|
||||
|
||||
> 例如这段代码就是错误的,不会被正常执行
|
||||
|
||||
```zig
|
||||
const std = @import("std");
|
||||
|
||||
test "pointer alignment safety" {
|
||||
var array align(4) = [_]u32{ 0x11111111, 0x11111111 };
|
||||
const bytes = std.mem.sliceAsBytes(array[0..]);
|
||||
try std.testing.expect(foo(bytes) == 0x11111111);
|
||||
}
|
||||
fn foo(bytes: []u8) u32 {
|
||||
const slice4 = bytes[1..5];
|
||||
const int_slice = std.mem.bytesAsSlice(u32, @as([]align(4) u8, @alignCast(slice4)));
|
||||
return int_slice[0];
|
||||
}
|
||||
```
|
||||
|
||||
```sh
|
||||
$ zig test test_incorrect_pointer_alignment.zig
|
||||
1/1 test_incorrect_pointer_alignment.test.pointer alignment safety...thread 958173 panic: incorrect alignment
|
||||
/home/ci/actions-runner/_work/zig-bootstrap/zig/doc/langref/test_incorrect_pointer_alignment.zig:10:68: 0x1048962 in foo (test)
|
||||
const int_slice = std.mem.bytesAsSlice(u32, @as([]align(4) u8, @alignCast(slice4)));
|
||||
^
|
||||
/home/ci/actions-runner/_work/zig-bootstrap/zig/doc/langref/test_incorrect_pointer_alignment.zig:6:31: 0x104880f in test.pointer alignment safety (test)
|
||||
try std.testing.expect(foo(bytes) == 0x11111111);
|
||||
^
|
||||
/home/ci/actions-runner/_work/zig-bootstrap/out/host/lib/zig/compiler/test_runner.zig:214:25: 0x10efab9 in mainTerminal (test)
|
||||
if (test_fn.func()) |_| {
|
||||
^
|
||||
/home/ci/actions-runner/_work/zig-bootstrap/out/host/lib/zig/compiler/test_runner.zig:62:28: 0x10e7ead in main (test)
|
||||
return mainTerminal();
|
||||
^
|
||||
/home/ci/actions-runner/_work/zig-bootstrap/out/host/lib/zig/std/start.zig:647:22: 0x10e7430 in posixCallMainAndExit (test)
|
||||
root.main();
|
||||
^
|
||||
/home/ci/actions-runner/_work/zig-bootstrap/out/host/lib/zig/std/start.zig:271:5: 0x10e6ffd in _start (test)
|
||||
asm volatile (switch (native_arch) {
|
||||
^
|
||||
???:?:?: 0x0 in ??? (???)
|
||||
error: the following test command crashed:
|
||||
/home/ci/actions-runner/_work/zig-bootstrap/out/zig-local-cache/o/608e4a8451ecb0974638281c85927599/test --seed=0x9bc870fd
|
||||
```
|
||||
|
||||
### 零指针
|
||||
|
||||
零指针实际上是一个未定义的错误行为([Pointer Cast Invalid Null](https://ziglang.org/documentation/master/#Pointer-Cast-Invalid-Null)),但是当我们给指针增加上 `allowzero` 修饰符后,它就变成合法的行为了!
|
||||
|
@ -185,3 +265,7 @@ zig 本身支持指针运算(加减操作),但有一点需要注意:最
|
|||
只要代码不依赖于未定义的内存布局,那么指针也可以在编译期发挥作用!
|
||||
|
||||
<<<@/code/release/pointer.zig#comptime_pointer
|
||||
|
||||
只要指针从未被取消引用,Zig 就能够保留 `comptime` 代码中的内存地址:
|
||||
|
||||
<<<@/code/release/pointer.zig#comp_pointer
|
||||
|
|
|
@ -55,7 +55,7 @@ outline: deep
|
|||
|
||||
平常使用过程中会面临另外的一个情况,就是匿名结构体要如何实现自引用呢?
|
||||
|
||||
答案是使用 [`@This`](https://ziglang.org/documentation/master/#This),这是 zig 专门为匿名结构体和文件类的类型声明(此处可以看 [命名空间](../../more/miscellaneous.md#容器))提供的处理方案。
|
||||
答案是使用 [`@This`](https://ziglang.org/documentation/master/#This),这是 zig 专门为匿名结构体和文件类的类型声明(此处可以看 [命名空间](../../basic/define-variable.md#容器))提供的处理方案。
|
||||
|
||||
此函数会返回一个当前包裹它的容器的类型!
|
||||
|
||||
|
@ -85,10 +85,12 @@ outline: deep
|
|||
|
||||
## 自动推断
|
||||
|
||||
zig 在使用结构体的时候还支持省略结构体类型,只要能让 zig 编译器推断出类型即可,例如:
|
||||
zig 在使用结构体的时候还支持省略结构体类型,此时 zig 将会使用结果位置语义来对类型进行推导,例如:
|
||||
|
||||
<<<@/code/release/struct.zig#auto_reference
|
||||
|
||||
_这种基于结果位置语义的推导还支持结构体本身的方法和其内部中定义的其他类型(此处指的并不是结构体字段)!_
|
||||
|
||||
## 泛型实现
|
||||
|
||||
依托于“类型是 zig 的一等公民”,我们可以很容易的实现泛型。
|
||||
|
@ -111,6 +113,12 @@ zig 在使用结构体的时候还支持省略结构体类型,只要能让 zig
|
|||
|
||||
<<<@/code/release/struct.zig#default_field
|
||||
|
||||
但仅仅这样还不够,因为在使用时很容易遇到只初始化部分字段,而其他字段使用默认值的情况,此时如果我们对结构体的字段值并无默认不变性的要求,那么这种默认值方案已经足够我们使用。
|
||||
|
||||
但如果要求结构体的字段值具有默认不变性(要么都是默认值,要么全部由使用者手动赋值),那么可以采用以下方案:
|
||||
|
||||
<<<@/code/release/struct.zig#all_default
|
||||
|
||||
## 空结构体
|
||||
|
||||
你还可以使用空结构体,具体如下:
|
||||
|
@ -136,7 +144,7 @@ zig 在使用结构体的时候还支持省略结构体类型,只要能让 zig
|
|||
|
||||
<<<@/code/release/struct.zig#base_ptr
|
||||
|
||||
这里使用了内建函数 [`@fieldParentPtr`](https://ziglang.org/documentation/master/#toc-fieldParentPtr) ,它会根据给定字段指针,返回对应的结构体基指针。
|
||||
这里使用了内建函数 [`@fieldParentPtr`](https://ziglang.org/documentation/master/#fieldParentPtr) ,它会根据给定字段指针,返回对应的结构体基指针。
|
||||
|
||||
## 元组
|
||||
|
||||
|
@ -154,6 +162,14 @@ zig 在使用结构体的时候还支持省略结构体类型,只要能让 zig
|
|||
|
||||
:::
|
||||
|
||||
:::info 🅿️ 提示
|
||||
|
||||
元组同时还支持解构语法!!
|
||||
|
||||
<<<@/code/release/struct.zig#destruct_tuple
|
||||
|
||||
:::
|
||||
|
||||
## 高级特性
|
||||
|
||||
以下特性如果你连名字都没有听说过,那就代表你目前无需了解以下部分,待需要时再来学习即可!
|
||||
|
|
|
@ -6,6 +6,8 @@ outline: deep
|
|||
|
||||
> 向量(Vector)为我们提供了并行操纵一组同类型(布尔、整型、浮点、指针)的值的方法,它尽可能使用 `SIMD` 指令。
|
||||
|
||||
向量类型使用内置函数 [@Vector](https://ziglang.org/documentation/master/#Vector) 创建
|
||||
|
||||
## 基本使用
|
||||
|
||||
向量支持与底层基本类型相同的内置运算符。这些操作是按元素执行,并返回与输入向量长度相同的向量,包括:
|
||||
|
@ -30,6 +32,12 @@ Zig 支持任何已知的最大 2^32-1 向量长度。请注意,过长的向
|
|||
|
||||
:::
|
||||
|
||||
## 解构向量
|
||||
|
||||
和数组一样,向量也可以被解构:
|
||||
|
||||
<<<@/code/release/vector.zig#deconstruct
|
||||
|
||||
## `@splat`
|
||||
|
||||
`@splat(scalar: anytype) anytype`
|
||||
|
|
|
@ -120,11 +120,7 @@ Zig 语言不允许在函数内声明函数,也不允许直接创建匿名函
|
|||
|
||||
<<<@/code/release/function.zig#closure
|
||||
|
||||
函数 `bar` 返回一个函数类型`fn (i32) i32`,接收一个编译期的参数,这使得该函数可以访问编译期传入的数据。
|
||||
|
||||
与此同时我们使用了匿名函数 (匿名结构体方法):
|
||||
|
||||
<<<@/code/release/function.zig#lambda
|
||||
函数 `bar` 返回一个函数类型`fn (i32) i32`,接收一个编译期的参数,这使得该函数可以访问编译期传入的数据,并且我们需要注意,我们还是使用了匿名结构体方法
|
||||
|
||||
关于闭包的更多内容可以参考以下文章或者帖子:
|
||||
|
||||
|
|
|
@ -63,6 +63,18 @@ zig 使用 `const` 作为关键字来声明常量,它无法再被更改,只
|
|||
在 `Debug` 模式下,Zig 将 `0xaa` 字节写入未定义的内存。这是为了尽早发现错误,并帮助检测调试器中未定义内存的使用。但是,此行为只是一种实现功能,而不是语言语义,因此不能保证代码可以观察到它。
|
||||
:::
|
||||
|
||||
## 解构赋值
|
||||
|
||||
解构赋值(Destructuring Assignment)是于 `0.12` 新引入的语法,允许对可索引的聚合结构(如元组、向量和数组)进行解构。
|
||||
|
||||
<<<@/code/release/define_variable.zig#deconstruct
|
||||
|
||||
解构表达式只能出现在块内(不在容器范围内),赋值的左侧必须由逗号分隔的列表组成,其中每个元素可以是左值(例如`var`)或变量声明:
|
||||
|
||||
<<<@/code/release/define_variable.zig#deconstruct_2
|
||||
|
||||
解构可以以 `comptime` 关键字作为前缀,在这种情况下,整个解构表达式在 `comptime` 处求值。所有声明的 `var` 都将是 `comptime var`,并且所有表达式(左值和右值)都在 `comptime` 处求值。
|
||||
|
||||
## 块
|
||||
|
||||
块(block)用于限制变量声明的范围,例如以下代码是非法的:
|
||||
|
@ -81,6 +93,30 @@ x += 1;
|
|||
|
||||
上方的 `blk` 是标签名字,它可以是你设置的任何名字。
|
||||
|
||||
### shadow
|
||||
|
||||
Shadow(遮蔽)指的是在内部作用域中声明一个与外部作用域中同名的变量,导致外部作用域的变量被"遮蔽"的现象。
|
||||
|
||||
但是该行为在 Zig 中禁止的!
|
||||
这样做的好处是会强制标识符始终具有在其周期内的一致性,并且可以防止意外使用错误的变量。
|
||||
|
||||
注意:如果两个块中的变量如果不交叉,那么它们是可以同名的。
|
||||
|
||||
### 空的块
|
||||
|
||||
空的块等效于 `void{}`,即一个空的函数体。
|
||||
|
||||
## 容器
|
||||
|
||||
在 Zig 中,**容器** 是充当保存变量和函数声明的命名空间的任何语法结构。容器也是可以实例化的类型定义。结构体、枚举、联合、不透明,甚至 Zig 源文件本身都是容器,但容器并不能包含语句(语句是描述程序运行操作的一个单位)。
|
||||
|
||||
当然,你也可以这样理解,容器是一个只包含变量或常量定义以及函数定义的命名空间。
|
||||
|
||||
注意:容器和块(block)不同!
|
||||
|
||||
> [!IMPORTANT]
|
||||
> 初次阅读此处困惑是正常的,后面的概念学习完成后此处自通。
|
||||
|
||||
## 注释
|
||||
|
||||
先来看一下在 zig 中如何正确的书写注释,zig 本身支持三种注释方式,分别是普通注释、文档注释、顶层文档注释。
|
||||
|
@ -105,8 +141,51 @@ PS:说实话,我认为这个设计并不太好。
|
|||
为什么是作用域顶层呢?实际上,zig 将一个源码文件看作是一个容器。
|
||||
:::
|
||||
|
||||
## 解构赋值
|
||||
## `usingnamespace`
|
||||
|
||||
解构赋值(Destructuring Assignment)是于 `0.12` 新引入的语法,允许对可索引的聚合结构(如元组、向量和数组)进行解构。
|
||||
关键字 `usingnamespace` 可以将一个容器中的所有 `pub` 声明混入到当前的容器中。
|
||||
|
||||
<<<@/code/release/define_variable.zig#deconstruct
|
||||
例如,可以使用将 `usingnamespace` 将 std 标准库混入到 `main.zig` 这个容器中:
|
||||
|
||||
```zig
|
||||
const T = struct {
|
||||
usingnamespace @import("std");
|
||||
};
|
||||
pub fn main() !void {
|
||||
T.debug.print("Hello, World!\n", .{});
|
||||
}
|
||||
```
|
||||
|
||||
注意:无法在结构体 `T` 内部直接使用混入的声明,需要使用 `T.debug` 这种方式才可以!
|
||||
|
||||
`usingnamespace` 还可以使用 `pub` 关键字进行修饰,用于转发声明,这常用于组织 API 文件和 C import。
|
||||
|
||||
```zig
|
||||
pub usingnamespace @cImport({
|
||||
@cInclude("epoxy/gl.h");
|
||||
@cInclude("GLFW/glfw3.h");
|
||||
@cDefine("STBI_ONLY_PNG", "");
|
||||
@cDefine("STBI_NO_STDIO", "");
|
||||
@cInclude("stb_image.h");
|
||||
});
|
||||
```
|
||||
|
||||
相关的使用方法可以是这样的:
|
||||
|
||||
```zig
|
||||
pub usingnamespace @cImport({
|
||||
@cInclude("xcb/xcb.h");
|
||||
@cInclude("xcb/xproto.h");
|
||||
});
|
||||
```
|
||||
|
||||
针对以上的引入的头文件,我们可以这样使用 `@This().xcb_generic_event_t`
|
||||
|
||||
> [!IMPORTANT]
|
||||
> 初次阅读此处困惑是正常的,后面的概念学习完成后此处自通。
|
||||
|
||||
## `threadlocal`
|
||||
|
||||
变量可以使用 `threadlocal` 修饰,来使得该变量在不同线程中是不同的示例:
|
||||
|
||||
<<<@/code/release/define_variable.zig#threadlocal
|
||||
|
|
|
@ -65,3 +65,25 @@ switch 的分支可以标记为 `inline` 来要求编译器生成该分支对应
|
|||
当使用 `inline else` 捕获 tag union 时,可以额外捕获 tag 和对应的 value:
|
||||
|
||||
<<<@/code/release/switch.zig#catch_tag_union_value
|
||||
|
||||
### labeled switch
|
||||
|
||||
这是 `0.14.0` 引入的新特性,当 `switch` 语句带有标签时,它可以被 `break` 或 `continue` 语句引用。`break` 将从 `switch` 语句返回一个值。
|
||||
|
||||
针对 `switch` 的 `continue` 语句必须带有一个操作数。当执行时,它会跳转到匹配的分支,就像用 `continue` 的操作数替换了初始 `switch` 值后重新执行 `switch` 一样。
|
||||
|
||||
例如以下两段代码的写法是一样的:
|
||||
|
||||
<<<@/code/release/switch.zig#labeled_switch_1
|
||||
|
||||
<<<@/code/release/switch.zig#labeled_switch_2
|
||||
|
||||
这可以提高(例如)状态机的清晰度,其中 `continue :sw .next_state` 这样的语法是明确的、清楚的,并且可以立即理解。
|
||||
|
||||
不过,这个设计的目的是处理对数组中每个元素进行 `switch` 判断的情况,在这种情况下,使用单个 `switch` 语句可以提高代码的清晰度和性能:
|
||||
|
||||
<<<@/code/release/switch.zig#vm
|
||||
|
||||
如果 `continue` 的操作数在编译时就已知,那么它可以被简化为一个无条件分支指令,直接跳转到相关的 `case`。这种分支是完全可预测的,因此通常执行速度很快。
|
||||
|
||||
如果操作数在运行时才能确定,每个 `continue` 可以内联嵌入一个条件分支(理想情况下通过跳转表实现),这使得 CPU 可以独立于其他分支来预测其目标。相比之下,基于循环的简化实现会迫使所有分支都通过同一个分发点,这会妨碍分支预测。
|
||||
|
|
|
@ -146,3 +146,26 @@ const Deconstruct = struct {
|
|||
_ = x;
|
||||
}
|
||||
};
|
||||
|
||||
const ThreadLocal = struct {
|
||||
// #region threadlocal
|
||||
const std = @import("std");
|
||||
threadlocal var x: i32 = 1234;
|
||||
|
||||
fn main() !void {
|
||||
const thread1 = try std.Thread.spawn(.{}, testTls, .{});
|
||||
const thread2 = try std.Thread.spawn(.{}, testTls, .{});
|
||||
testTls();
|
||||
thread1.join();
|
||||
thread2.join();
|
||||
}
|
||||
|
||||
fn testTls() void {
|
||||
// 1234
|
||||
std.debug.print("x is {}\n", .{x});
|
||||
x += 1;
|
||||
// 1235
|
||||
std.debug.print("x is {}\n", .{x});
|
||||
}
|
||||
// #endregion threadlocal
|
||||
};
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
./12
|
|
@ -21,6 +21,28 @@ const CreateArray = struct {
|
|||
// #endregion create_array
|
||||
};
|
||||
|
||||
const Deconstruct = struct {
|
||||
// #region deconstruct
|
||||
const print = @import("std").debug.print;
|
||||
|
||||
fn swizzleRgbaToBgra(rgba: [4]u8) [4]u8 {
|
||||
// 解构
|
||||
const r, const g, const b, const a = rgba;
|
||||
return .{ b, g, r, a };
|
||||
}
|
||||
|
||||
pub fn main() void {
|
||||
const pos = [_]i32{ 1, 2 };
|
||||
// 解构
|
||||
const x, const y = pos;
|
||||
print("x = {}, y = {}\n", .{ x, y });
|
||||
|
||||
const orange: [4]u8 = .{ 255, 165, 0, 255 };
|
||||
print("{any}\n", .{swizzleRgbaToBgra(orange)});
|
||||
}
|
||||
// #endregion deconstruct
|
||||
};
|
||||
|
||||
const Matrix = struct {
|
||||
// #region matrix
|
||||
const print = @import("std").debug.print;
|
||||
|
|
|
@ -6,11 +6,12 @@
|
|||
//
|
||||
// It is redundant to include "zig" in this name because it is already
|
||||
// within the Zig package namespace.
|
||||
.name = "basic",
|
||||
.name = .basic,
|
||||
|
||||
// This is a [Semantic Version](https://semver.org/).
|
||||
// In a future version of Zig it will be used for package deduplication.
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0x907975534fe79435,
|
||||
|
||||
// This field is optional.
|
||||
// This is currently advisory only; Zig does not yet do anything
|
||||
|
|
|
@ -6,11 +6,12 @@
|
|||
//
|
||||
// It is redundant to include "zig" in this name because it is already
|
||||
// within the Zig package namespace.
|
||||
.name = "cli",
|
||||
.name = .cli,
|
||||
|
||||
// This is a [Semantic Version](https://semver.org/).
|
||||
// In a future version of Zig it will be used for package deduplication.
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0x48f6513c15de5e44,
|
||||
|
||||
// This field is optional.
|
||||
// This is currently advisory only; Zig does not yet do anything
|
||||
|
|
|
@ -6,11 +6,12 @@
|
|||
//
|
||||
// It is redundant to include "zig" in this name because it is already
|
||||
// within the Zig package namespace.
|
||||
.name = "docs",
|
||||
.name = .docs,
|
||||
|
||||
// This is a [Semantic Version](https://semver.org/).
|
||||
// In a future version of Zig it will be used for package deduplication.
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0x51572bb73db54779,
|
||||
|
||||
// This field is optional.
|
||||
// This is currently advisory only; Zig does not yet do anything
|
||||
|
|
|
@ -6,11 +6,12 @@
|
|||
//
|
||||
// It is redundant to include "zig" in this name because it is already
|
||||
// within the Zig package namespace.
|
||||
.name = "embedfile",
|
||||
.name = .embedfile,
|
||||
|
||||
// This is a [Semantic Version](https://semver.org/).
|
||||
// In a future version of Zig it will be used for package deduplication.
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0x3e6a9e8b250bb664,
|
||||
|
||||
// This field is optional.
|
||||
// This is currently advisory only; Zig does not yet do anything
|
||||
|
|
|
@ -6,11 +6,12 @@
|
|||
//
|
||||
// It is redundant to include "zig" in this name because it is already
|
||||
// within the Zig package namespace.
|
||||
.name = "externalfile",
|
||||
.name = .externalfile,
|
||||
|
||||
// This is a [Semantic Version](https://semver.org/).
|
||||
// In a future version of Zig it will be used for package deduplication.
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0x61de81227ca0d23c,
|
||||
|
||||
// This field is optional.
|
||||
// This is currently advisory only; Zig does not yet do anything
|
||||
|
|
|
@ -6,11 +6,12 @@
|
|||
//
|
||||
// It is redundant to include "zig" in this name because it is already
|
||||
// within the Zig package namespace.
|
||||
.name = "library",
|
||||
.name = .library,
|
||||
|
||||
// This is a [Semantic Version](https://semver.org/).
|
||||
// In a future version of Zig it will be used for package deduplication.
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0xa18098bc77aad8e8,
|
||||
|
||||
// This field is optional.
|
||||
// This is currently advisory only; Zig does not yet do anything
|
||||
|
|
|
@ -6,11 +6,12 @@
|
|||
//
|
||||
// It is redundant to include "zig" in this name because it is already
|
||||
// within the Zig package namespace.
|
||||
.name = "options",
|
||||
.name = .options,
|
||||
|
||||
// This is a [Semantic Version](https://semver.org/).
|
||||
// In a future version of Zig it will be used for package deduplication.
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0xd035fa8769f41b1a,
|
||||
|
||||
// This field is optional.
|
||||
// This is currently advisory only; Zig does not yet do anything
|
||||
|
|
|
@ -6,11 +6,12 @@
|
|||
//
|
||||
// It is redundant to include "zig" in this name because it is already
|
||||
// within the Zig package namespace.
|
||||
.name = "step",
|
||||
.name = .step,
|
||||
|
||||
// This is a [Semantic Version](https://semver.org/).
|
||||
// In a future version of Zig it will be used for package deduplication.
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0x43b9fe3c8067cab6,
|
||||
|
||||
// This field is optional.
|
||||
// This is currently advisory only; Zig does not yet do anything
|
||||
|
|
|
@ -6,11 +6,12 @@
|
|||
//
|
||||
// It is redundant to include "zig" in this name because it is already
|
||||
// within the Zig package namespace.
|
||||
.name = "system_lib",
|
||||
.name = .system_lib,
|
||||
|
||||
// This is a [Semantic Version](https://semver.org/).
|
||||
// In a future version of Zig it will be used for package deduplication.
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0x64b791172db5c549,
|
||||
|
||||
// This field is optional.
|
||||
// This is currently advisory only; Zig does not yet do anything
|
||||
|
|
|
@ -6,11 +6,12 @@
|
|||
//
|
||||
// It is redundant to include "zig" in this name because it is already
|
||||
// within the Zig package namespace.
|
||||
.name = "test",
|
||||
.name = .ttest,
|
||||
|
||||
// This is a [Semantic Version](https://semver.org/).
|
||||
// In a future version of Zig it will be used for package deduplication.
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0x334b1002be4b456c,
|
||||
|
||||
// This field is optional.
|
||||
// This is currently advisory only; Zig does not yet do anything
|
||||
|
|
|
@ -6,11 +6,12 @@
|
|||
//
|
||||
// It is redundant to include "zig" in this name because it is already
|
||||
// within the Zig package namespace.
|
||||
.name = "tinytetris",
|
||||
.name = .tinytetris,
|
||||
|
||||
// This is a [Semantic Version](https://semver.org/).
|
||||
// In a future version of Zig it will be used for package deduplication.
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0x5f03916d4d995c27,
|
||||
|
||||
// This field is optional.
|
||||
// This is currently advisory only; Zig does not yet do anything
|
||||
|
|
|
@ -137,12 +137,75 @@ const Block = struct {
|
|||
const Deconstruct = struct {
|
||||
fn main() void {
|
||||
// #region deconstruct
|
||||
const print = @import("std").debug.print;
|
||||
var x: u32 = undefined;
|
||||
var y: u32 = undefined;
|
||||
var z: u32 = undefined;
|
||||
// var z: u32 = undefined;
|
||||
const x, var y, z = [3]u32{ 1, 2, 3 };
|
||||
y += 10;
|
||||
// x 是 1,y 是 2,z 是 3
|
||||
// 元组
|
||||
const tuple = .{ 1, 2, 3 };
|
||||
// 解构元组
|
||||
x, y, z = tuple;
|
||||
|
||||
print("tuple: x = {}, y = {}, z = {}\n", .{ x, y, z });
|
||||
// 数组
|
||||
const array = [_]u32{ 4, 5, 6 };
|
||||
// 解构数组
|
||||
x, y, z = array;
|
||||
|
||||
print("array: x = {}, y = {}, z = {}\n", .{ x, y, z });
|
||||
// 向量定义
|
||||
const vector: @Vector(3, u32) = .{ 7, 8, 9 };
|
||||
// 解构向量
|
||||
x, y, z = vector;
|
||||
|
||||
print("vector: x = {}, y = {}, z = {}\n", .{ x, y, z });
|
||||
// #endregion deconstruct
|
||||
_ = x;
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
const Deconstruct_2 = struct {
|
||||
pub fn main() !void {
|
||||
// #region deconstruct_2
|
||||
const print = @import("std").debug.print;
|
||||
var x: u32 = undefined;
|
||||
|
||||
const tuple = .{ 1, 2, 3 };
|
||||
|
||||
x, var y: u32, const z = tuple;
|
||||
|
||||
print("x = {}, y = {}, z = {}\n", .{ x, y, z });
|
||||
|
||||
// y 可变
|
||||
y = 100;
|
||||
|
||||
// 可以用 _ 丢弃不想要的值
|
||||
_, x, _ = tuple;
|
||||
|
||||
print("x = {}", .{x});
|
||||
// #endregion deconstruct_2
|
||||
}
|
||||
};
|
||||
|
||||
const ThreadLocal = struct {
|
||||
// #region threadlocal
|
||||
const std = @import("std");
|
||||
threadlocal var x: i32 = 1234;
|
||||
|
||||
fn main() !void {
|
||||
const thread1 = try std.Thread.spawn(.{}, testTls, .{});
|
||||
const thread2 = try std.Thread.spawn(.{}, testTls, .{});
|
||||
testTls();
|
||||
thread1.join();
|
||||
thread2.join();
|
||||
}
|
||||
|
||||
fn testTls() void {
|
||||
// 1234
|
||||
std.debug.print("x is {}\n", .{x});
|
||||
x += 1;
|
||||
// 1235
|
||||
std.debug.print("x is {}\n", .{x});
|
||||
}
|
||||
// #endregion threadlocal
|
||||
};
|
||||
|
|
|
@ -61,3 +61,20 @@ inline fn shiftLeftOne(a: u32) u32 {
|
|||
return a << 1;
|
||||
}
|
||||
// #endregion shiftLeftOne
|
||||
|
||||
// #region closure
|
||||
fn bar(comptime x: i32) fn (i32) i32 {
|
||||
return struct {
|
||||
pub fn foo(y: i32) i32 {
|
||||
var counter = 0;
|
||||
for (x..y) |i| {
|
||||
if ((i % 2) == 0) {
|
||||
counter += i * i;
|
||||
}
|
||||
}
|
||||
return counter;
|
||||
}
|
||||
}.foo;
|
||||
}
|
||||
|
||||
// #endregion closure
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
.{
|
||||
.name = "pkg1",
|
||||
.name = .pkg1,
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0x759ba61b105d16f9,
|
||||
.dependencies = .{
|
||||
.pkg2 = .{
|
||||
// path 为本地包的路径
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
.{
|
||||
.name = "pkg2",
|
||||
.name = .pkg2,
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0xec92f7a1a7362798,
|
||||
.dependencies = .{},
|
||||
.paths = .{
|
||||
"build.zig",
|
||||
|
|
|
@ -6,11 +6,12 @@
|
|||
//
|
||||
// It is redundant to include "zig" in this name because it is already
|
||||
// within the Zig package namespace.
|
||||
.name = "import_vcpkg",
|
||||
.name = .import_vcpkg,
|
||||
|
||||
// This is a [Semantic Version](https://semver.org/).
|
||||
// In a future version of Zig it will be used for package deduplication.
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0x75583b9623cebdcb,
|
||||
|
||||
// This field is optional.
|
||||
// This is currently advisory only; Zig does not yet do anything
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
const builtin = @import("builtin");
|
||||
pub fn main() !void {
|
||||
try GPA.main();
|
||||
try DebugAllocator.main();
|
||||
try SmpAllocator.main();
|
||||
try BestAllocator.main();
|
||||
try FixedBufferAllocator.main();
|
||||
try ThreadSafeFixedBufferAllocator.main();
|
||||
try ArenaAllocator.main();
|
||||
|
@ -10,17 +11,17 @@ pub fn main() !void {
|
|||
try MemoryPool.main();
|
||||
}
|
||||
|
||||
const GPA = struct {
|
||||
// #region GeneralPurposeAllocator
|
||||
const DebugAllocator = struct {
|
||||
// #region DebugAllocator
|
||||
const std = @import("std");
|
||||
|
||||
pub fn main() !void {
|
||||
// 使用模型,一定要是变量,不能是常量
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
var gpa = std.heap.DebugAllocator(std.heap.DebugAllocatorConfig{}){};
|
||||
// 拿到一个allocator
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
// defer 用于执行general_purpose_allocator善后工作
|
||||
// defer 用于执行debug_allocator善后工作
|
||||
defer {
|
||||
// 尝试进行 deinit 操作
|
||||
const deinit_status = gpa.deinit();
|
||||
|
@ -34,7 +35,23 @@ const GPA = struct {
|
|||
// 延后释放内存
|
||||
defer allocator.free(bytes);
|
||||
}
|
||||
// #endregion GeneralPurposeAllocator
|
||||
// #endregion DebugAllocator
|
||||
};
|
||||
|
||||
const SmpAllocator = struct {
|
||||
// #region SmpAllocator
|
||||
const std = @import("std");
|
||||
|
||||
pub fn main() !void {
|
||||
// 无需任何初始化,拿来就可以使用
|
||||
const allocator = std.heap.smp_allocator;
|
||||
|
||||
//申请内存
|
||||
const bytes = try allocator.alloc(u8, 100);
|
||||
// 延后释放内存
|
||||
defer allocator.free(bytes);
|
||||
}
|
||||
// #endregion SmpAllocator
|
||||
};
|
||||
|
||||
const FixedBufferAllocator = struct {
|
||||
|
@ -83,6 +100,29 @@ const ThreadSafeFixedBufferAllocator = struct {
|
|||
// #endregion ThreadSafeFixedBufferAllocator
|
||||
};
|
||||
|
||||
const BestAllocator = struct {
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
|
||||
|
||||
pub fn main() !void {
|
||||
const allocator, const is_debug = allocator: {
|
||||
if (builtin.os.tag == .wasi) break :allocator .{ std.heap.wasm_allocator, false };
|
||||
break :allocator switch (builtin.mode) {
|
||||
.Debug, .ReleaseSafe => .{ debug_allocator.allocator(), true },
|
||||
.ReleaseFast, .ReleaseSmall => .{ std.heap.smp_allocator, false },
|
||||
};
|
||||
};
|
||||
defer if (is_debug) {
|
||||
_ = debug_allocator.deinit();
|
||||
};
|
||||
//申请内存
|
||||
const bytes = try allocator.alloc(u8, 100);
|
||||
// 延后释放内存
|
||||
defer allocator.free(bytes);
|
||||
}
|
||||
};
|
||||
|
||||
const ArenaAllocator = struct {
|
||||
// #region ArenaAllocator
|
||||
const std = @import("std");
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
// #region package_management
|
||||
.{
|
||||
// 包名字
|
||||
.name = "exporter",
|
||||
.name = .exporter,
|
||||
// 包版本
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0x6a125ce7eaa53f2,
|
||||
// 包所包含的源文件,一般用于在对外提供包时才使用,还是建议养成写清楚paths的习惯
|
||||
.paths = .{
|
||||
"build.zig",
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
// #region package_management
|
||||
.{
|
||||
// 包名字
|
||||
.name = "importer",
|
||||
.name = .importer,
|
||||
// 包版本
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0x64e883e88dde22e2,
|
||||
// 包依赖
|
||||
.dependencies = .{
|
||||
// 包依赖项的名字
|
||||
.@"tarball-exporter" = .{
|
||||
// url 为包的下载地址
|
||||
.url = "https://github.com/zigcc/zig-msgpack/archive/refs/tags/0.0.5.tar.gz",
|
||||
// hash 为包的哈希值,但不是对整个包的哈希值,而是对包中的所有文件的哈希值
|
||||
.hash = "12207ec1296852c17c0424b5e650003bd9a3c4f35e9d0ab586d39ea0ab812c2d9f2c",
|
||||
.url = "https://github.com/zigcc/zig-msgpack/archive/refs/tags/0.0.7.tar.gz",
|
||||
.hash = "zig_msgpack-0.0.7-evvueE3MAADy-2EAgCGUYIf1tHC9-z4n2sDIldvTZcY8",
|
||||
},
|
||||
.@"path-exporter" = .{
|
||||
// path 为本地包的路径
|
||||
|
|
|
@ -9,6 +9,8 @@ pub fn main() !void {
|
|||
try AlignCast.main();
|
||||
try ZeroPointer.main();
|
||||
ComptimePointer.main();
|
||||
ptr2int.main();
|
||||
try compPointer.main();
|
||||
}
|
||||
|
||||
const SinglePointer = struct {
|
||||
|
@ -24,6 +26,34 @@ const SinglePointer = struct {
|
|||
}
|
||||
// #endregion single_pointer
|
||||
};
|
||||
|
||||
const fnPointer = struct {
|
||||
// #region fn_pointer
|
||||
const Call2Op = *const fn (a: i8, b: i8) i8;
|
||||
// Call20p 是一个函数指针类型,指向一个接受两个 i8 类型参数并返回 i8 类型的函数
|
||||
// #endregion fn_pointer
|
||||
};
|
||||
|
||||
const ptr2int = struct {
|
||||
pub fn main() void {
|
||||
// #region ptr2int
|
||||
const std = @import("std");
|
||||
|
||||
// ptrFromInt 将整数转换为指针
|
||||
const ptr: *i32 = @ptrFromInt(0xdeadbee0);
|
||||
// intFromPtr 将指针转换为整数
|
||||
const addr = @intFromPtr(ptr);
|
||||
|
||||
if (@TypeOf(addr) == usize) {
|
||||
std.debug.print("success\n", .{});
|
||||
}
|
||||
if (addr == 0xdeadbee0) {
|
||||
std.debug.print("success\n", .{});
|
||||
}
|
||||
// #endregion ptr2int
|
||||
}
|
||||
};
|
||||
|
||||
const MultiPointer = struct {
|
||||
// #region multi_pointer
|
||||
const print = @import("std").debug.print;
|
||||
|
@ -192,3 +222,45 @@ const ComptimePointer = struct {
|
|||
}
|
||||
// #endregion comptime_pointer
|
||||
};
|
||||
|
||||
const compPointer = struct {
|
||||
pub fn main() !void {
|
||||
// #region comp_pointer
|
||||
comptime {
|
||||
const expect = @import("std").testing.expect;
|
||||
// 只要指针不被解引用,那么就可以这么做
|
||||
const ptr: *i32 = @ptrFromInt(0xdeadbee0);
|
||||
const addr = @intFromPtr(ptr);
|
||||
try expect(@TypeOf(addr) == usize);
|
||||
try expect(addr == 0xdeadbee0);
|
||||
}
|
||||
// #endregion comp_pointer
|
||||
}
|
||||
};
|
||||
|
||||
const ptrCast = struct {
|
||||
const std = @import("std");
|
||||
pub fn main() !void {
|
||||
// #region ptr_cast
|
||||
const bytes align(@alignOf(u32)) = [_]u8{ 0x12, 0x12, 0x12, 0x12 };
|
||||
// 将 u8数组指针 转换为 u32 类型的指针
|
||||
const u32_ptr: *const u32 = @ptrCast(&bytes);
|
||||
|
||||
if (u32_ptr.* == 0x12121212) {
|
||||
std.debug.print("success\n", .{});
|
||||
}
|
||||
|
||||
// 通过标准库转为 u32
|
||||
const u32_value = std.mem.bytesAsSlice(u32, bytes[0..])[0];
|
||||
|
||||
if (u32_value == 0x12121212) {
|
||||
std.debug.print("success\n", .{});
|
||||
}
|
||||
|
||||
// 通过内置函数转换
|
||||
if (@as(u32, @bitCast(bytes)) == 0x12121212) {
|
||||
std.debug.print("success\n", .{});
|
||||
}
|
||||
// #endregion ptr_cast
|
||||
}
|
||||
};
|
||||
|
|
|
@ -10,8 +10,32 @@ pub fn main() !void {
|
|||
NamePrinciple.main();
|
||||
try PackedBitOffset.main();
|
||||
try PackedCast.main();
|
||||
DestructTuple.main();
|
||||
}
|
||||
|
||||
const StructAllDefault = struct {
|
||||
// #region all_default
|
||||
const Threshold = struct {
|
||||
minimum: f32,
|
||||
maximum: f32,
|
||||
|
||||
// 选择声明一个默认值
|
||||
const default: Threshold = .{
|
||||
.minimum = 0.25,
|
||||
.maximum = 0.75,
|
||||
};
|
||||
};
|
||||
|
||||
pub fn main() !void {
|
||||
const std = @import("std");
|
||||
// 初始化时直接使用默认值
|
||||
const threshold: Threshold = .default;
|
||||
std.debug.print("minimum is %d, maximum is %d", .{ threshold.minimum, threshold.maximum });
|
||||
}
|
||||
|
||||
// #endregion all_default
|
||||
};
|
||||
|
||||
const Struct = struct {
|
||||
// #region more_struct
|
||||
const std = @import("std");
|
||||
|
@ -93,6 +117,24 @@ const SelfReference2 = struct {
|
|||
// #endregion more_self_reference2
|
||||
};
|
||||
|
||||
const DestructTuple = struct {
|
||||
pub fn main() void {
|
||||
// #region destruct_tuple
|
||||
const print = @import("std").debug.print;
|
||||
|
||||
var x: u32 = undefined;
|
||||
var y: u32 = undefined;
|
||||
var z: u32 = undefined;
|
||||
|
||||
const tuple = .{ 1, 2, 3 };
|
||||
|
||||
x, y, z = tuple;
|
||||
|
||||
print("tuple: x = {}, y = {}, z = {}\n", .{ x, y, z });
|
||||
// #endregion destruct_tuple
|
||||
}
|
||||
};
|
||||
|
||||
const SelfReference3 = struct {
|
||||
// #region more_self_reference3
|
||||
const std = @import("std");
|
||||
|
|
|
@ -4,6 +4,8 @@ pub fn main() !void {
|
|||
Expression.main();
|
||||
Catch_tagUnion.main();
|
||||
AutoRefer.main();
|
||||
try LabeledSwitch1.main();
|
||||
try LabeledSwitch2.main();
|
||||
}
|
||||
|
||||
const Basic = struct {
|
||||
|
@ -191,3 +193,91 @@ fn getNum(u: U) u32 {
|
|||
}
|
||||
}
|
||||
// #endregion catch_tag_union_value
|
||||
|
||||
const LabeledSwitch1 = struct {
|
||||
pub fn main() !void {
|
||||
// #region labeled_switch_1
|
||||
sw: switch (@as(i32, 5)) {
|
||||
5 => continue :sw 4,
|
||||
|
||||
// `continue` can occur multiple times within a single switch prong.
|
||||
2...4 => |v| {
|
||||
if (v > 3) {
|
||||
continue :sw 2;
|
||||
} else if (v == 3) {
|
||||
|
||||
// `break` can target labeled loops.
|
||||
break :sw;
|
||||
}
|
||||
|
||||
continue :sw 1;
|
||||
},
|
||||
|
||||
1 => return,
|
||||
|
||||
else => unreachable,
|
||||
}
|
||||
// #endregion labeled_switch_1
|
||||
}
|
||||
};
|
||||
|
||||
const LabeledSwitch2 = struct {
|
||||
pub fn main() !void {
|
||||
// #region labeled_switch_2
|
||||
var sw: i32 = 5;
|
||||
while (true) {
|
||||
switch (sw) {
|
||||
5 => {
|
||||
sw = 4;
|
||||
continue;
|
||||
},
|
||||
2...4 => |v| {
|
||||
if (v > 3) {
|
||||
sw = 2;
|
||||
continue;
|
||||
} else if (v == 3) {
|
||||
break;
|
||||
}
|
||||
|
||||
sw = 1;
|
||||
continue;
|
||||
},
|
||||
1 => return,
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
// #endregion labeled_switch_2
|
||||
}
|
||||
};
|
||||
|
||||
// #region vm
|
||||
const Instruction = enum {
|
||||
add,
|
||||
mul,
|
||||
end,
|
||||
};
|
||||
|
||||
fn evaluate(initial_stack: []const i32, code: []const Instruction) !i32 {
|
||||
const std = @import("std");
|
||||
var stack = try std.BoundedArray(i32, 8).fromSlice(initial_stack);
|
||||
var ip: usize = 0;
|
||||
|
||||
return vm: switch (code[ip]) {
|
||||
// Because all code after `continue` is unreachable, this branch does
|
||||
// not provide a result.
|
||||
.add => {
|
||||
try stack.append(stack.pop().? + stack.pop().?);
|
||||
|
||||
ip += 1;
|
||||
continue :vm code[ip];
|
||||
},
|
||||
.mul => {
|
||||
try stack.append(stack.pop().? * stack.pop().?);
|
||||
|
||||
ip += 1;
|
||||
continue :vm code[ip];
|
||||
},
|
||||
.end => stack.pop().?,
|
||||
};
|
||||
}
|
||||
// #endregion vm
|
||||
|
|
|
@ -49,6 +49,24 @@ const Splat = struct {
|
|||
}
|
||||
};
|
||||
|
||||
const Deconstruct = struct {
|
||||
// #region deconstruct
|
||||
const print = @import("std").debug.print;
|
||||
|
||||
pub fn unpack(x: @Vector(4, f32), y: @Vector(4, f32)) @Vector(4, f32) {
|
||||
const a, const c, _, _ = x;
|
||||
const b, const d, _, _ = y;
|
||||
return .{ a, b, c, d };
|
||||
}
|
||||
|
||||
pub fn main() void {
|
||||
const x: @Vector(4, f32) = .{ 1.0, 2.0, 3.0, 4.0 };
|
||||
const y: @Vector(4, f32) = .{ 5.0, 6.0, 7.0, 8.0 };
|
||||
print("{}", .{unpack(x, y)});
|
||||
}
|
||||
// #endregion deconstruct
|
||||
};
|
||||
|
||||
const Reduce = struct {
|
||||
const std = @import("std");
|
||||
const print = std.debug.print;
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
./14
|
|
@ -1 +1 @@
|
|||
12
|
||||
./14
|
|
@ -6,7 +6,7 @@ outline: deep
|
|||
|
||||
现在,我们已经安装了 zig,也安装了对应的编辑器,接下来就了解一下基本的 `zig` 命令。
|
||||
|
||||
这单单一个命令可神了,它囊括了项目建立、构建、测试、运行,甚至你可以用它来部署你的项目,也可以用来给 C/C++ 作为编译或者依赖管理工具,非常的全面,这一切都是得益于 zig 本身的编译期。
|
||||
这单单一个命令可神了,它囊括了项目建立、构建、测试、运行,甚至你可以用它来部署你的项目,也可以用来给 C/C++ 作为编译或者依赖管理工具,非常的全面,这一切都是得益于 zig 本身的编译器。
|
||||
|
||||
以下仅列出常用的命令!
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@ outline: deep
|
|||
|
||||
<<<@/code/release/hello_world.zig#one
|
||||
|
||||
然后只需要运行命令:`zig build run`,你就可以看到熟悉的 `Hello, World!` 了!
|
||||
|
||||
_很简单,不是吗?_
|
||||
|
||||
## 简单说明
|
||||
|
|
|
@ -20,6 +20,8 @@ showVersion: false
|
|||
> 允许函数处理各种数据,以及一小组新的编译器指令,以允许使用反射访问有关这些类型的信息。
|
||||
> Zig 还旨在提高代码的安全性,它不提供垃圾回收(GC),但是使用可选类型代替 `null` ,这避免了空指针的出现。
|
||||
|
||||

|
||||
|
||||
## 为何使用 Zig
|
||||
|
||||
从本质上看,Zig 是一门 `low level` 的高级语言,它和 C 很像,但改善旧问题并提供了完善的工具链,并且它可选支持 `libc`。
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
---
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# 杂项
|
||||
|
||||
本部分是关于 zig 一些额外知识的补充,暂时还没有决定好分类到何处!
|
||||
|
||||
## 容器
|
||||
|
||||
在 Zig 中,**容器** 是充当保存变量和函数声明的命名空间的任何语法结构。容器也是可以实例化的类型定义。结构体、枚举、联合、不透明,甚至 Zig 源文件本身都是容器,但容器并不能包含语句(语句是描述程序运行操作的一个单位)。
|
||||
|
||||
当然,你也可以这样理解,容器是一个只包含变量或常量定义以及函数定义的命名空间。
|
||||
|
||||
注意:容器和块(block)不同!
|
||||
|
||||
## `usingnamespace`
|
||||
|
||||
关键字 `usingnamespace` 可以将一个容器中的所有 `pub` 声明混入到当前的容器中。
|
||||
|
||||
例如,可以使用将 `usingnamespace` 将 std 标准库混入到 `main.zig` 这个容器中:
|
||||
|
||||
```zig
|
||||
const T = struct {
|
||||
usingnamespace @import("std");
|
||||
};
|
||||
pub fn main() !void {
|
||||
T.debug.print("Hello, World!\n", .{});
|
||||
}
|
||||
```
|
||||
|
||||
注意:无法在结构体 `T` 内部直接使用混入的声明,需要使用 `T.debug` 这种方式才可以!
|
||||
|
||||
`usingnamespace` 还可以使用 `pub` 关键字进行修饰,用于转发声明,这常用于组织 API 文件和 C import。
|
||||
|
||||
```zig
|
||||
pub usingnamespace @cImport({
|
||||
@cInclude("epoxy/gl.h");
|
||||
@cInclude("GLFW/glfw3.h");
|
||||
@cDefine("STBI_ONLY_PNG", "");
|
||||
@cDefine("STBI_NO_STDIO", "");
|
||||
@cInclude("stb_image.h");
|
||||
});
|
||||
```
|
|
@ -27,3 +27,5 @@ C 很好,非常好,它非常成功,以至于 C 现在已经不再是一门
|
|||
或许可能有人会跟我说 Rust 比 Zig 好,我要说的是你说的基本是对的,目前情况来看,Rust 的的确确比 Zig 好很多,更为完善的生态,更多能拿得出手的代表项目,以及相较 Zig 庞大很多的社区等等,但是在未来谁说的准呢?更何况 Rust 和 Zig 并不是一个赛道上的东西,在我看来,Rust 的目标是 C++ 的替代,因此我更愿意称之为“披着高抽象皮的 low level 语言”,Zig 的目标则是 C,而且目前 Zig 的特性也的确在这个方向发展。
|
||||
|
||||
Zig 的社区需要更多的人来构建,所以我写了这个文档,帮助新人来更好的理解和学习 Zig!
|
||||
|
||||

|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 524 KiB |
|
@ -0,0 +1,408 @@
|
|||
---
|
||||
outline: deep
|
||||
comments: false
|
||||
showVersion: false
|
||||
---
|
||||
|
||||
# `0.14.0`
|
||||
|
||||
2025/3/5,`0.14.0` 发布,历时 9 个月,有 251 位贡献者,一共进行了 3467 次提交!
|
||||
|
||||
除了大量的构建系统升级、语言变更和目标支持增强之外,此版本还朝着我们的两项长期目标迈进——增量编译和更快的 x86 后端 - 都专注于 **减少编辑/编译/调试周期延迟**。
|
||||
|
||||
## 目标支持
|
||||
|
||||
此版本的 Zig 的一个重要特性是改进目标支持状况,Zig 可以正确交叉编译和运行的目标列表已增加很多。
|
||||
Zig 可以为其构建程序的目标见下表,[zig-bootstrap README](https://github.com/ziglang/zig-bootstrap/blob/master/README.md#supported-triples) 涵盖了 Zig 编译器本身可以轻松交叉编译以运行的目标。
|
||||
|
||||
针对特性目标修复的完整列表太长,不在列出,但简言之,如果过去尝试针对 `arm/thumb`、`mips/mips64`、`powerpc/powerpc64`、`riscv32/riscv64` 或 `s390x` 并遇到工具链问题、缺少标准库支持或看似无意义的崩溃,那么现在大概率 Zig `0.14.0` 可以正常工作。
|
||||
|
||||
### 目标三重变化
|
||||
|
||||
Zig 对理解的目标三重做了一些更改:
|
||||
|
||||
- `arm-windows-gnu` 已被替换为 `thumb-windows-gnu`,以反映 Windows 仅支持 Thumb-2 模式的事实。
|
||||
- `armeb-windows-gnu` 和 `aarch64_be-windows-gnu` 已被移除,因为 Windows 不支持大端。
|
||||
- 添加了 `thumb[eb]-linux-musleabi[hf]` 以使用 musl libc 针对纯 Thumb 模式。
|
||||
- 添加了 `mips[el]-linux-musleabi` 以使用软浮点 ABI 和 musl libc 针对 32 位 mips。
|
||||
- `mips[el]-linux-musl` 已重命名为 `mips[el]-linux-musleabihf` 以明确它们针对硬浮点 ABI。
|
||||
- `mips64[el]-linux-musl` 已重命名为 `mips64[el]-linux-muslabi64` 以与 `mips64[el]-linux-gnuabi64` 保持一致。
|
||||
- 添加了 `mips64[el]-linux-muslabin32` 以使用 32 位指针和 musl libc 针对 64 位 mips。
|
||||
- 添加了 `powerpc-linux-musleabi` 以使用软浮点 ABI 和 musl libc 针对 32 位 powerpc。
|
||||
- `powerpc-linux-musl` 已重命名为 `powerpc-linux-musleabihf` 以明确它针对硬浮点 ABI。
|
||||
- 添加了 `x86_64-linux-muslx32` 以使用 32 位指针和 musl libc 针对 64 位 x86。
|
||||
|
||||
### 分层系统
|
||||
|
||||
Zig 对各种目标的支持水平大致分为四个层级,其中 Tier 1 是最高的。请注意,目前,即使是一些 Tier 1 目标也可能有一些禁用的测试,因为我们正在努力实现 100% 的测试通过率。
|
||||
|
||||
#### Tier 1
|
||||
|
||||
- 所有非实验性语言功能都已知可以正常工作。
|
||||
- 编译器可以在不依赖 LLVM 的情况下为此目标生成机器代码,同时在功能支持方面与 LLVM 相当。
|
||||
- 即使在交叉编译时,libc 也可用于此目标。
|
||||
|
||||
#### Tier 2
|
||||
|
||||
- 标准库的跨平台抽象具有此目标的实现。
|
||||
- 此目标具有调试信息功能,因此在断言失败和崩溃时会生成堆栈跟踪。
|
||||
- CI 机器会在每次提交到主分支时自动构建和测试此目标。
|
||||
|
||||
#### Tier 3
|
||||
|
||||
- 编译器可以通过依赖外部后端(如 LLVM)为此目标生成机器代码。
|
||||
- 链接器可以为此目标生成目标文件、库和可执行文件。
|
||||
|
||||
#### Tier 4
|
||||
|
||||
- 编译器可以通过依赖外部后端(如 LLVM)为此目标生成汇编源代码。
|
||||
- 此目标可能被 LLVM 视为实验性目标,在这种情况下,需要从源代码构建 LLVM 和 Zig 才能使用它。
|
||||
|
||||
### 支持列表
|
||||
|
||||
在下表中,🟢 表示完全支持,🔴 表示不支持,🟡 表示部分支持,例如仅支持某些子目标,或存在一些显著的已知问题。❔ 表示状态大致未知,通常是因为该目标很少被使用。将鼠标悬停在其他图标上以查看详细信息。
|
||||
|
||||
| 目标 | 层级 | 语言特性 | 标准库 | 代码生成 | 链接器 | 调试信息 | libc | CI |
|
||||
| ------------------- | ---- | -------- | ------ | -------- | ------ | -------- | ---- | --- |
|
||||
| x86_64-linux | 1 | 🟢 | 🟢 | 🖥️⚡ | 🟢 | 🟢 | 🟢 | 🟢 |
|
||||
| aarch64[_be]-linux | 2 | 🟢 | 🟢 | 🖥️🛠️ | 🟢 | 🟢 | 🟢 | 🟢 |
|
||||
| aarch64-macos | 2 | 🟢 | 🟢 | 🖥️ | 🟢 | 🟢 | 🟢 | 🟢 |
|
||||
| aarch64-windows | 2 | 🟢 | 🟢 | 🖥️ | 🟢 | 🟢 | 🟢 | 🟢 |
|
||||
| arm[eb]-linux | 2 | 🟢 | 🟢 | 🖥️🛠️ | 🟢 | 🟢 | 🟢 | 🟢 |
|
||||
| powerpc-linux | 2 | 🟢 | 🟢 | 🖥️ | 🟡 | 🟢 | 🟢 | 🟡 |
|
||||
| powerpc64-linux | 2 | 🟢 | 🟢 | 🖥️ | 🟡 | 🟢 | 🟡 | 🟡 |
|
||||
| powerpc64le-linux | 2 | 🟢 | 🟢 | 🖥️ | 🟢 | 🟢 | 🟢 | 🟢 |
|
||||
| wasm32-wasi | 2 | 🟢 | 🟢 | 🖥️⚡ | 🟢 | 🟢 | 🟢 | 🟢 |
|
||||
| x86-linux | 2 | 🟢 | 🟢 | 🖥️ | 🟢 | 🟢 | 🟢 | 🟢 |
|
||||
| x86-windows | 2 | 🟢 | 🟢 | 🖥️ | 🟢 | 🟢 | 🟢 | 🟢 |
|
||||
| x86_64-macos | 2 | 🟢 | 🟢 | 🖥️ | 🟢 | 🟢 | 🟢 | 🟢 |
|
||||
| x86_64-windows | 2 | 🟢 | 🟢 | 🖥️ | 🟢 | 🟢 | 🟢 | 🟢 |
|
||||
| aarch64-freebsd | 3 | ❔ | ❔ | 🖥️🛠️ | 🟢 | 🟢 | 🔴 | 🔴 |
|
||||
| aarch64[_be]-netbsd | 3 | ❔ | ❔ | 🖥️🛠️ | 🟢 | ❔ | 🔴 | 🔴 |
|
||||
| aarch64-openbsd | 3 | ❔ | ❔ | 🖥️🛠️ | 🟢 | ❔ | 🔴 | 🔴 |
|
||||
| hexagon-linux | 3 | 🟡 | 🟡 | 🖥️ | 🟢 | 🔴 | 🔴 | 🔴 |
|
||||
| loongarch64-linux | 3 | 🟡 | 🟡 | 🖥️ | 🟢 | 🔴 | 🟢 | 🔴 |
|
||||
| mips[el]-linux | 3 | 🟢 | 🟢 | 🖥️ | 🟢 | 🔴 | 🟢 | 🔴 |
|
||||
| mips64[el]-linux | 3 | 🟢 | 🟢 | 🖥️ | 🟡 | 🔴 | 🟢 | 🔴 |
|
||||
| riscv32-linux | 3 | 🟢 | 🟢 | 🖥️ | 🟢 | 🔴 | 🟢 | 🟢 |
|
||||
| riscv64-linux | 3 | 🟢 | 🟢 | 🖥️🛠️ | 🟢 | 🔴 | 🟢 | 🟢 |
|
||||
| s390x-linux | 3 | 🟢 | 🟢 | 🖥️ | 🟢 | 🔴 | 🟢 | 🟢 |
|
||||
| sparc64-linux | 3 | ❔ | 🟢 | 🖥️🛠️ | 🟢 | ❔ | 🟢 | 🔴 |
|
||||
| sparc64-solaris | 3 | ❔ | ❔ | 🖥️🛠️ | 🟢 | ❔ | 🔴 | 🔴 |
|
||||
| wasm64-wasi | 3 | ❔ | ❔ | 🖥️⚡ | 🟢 | ❔ | 🔴 | 🔴 |
|
||||
| x86_64-dragonfly | 3 | 🟢 | ❔ | 🖥️⚡ | 🟢 | ❔ | 🔴 | 🔴 |
|
||||
| x86_64-freebsd | 3 | 🟢 | 🟢 | 🖥️⚡ | 🟢 | 🟢 | 🔴 | 🔴 |
|
||||
| x86_64-illumos | 3 | 🟢 | ❔ | 🖥️⚡ | 🟢 | ❔ | 🔴 | 🔴 |
|
||||
| x86_64-netbsd | 3 | 🟢 | 🟢 | 🖥️⚡ | 🟢 | 🟢 | 🔴 | 🔴 |
|
||||
| x86_64-openbsd | 3 | 🟢 | 🟢 | 🖥️⚡ | 🟢 | 🟢 | 🔴 | 🔴 |
|
||||
| x86_64-solaris | 3 | 🟢 | ❔ | 🖥️⚡ | 🟢 | ❔ | 🔴 | 🔴 |
|
||||
| arc-linux | 4 | ❔ | 🟢 | 📄 | 🔴 | ❔ | 🟢 | 🔴 |
|
||||
| csky-linux | 4 | ❔ | 🟢 | 📄 | 🔴 | ❔ | 🟢 | 🔴 |
|
||||
| m68k-linux | 4 | ❔ | 🔴 | 🖥️ | 🔴 | ❔ | 🟢 | 🔴 |
|
||||
| m68k-netbsd | 4 | ❔ | 🔴 | 🖥️ | 🔴 | ❔ | 🔴 | 🔴 |
|
||||
| sparc-linux | 4 | ❔ | 🔴 | 🖥️ | 🔴 | ❔ | 🟢 | 🔴 |
|
||||
| xtensa-linux | 4 | ❔ | 🔴 | 📄 | 🔴 | ❔ | 🔴 | 🔴 |
|
||||
|
||||
### 附加平台
|
||||
|
||||
Zig 还对以下目标具有不同程度的支持,这些目标不完全适用于分层系统:
|
||||
|
||||
| 目标 | 目标 | 目标 |
|
||||
| ------------------------- | ----------------------------- | ---------------------------- |
|
||||
| aarch64-driverkit | aarch64-ios | amdgcn-amdhsa |
|
||||
| aarch64[_be]-freestanding | aarch64-tvos | arc-freestanding |
|
||||
| aarch64-uefi | aarch64-visionos | arm[eb]-freestanding |
|
||||
| aarch64-watchos | avr-freestanding | bpf(eb,el)-freestanding |
|
||||
| csky-freestanding | hexagon-freestanding | kalimba-freestanding |
|
||||
| lanai-freestanding | loongarch(32,64)-freestanding | loongarch(32,64)-uefi |
|
||||
| m68k-freestanding | mips[64][el]-freestanding | msp430-freestanding |
|
||||
| nvptx[64]-cuda | nvptx[64]-nvcl | powerpc[64][le]-freestanding |
|
||||
| propeller-freestanding | riscv(32,64)-freestanding | riscv(32,64)-uefi |
|
||||
| s390x-freestanding | sparc[64]-freestanding | spirv(32,64)-opencl |
|
||||
| spirv(32,64)-vulkan | ve-freestanding | wasm(32,64)-emscripten |
|
||||
| wasm(32,64)-freestanding | x86-elfiamcu | x86[_64]-freestanding |
|
||||
| x86[_64]-uefi | x86_64-driverkit | x86_64-ios |
|
||||
| x86_64-tvos | x86_64-visionos | x86_64-watchos |
|
||||
| xcore-freestanding | xtensa-freestanding | |
|
||||
|
||||
## 编译器
|
||||
|
||||
未分类的更改:
|
||||
|
||||
- `-fno-omit-frame-pointer` 现在是 x86 目标上 `ReleaseSmall` 的默认设置
|
||||
- 管道:更早地生成产生链接器输入的作业
|
||||
|
||||
### 多线程后端支持
|
||||
|
||||
编译器的一些后端(例如 x86 后端)现在支持在与前端不同的线程中运行代码生成。作为一个数据点,这使得编译器在一台计算机上自我构建的速度从 12.8 秒加快到 8.57 秒。
|
||||
|
||||
### 增量编译
|
||||
|
||||
虽然此功能尚未准备好默认启用,但可以通过传递给 `zig build` 的 `-fincremental` 标志选择加入。建议与文件系统监视结合使用,因为编译器状态序列化尚未实现。
|
||||
|
||||
此功能的完整性因使用的链接器后端而异。它们中的任何一个都尚未普遍准备好使用,但与 `-fno-emit-bin` 结合使用效果很好。
|
||||
|
||||
鼓励用户创建一个仅检查编译错误的构建选项,并通过以下方式尝试增量编译:
|
||||
|
||||
```zig
|
||||
const no_bin = b.option(bool, "no-bin", "skip emitting binary") orelse false;
|
||||
if (no_bin) {
|
||||
b.getInstallStep().dependOn(&exe.step);
|
||||
} else {
|
||||
b.installArtifact(exe);
|
||||
}
|
||||
```
|
||||
|
||||
在进行大型重构并且希望快速获得编译错误反馈时:
|
||||
|
||||
```sh
|
||||
$ zig build -Dno-bin -fincremental --watch
|
||||
Build Summary: 3/3 steps succeeded
|
||||
install success
|
||||
└─ zig build-exe zig Debug native success 14s
|
||||
└─ options success
|
||||
Build Summary: 3/3 steps succeeded
|
||||
install success
|
||||
└─ zig build-exe zig Debug native success 63ms
|
||||
watching 119 directories, 1 processes
|
||||
```
|
||||
|
||||
上面的例子中,对于 50 万行代码库,需要 14 秒才能生成编译错误。但是,由于我们使用了 `--watch` 和 `-fincremental` ,因此在进行编辑并保存后,仅需 63 毫秒即可对更改的代码进行重新分析。
|
||||
|
||||
那些在构建脚本中添加 `-Dno-bin` 选项的用户也可以享受类似的工作流程。
|
||||
|
||||
未来,这也将能够生成一个完全可用的二进制文件,可以像平常一样进行测试和调试。
|
||||
|
||||
该功能尚不兼容 `usingnamespace`,建议尽可能避免使用 `usingnamespace`,根据社区讨论,在未来很可能删除 `usingnamespace`。
|
||||
|
||||
### x86 后端
|
||||
|
||||
x86 后端现在通过了 1923 个测试中的 1884(98%)。目前还未作为默认后端使用,但在开发过程中,它通常比 LLVM 后端更好,因为它具有显著更快的编译速度,并且具有更好的调试器支持。
|
||||
|
||||
该后端接近完成,预计在下一个发布周期使用,作为调试默认的默认后端。建议用户现在就在 `0.14.0` 版本中尝试它。可以使用`-fno-llvm` 或在构建脚本中 `use_llvm = false` 来启用它。
|
||||
|
||||
### 导入 ZON
|
||||
|
||||
现在可以在编译时导入 ZON:
|
||||
|
||||
```zig
|
||||
const foo: Foo = @import("foo.zon");
|
||||
```
|
||||
|
||||
目前,这需要已知的结果类型,计划未来取消此限制。
|
||||
|
||||
有关在运行时导入 ZON 的信息,请参阅升级指南中的 **ZON 解析和序列化**。
|
||||
|
||||
### 标记器:简化和规范一致性
|
||||
|
||||
我们用模糊测试器测试了 tokenizer,结果它立刻就崩溃了。检查后,对这个实现不满意。
|
||||
|
||||
此提交移除了几个机制:
|
||||
|
||||
- 移除了“无效字节”编译错误注释。
|
||||
- 通过使恢复总是在换行处发生,而不是其他地方,大大简化了标记器恢复。
|
||||
- 移除了 UTF-8 验证。
|
||||
- 将一些字符验证逻辑移动到 `std.zig.parseCharLiteral`。
|
||||
|
||||
移除 UTF-8 验证是对 [#663](https://github.com/ziglang/zig/issues/663) 的回退,然而,现有实现存在问题。当重新添加此功能时,必须在检查其与同一文件上的独立 Unicode 验证实现匹配的属性时进行模糊测试。同时,模糊测试还应检查该提案的其他属性,例如源代码中不存在 ASCII 控制字符,`\r` 总是跟随 `\n`,等等。
|
||||
|
||||
此提交中包含的其他更改:
|
||||
|
||||
- 弃用 `std.unicode.utf8Decode` 及其 WTF-8 对应函数。此函数的 API 很尴尬,太容易被误用。
|
||||
- 使 `utf8Decode2` 及其相关函数使用数组作为参数,消除运行时断言,转而使用类型系统。
|
||||
|
||||
在此提交之后,由模糊测试发现的崩溃(即 `"\x07\xd5\x80\xc3=o\xda|a\xfc{\x9a\xec\x91\xdf\x0f\\\x1a^\xbe;\x8c\xbf\xee\xea"`)不再导致崩溃。没有必要添加此单元测试,因为简化的逻辑消除了这种性质的崩溃。
|
||||
|
||||
**Benchmark 1 (100 runs)**: `before/zig ast-check /home/andy/dev/zig/src/Sema.zig`
|
||||
|
||||
| 测量项 | 平均值 ± 标准差 | 最小值 … 最大值 | 异常值比例 | 变化率 |
|
||||
| ---------------- | --------------- | --------------- | ---------- | ------ |
|
||||
| wall_time | 50.0ms ± 2.04ms | 48.7ms … 57.4ms | 14 (14%) | 0% |
|
||||
| peak_rss | 60.0MB ± 147KB | 59.4MB … 60.2MB | 3 (3%) | 0% |
|
||||
| cpu_cycles | 232M ± 745K | 230M … 234M | 3 (3%) | 0% |
|
||||
| instructions | 522M ± 24.3 | 522M … 522M | 1 (1%) | 0% |
|
||||
| cache_references | 6.55M ± 120K | 6.39M … 7.45M | 2 (2%) | 0% |
|
||||
| cache_misses | 205K ± 3.47K | 198K … 215K | 1 (1%) | 0% |
|
||||
| branch_misses | 2.86M ± 10.3K | 2.80M … 2.87M | 9 (9%) | 0% |
|
||||
|
||||
**Benchmark 2 (104 runs)**: `after/zig ast-check /home/andy/dev/zig/src/Sema.zig`
|
||||
|
||||
| 测量项 | 平均值 ± 标准差 | 最小值 … 最大值 | 异常值比例 | 变化率 |
|
||||
| ---------------- | --------------- | --------------- | ---------- | --------------- |
|
||||
| wall_time | 48.3ms ± 250us | 48.1ms … 50.0ms | 10 (10%) | ⚡- 3.3% ± 0.8% |
|
||||
| peak_rss | 62.4MB ± 142KB | 62.1MB … 62.6MB | 0 (0%) | 💩+ 4.1% ± 0.1% |
|
||||
| cpu_cycles | 227M ± 637K | 226M … 230M | 7 (7%) | ⚡- 1.9% ± 0.1% |
|
||||
| instructions | 501M ± 44.8 | 501M … 501M | 7 (7%) | ⚡- 4.0% ± 0.0% |
|
||||
| cache_references | 6.65M ± 141K | 6.45M … 7.67M | 4 (4%) | + 1.5% ± 0.5% |
|
||||
| cache_misses | 208K ± 3.79K | 201K … 226K | 3 (3%) | + 1.3% ± 0.5% |
|
||||
| branch_misses | 2.84M ± 8.62K | 2.81M … 2.86M | 1 (1%) | - 0.4% ± 0.1% |
|
||||
|
||||
### 在所有函数中包含错误跟踪
|
||||
|
||||
具体见 PR [#22572](https://github.com/ziglang/zig/issues/22572)。
|
||||
|
||||
## 链接器
|
||||
|
||||
### 将输入文件解析移至前端
|
||||
|
||||
将 GNU `ld` 脚本处理移至前端,以加入相关的库查找逻辑,使得库符合与所有其他库相同的搜索标准。
|
||||
|
||||
此更改是必要的,以便编译器在编译开始时了解所有链接器输入,从而使链接和编译可以同时开始。在 `flush()` 期间发现链接器输入为时已晚。此分支完全移除了传递给 ELF 链接代码的 `lib_dirs`。
|
||||
|
||||
不幸的是,这意味着在针对 ELF 时,需要对所有 `.so` 文件进行文件系统访问,以确定它们是否是链接器脚本,因此我引入了一个选择性加入的 CLI 标志来启用 `.so` 脚本。当遇到 GNU ld 脚本时,错误消息会通知用户有关 CLI 标志的信息,该标志将立即解决他们的问题,即传递 `-fallow-so-scripts` 给 `zig build`。这意味着那些没有奇怪库的用户,或者至少避免链接这些库的用户,不必支付这些文件系统访问的成本。
|
||||
|
||||
所有目标文件、归档文件和共享目标文件的解析现在都在并行编译管道中进行,而不是在最后形成瓶颈。
|
||||
|
||||
这朝着增量编译迈出了一步。
|
||||
|
||||
### Wasm 链接器重构
|
||||
|
||||
目标:
|
||||
|
||||
- 使用 wasm 链接器和后端时编译更快
|
||||
- 通过直接将内存中的链接器状态复制到磁盘来启用保存编译器状态
|
||||
- 更有效的编译器内存利用
|
||||
- 为 wasm 链接器代码引入整数类型安全
|
||||
- 生成更好的 WebAssembly 代码
|
||||
- 完全参与增量编译
|
||||
- 在编译器管道中尽可能多地完成工作,而不是在最后形成瓶颈,同时继续进行垃圾收集
|
||||
- 避免不必要的堆分配
|
||||
- 避免不必要的间接函数调用
|
||||
|
||||
所有这些目标都已实现。演示和性能数据点在报告中可用。
|
||||
|
||||
虽然它通过了之前的相同测试套件,但链接器尚未完成,尚未默认启用,因此不会消除对 LLD 的依赖。
|
||||
|
||||
### 模糊测试器
|
||||
|
||||
Zig `0.14.0` 集成了一个模糊测试器。它处于 `alpha` 状态,这意味着使用它需要参与开发。
|
||||
|
||||
为构建运行器添加了 `--fuzz` CLI 选项。当使用此选项时,它会重新构建包含至少一个模糊测试的任何单元测试二进制文件,并使用 `-ffuzz`,然后告诉它开始模糊测试,这会进行进程内模糊测试。
|
||||
|
||||
这仅包含模糊测试器逻辑的基本实现,实际上只是一些早期的实验,但已经使这个测试用例在我们的机器上在 65 毫秒内失败:
|
||||
|
||||
```zig
|
||||
test "fuzz example" {
|
||||
const input_bytes = std.testing.fuzzInput(.{});
|
||||
try std.testing.expect(!std.mem.eql(u8, "canyoufindme", input_bytes));
|
||||
}
|
||||
```
|
||||
|
||||
`--fuzz` 标志使构建系统生成一个 HTTP 服务器,该服务器提供一个模糊测试器 Web UI,显示模糊测试器探索不同输入时实时更新的代码覆盖率。
|
||||
|
||||
```sh
|
||||
$ zig build test --fuzz
|
||||
info: web interface listening at http://127.0.0.1:38239/
|
||||
info: hint: pass --port 38239 to use this same port next time
|
||||
[0/1] Fuzzing
|
||||
└─ foo.bar.example
|
||||
```
|
||||
|
||||
[asciinema demo](https://asciinema.org/a/asgN7rIr6LFeMhQMOXI825wGe)
|
||||
|
||||
[vedio demo and screenshots](https://github.com/ziglang/zig/pull/20958)
|
||||
|
||||
## Bug 修复
|
||||
|
||||
在该版本中关闭的 [Issue 列表](https://github.com/ziglang/zig/issues?q=is%3Aclosed+is%3Aissue+label%3Abug+milestone%3A0.14.0)
|
||||
|
||||
## 工具链
|
||||
|
||||
### UBSan 运行时
|
||||
|
||||
Zig 现在提供了一个用于 [UBSan](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html) 的运行时库,在 Debug 模式下编译时默认启用。
|
||||
|
||||
简言之,默认情况下,当在 C 代码中触发未定义行为时,它现在看起来像这样:
|
||||
|
||||
```sh
|
||||
$ zig run test.c -lc
|
||||
thread 208135 panic: signed integer overflow: 2147483647 + 2147483647 cannot be represented in type 'int'
|
||||
/home/david/Code/zig/build/test.c:4:14: 0x1013e41 in foo (test.c)
|
||||
return x + y;
|
||||
^
|
||||
/home/david/Code/zig/build/test.c:8:18: 0x1013e63 in main (test.c)
|
||||
int result = foo(0x7fffffff, 0x7fffffff);
|
||||
^
|
||||
../sysdeps/nptl/libc_start_call_main.h:58:16: 0x7fca4c42e1c9 in __libc_start_call_main (../sysdeps/x86/libc-start.c)
|
||||
../csu/libc-start.c:360:3: 0x7fca4c42e28a in __libc_start_main_impl (../sysdeps/x86/libc-start.c)
|
||||
???:?:?: 0x1013de4 in ??? (???)
|
||||
???:?:?: 0x0 in ??? (???)
|
||||
fish: Job 1, 'zig run test.c -lc' terminated by signal SIGABRT (Abort)
|
||||
```
|
||||
|
||||
如果默认设置未能正确检测是否应省略或包含运行时库,可以分别使用 `-fno-ubsan-rt` 和 `-fubsan-rt` 覆盖默认设置。相关 [Issue](https://github.com/ziglang/zig/issues/22572)。
|
||||
|
||||
[开发日志](https://ziglang.org/devlog/2025/#2025-02-24)
|
||||
|
||||
### compiler_rt
|
||||
|
||||
#### 优化的 `memcpy`
|
||||
|
||||
相关 PR [#18912](https://github.com/ziglang/zig/pull/18912)
|
||||
|
||||
### LLVM 19
|
||||
|
||||
此版本的 Zig 升级到 [LLVM 19.1.7](https://releases.llvm.org/19.1.0/docs/ReleaseNotes.html)。这涵盖了 Clang (`zig cc`/`zig c++`)、libc++、libc++abi、libunwind 和 libtsan。
|
||||
|
||||
### musl 1.2.5
|
||||
|
||||
Zig 附带了 musl 的源代码。
|
||||
|
||||
当选择 musl C ABI 时,Zig 从源代码为所选目标构建静态 musl。
|
||||
|
||||
Zig 还支持针对动态链接的 [musl](https://releases.llvm.org/19.1.0/docs/ReleaseNotes.html),这对于使用它作为系统 libc 的 Linux 发行版(如 [Alpine Linux](https://www.alpinelinux.org/))非常有用。
|
||||
|
||||
此版本保持 v1.2.5,但:
|
||||
|
||||
- 应用了 Rich Felker 的 [CVE-2025-26519](https://www.openwall.com/lists/oss-security/2025/02/13/2) 缓解补丁。
|
||||
- 应用了许多正在上游处理的特定目标补丁。
|
||||
- 更新了动态链接时提供的 libc 存根符号列表。
|
||||
- 修复了一些特殊 libc 函数(如软浮点目标上的 `fma`)中的堆栈溢出问题。
|
||||
- 此外,Zig 不再附带 musl 的 [`memcpy` 文件](https://github.com/ziglang/zig/pull/22513)。相反,Zig 提供了优化的 `memcpy`。
|
||||
|
||||
### glibc 2.41
|
||||
|
||||
在交叉编译时,现在可以使用 glibc 版本 2.40 和 2.41。
|
||||
|
||||
一些显著的修复:
|
||||
|
||||
- 修复了 Zig 构建的可执行文件未定义 `_IO_stdin_used`,导致它们在多个目标上无法使用的问题。
|
||||
- 修复了某些目标(例如 powerpc 和 s390x 上的 `lgammal`)导致重复存根符号的边缘情况。
|
||||
- 更改了存根符号,使它们不再具有相同的地址。这导致 LLVM 在某些目标上合并它们,导致代码严重损坏。
|
||||
- 应用了一个针对 LLVM 错误的解决方法,该错误导致在针对 mips r6 时 `j $ra` 被错误地汇编。
|
||||
- 应用了一个针对 LLVM 的 s390x 汇编器缺少语法支持的解决方法。
|
||||
|
||||
### Linux 6.13.4 头文件
|
||||
|
||||
此版本包括 Linux 内核版本 6.13.4 的头文件。
|
||||
|
||||
### Darwin libSystem 15.1
|
||||
|
||||
此版本包括 Xcode SDK 版本 15.1 的 Darwin libSystem 符号。
|
||||
|
||||
### MinGW-w64
|
||||
|
||||
此版本将捆绑的 MinGW-w64 版本提升到提交 `3839e21b08807479a31d5a9764666f82ae2f0356`。
|
||||
|
||||
其他更改:
|
||||
|
||||
- Zig 现在捆绑了 `winpthreads` 库。
|
||||
- Zig 现在支持为 `thumb-windows-gnu` 进行交叉编译。
|
||||
|
||||
### wasi-libc
|
||||
|
||||
此版本将捆绑的 wasi-libc 版本提升到提交 `d03829489904d38c624f6de9983190f1e5e7c9c5`。
|
||||
|
||||
## 路线图
|
||||
|
||||
`0.15.0` 版本周期的主要目标将是编译速度。
|
||||
|
||||
一些即将到来的里程碑:
|
||||
|
||||
- 使 x86 后端成为调试模式的默认后端。
|
||||
- 增强链接器实现,消除对 LLD 的依赖并支持增量编译。
|
||||
- 增强集成的模糊测试器,使其与 AFL 和其他最先进的模糊测试器竞争。
|
||||
|
||||
这里的想法是,优先提高编译速度将增加编译器本身的开发速度,从而在接下来的发布周期中修复更多的错误并完成更多的功能。
|
||||
|
||||
为了提高编译速度,可能会对语法进行变动。
|
File diff suppressed because it is too large
Load Diff
|
@ -8,8 +8,8 @@
|
|||
"export-pdf": "press-export-pdf export course"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.13.5",
|
||||
"prettier": "^3.5.1",
|
||||
"@types/node": "^22.14.1",
|
||||
"prettier": "^3.5.3",
|
||||
"vitepress": "^1.6.3",
|
||||
"vitepress-export-pdf": "^1.0.0"
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue