diff --git a/learn/basic/hello-world.md b/learn/basic/hello-world.md index 02058015..006e215b 100644 --- a/learn/basic/hello-world.md +++ b/learn/basic/hello-world.md @@ -16,7 +16,7 @@ pub fn main() void { _很简单,不是吗?_ -## 说明 +## 简单说明 以上程序中,我们先通过 `@import` 这个内置函数(在zig中有很多的内置函数,它们都以`@`开头,并且遵循[小驼峰命名法](#))引入了 zig 的标准库。 @@ -32,3 +32,60 @@ zig 会自动为我们根据后面的参量表推导出对应的类型,当 zig 你是不是觉得自己被耍了?别担心,上面仅仅只是一个示例而已,来为你演示一下 zig 的使用! ::: + +## 换一种方式? + +接下来,让我们换一种方式来讲述如何“正确”地使用 zig 打印出“Hello, World!”,不要认为这是一个简单的问题,这涉及到计算机相当底层的涉及哲学。 + +首先,我要告诉你,zig 并没有一个内置的打印功能,包含“输出”功能的包只有 `log` 包和 `debug` 包,zig 并没有内置类似与 `@print()` 这种函数。再来一个简单的例子告诉你,如何打印东西(**_但是请记住,以下示例代码不应用于生产环境中_**)。 + +```zig +const std = @import("std"); + +pub fn main() !void { + var out = std.io.getStdOut().writer(); + var err = std.io.getStdErr().writer(); + + try out.print("Hello {s}!\n", .{"out"}); + try err.print("Hello {s}!\n", .{"err"}); +} +``` + +这段代码将会分别输出 `Hello out!` 和 `Hello err!`,这里我需要向你讲述一下 `stdout` 和 `stderr` ,它们均是抽象的io(input and output)流句柄(关于流这个概念可能不好解释,你暂时就当作向水流一样的数据的就行)。`stdout` 用于正常的输出,它可能会出现错误导致写入失败。`stderr` 用于错误输出,我们假定 `stderr` 一定不会失败(这个是操作系统负责保证的),这就是它们的区别。 + +通过 `io` 命名空间(或者称之为包)获取到了标准输出和错误输出的 `writer` 句柄,这个句柄实现流`print`函数,我们只需要正常打印即可! + +接下来加深一点难度,你有没有想过,这些`print`函数是如何实现的? + +它们都是依靠系统调用来实现输出效果,但是这就面临着性能问题,我们知道系统调用会造成内核上下文切换的开销(系统调用的流程:执行系统调用,此时控制权会切换回内核,由内核执行完成进程需要的系统调用函数后再将控制权返回给进程),所以我们如何解决这个问题呢?可以增加一个缓冲区,等到要打印的内容都到一定程度后再一次性全部 `print`,那么此时的解决方式就如下: + +```zig +const std = @import("std"); + +pub fn main() !void { + var out = std.io.getStdOut().writer(); + var err = std.io.getStdErr().writer(); + + // 获取buffer + var out_buffer = std.io.bufferedWriter(out); + var err_buffer = std.io.bufferedWriter(err); + + // 获取writer句柄 + var out_writer = out_buffer.writer(); + var err_writer = err_buffer.writer(); + + // 通过句柄写入buffer + try out_writer.print("Hello {s}!\n", .{"out"}); + try err_writer.print("Hello {s}!\n", .{"err"}); + + // 尝试刷新buffer + try out_buffer.flush(); + try err_buffer.flush(); +} +``` + +此时我们就分别得到了使用缓冲区的 `stdout` 和 `stderr`, 性能更高了! + +## 再进一步? + +TODO