本章导读
很多语言把"标准库很大"当成学习负担,xlang 反过来:标准库的用法你早就会了——第 5 章的 print、第 18 章的 string.split、第 20 章的 time.now(),全是标准库成员。本章要做的是三件事。一,讲透设计哲学:为什么不需要 import(默认注入),为什么 math 直通 CRT 而 time 走 XRT(XRT 优先原则),为什么没用到的函数不进你的可执行文件(按需注入,有 cgen 证据)。二,给全地图:33 个命名空间加全局 4 函数,每个多少函数、什么定位、哪章讲过,一张表查一辈子。三,补讲重点模块:math 的 34 个函数批量实测,random 的种子复现,hash 的 8 个变体,codec/encoding 的编解码对,log 的级别系统,json/xson 的往返,os/signal 的进程信息与信号处理,以及压轴的 tcc 模块——脚本里编译 C 源码跑出真 exe。读完你将获得一项元技能:会自己读标准库源码(36.10),从此不再依赖文档更新速度。
理解标准库默认注入、短命名空间、XRT 优先、按需注入四条设计原则及其 cgen 证据;掌握 33 命名空间+全局的全景分布(515 函数/115 常量实测口径);会用 math 34 函数与 5 常量;会用 random(seed 复现/range/string/Random 类)与 hash 8 函数;会用 codec(base64/hex/URL 三对)与 encoding(detect/name/charSize/encodeUtf8/decodeUtf8);会用 log 级别系统与格式常量;会用 json/xson 的 parse/stringify/valid/read/write;会用 os 进程信息与 signal 信号注册/触发/计数;会用 tcc.run/tcc.compile 在脚本内编译运行 C;掌握 xl6_stdlib_data.c 的源码阅读法。
36.1 设计哲学
四条原则全部来自 docs/标准库设计.md,并且都能用编译器实测验证。
一,默认注入,免 import 免 extern。"DEMO6 标准库默认进入 xlang 编译环境,用户不需要为基础能力手写 extern 或 import。"与第 35 章的扩展库形成分工:标准库是"每行代码都可能用"的基础能力,默认可得;扩展库是按需引入的专业能力,import 显式声明。
二,XRT 优先。"能够由 XRT 提供的能力,必须优先映射到 XRT;确实属于 CRT 基础设施、没有必要在 XRT 重复实现的能力,可以直接映射到 CRT。典型例子是 math 命名空间。"所以 time/file/json 走 XRT(跨平台一致行为),math 走 CRT(sin/sqrt 没有重造轮子的理由)。
三,按需注入。编译器只为实际用到的标准库函数注入 extern 声明。实测一个只用了 math.sqrt 的程序,生成 C 里就一条声明、一次直调:
double sqrt(double x); // ← 唯一注入的数学声明 // 调用处:xvoCreateFloat((double)(sqrt(2.0)))
math.sqrt 展开后就是裸的 CRT sqrt——零包装、零开销,没用到的 500 多个函数连名字都不出现在产物里。
四,字符串所有权统一桥接。XRT 函数返回的需要 xrtFree 的堆字符串,由标准库薄封装统一调 take_cstr 复制进临时内存区后立即释放原指针——你拿到的永远是普通 string,"用户不需要也不能手动释放"(深入细节 2 展开)。
顺带一个反直觉事实:print 家族不经过 CRT 的 stdout——Windows 下生成代码直接用 WinAPI 标准输出句柄写入,POSIX 下用 write(1, ...),连 stdio.h 都不包含。这就是为什么 print 在混编场景永远没有缓冲刷新问题。
36.2 全景地图
本章对 src/stdlib/xl6_stdlib_data.c 注册表做了全量统计:515 个公开函数(唯一名)、115 个常量、33 个命名空间加全局。一表看全(函数数为唯一名计数,已讲章节给出回链):
| 命名空间 | 函数 | 常量 | 定位 | 回链 |
|---|---|---|---|---|
| 全局 | 4 | — | print/println/write/writeln 输出家族 | 第 5 章 |
| math | 34 | 5 | CRT 数学直通 | 本章 36.3 |
| string | 44 | — | 字符串全操作 | 第 18 章 |
| int / float | 1 + 1 | — | 数值格式化 | 第 19 章 |
| time | 67 | — | 时间与日期 | 第 20 章 |
| random | 5 | — | 随机数与随机字符串 | 本章 36.4 |
| hash | 8 | — | 非加密哈希(u32/u64/微纳秒级) | 本章 36.4 |
| codec | 12 | — | base64/hex/URL 编解码 | 本章 36.5 |
| encoding | 21 | 9 | 字符集检测与 UTF 转换 | 本章 36.5 |
| log | 16 | 10 | 级别化日志(控制台/文件/滚动) | 本章 36.6 |
| json | 7 | 3 | JSON 解析/生成/流式访问 | 本章 36.7 |
| xson | 8 | 3 | XSON(xlang 扩展记法) | 本章 36.7 |
| dict | 1 | — | dict.remove 键删除 | 第 17 章 |
| os | 10 | — | 平台/环境变量/参数/进程链 | 本章 36.8 |
| signal | 12 | 7 | 信号注册/触发/路由 | 本章 36.8 |
| tcc | 7 | 5 | 脚本内编译 C(内嵌 TCC) | 本章 36.9 |
| xlang | 3 | — | 脚本内运行 xlang 脚本 | 本章 36.9 |
| path | 19 | — | 路径 join/abs/normalize 等 | stdlib.html |
| file | 31 | 10 | 文件读写/属性/原子写 | stdlib.html |
| dir | 23 | — | 目录遍历/glob/walk | stdlib.html |
| process | 11 | 11 | 子进程 exec/pipeline/管道 | stdlib.html |
| crypto | 54 | — | AES/SHA/RSA/ECC 等加密族 | stdlib.html |
| id | 5 | — | XID 全局唯一标识 | stdlib.html |
| regex | 11 | 4 | 正则编译/查找/替换 | stdlib.html |
| template | 5 | — | 模板引擎 | stdlib.html |
| task | 1 | — | task.group 任务组 | 第 31 章 |
| net | 6 | 7 | 本机 IP/MAC/代理解析 | 卷九预告 |
| tcp | 5 | 6 | TCP 连接/监听 | 第 45 章 |
| udp | 2 | 2 | UDP 绑定/收发 | 第 46 章 |
| url | 18 | — | URL 解析/构造 | 卷九预告 |
| query | 10 | — | 查询串编解码 | 卷九预告 |
| http | 5 | 23 | HTTP 客户端 | 第 43 章 |
| http.server | 48 | 10 | HTTP 服务器与路由 | 第 44 章 |
读表三式:按任务查(要压缩去 codec,要日志去 log,要唯一 ID 去 id);按章节回(已讲章节有系统讲解,未讲的以 stdlib.html 参考页为准);按源码验证(36.10 的方法,任何函数的精确签名一分钟查清)。注意一个口径说明:功能清单文档写的还是"488 函数/105 常量"的旧数,本章的 515/115 是对注册表源码的实时统计——标准库在生长,以注册表为准(深入细节 3)。
36.3 math:CRT 直通
34 个函数分五族,全部批量实测通过。常量 5 个(pi/tau/e/nan/inf),在重写阶段直接替换为字面量,连函数调用都不发生:
| 族 | 函数 | 实测样例 → 结果 |
|---|---|---|
| 三角/双曲 | sin cos tan asin acos atan atan2 | math.deg(math.pi) → 180.0;math.rad(180.0) → 3.141592653589793 |
| 幂/根/对数 | sqrt cbrt pow exp exp2 expm1 log log2 log10 log1p hypot | sqrt(2.0) → 1.4142135623730951;pow(2.0,10.0) → 1024.0;log(e) → 1.0;log10(1000.0) → 3.0;hypot(3.0,4.0) → 5.0 |
| 取整/舍入 | floor ceil round trunc fract mod | floor(3.7) → 3.0;ceil(3.2) → 4.0;round(3.5) → 4.0;trunc(-3.7) → -3.0;mod(10.0,3.0) → 1.0 |
| 符号/最值/夹取 | abs min max clamp sign | abs(-4.2) → 4.2;clamp(15.0,0.0,10.0) → 10.0;sign(-8.0) → -1 |
| 判定 | isNaN isInf isFinite | isNaN(math.nan) → true;isFinite(42.0) → true |
三个使用要点。一,参数返回统一 float(int 实参自动提升),唯 math.abs 有 int 重载保持整数。二,round 是编译器辅助实现(内部用 floor/ceil 组合),不是 CRT round——跨平台行为一致。三,nan/inf 是字面量常量,配合 isNaN/isInf 做防御:if math.isNaN(x) then ...。
36.4 random 与 hash
random 只有 5 个函数,但有两个关键词:seed(seed, seq) 双参数与可复现:
random.seed(uint64(2026), uint64(1)) print(random.u32()) // 831089821 random.seed(uint64(2026), uint64(1)) // 同种子同序列 print(random.u32()) // 831089821 —— 完全复现 print(random.range(1, 6)) // 1..6 闭区间整数 print(random.string(8)) // "1bQptd-0" 类随机串 print(random.string(6, "ABC")) // 限定字符集,如 "BCAAAC"
同 seed 同 seq 给出完全相同的序列——测试与模拟场景的刚需;要不可预测就不调 seed(线程种子自动初始化)。需要多个独立流时用第 34 章见过的 Random native class:Random(uint64(42), uint64(1)) 每个实例一条流,官方示例验证过同参数两实例逐值相等。hash 的 8 个函数是非加密哈希(校验、分桶、指纹用):u32/u64 两宽度,Micro/Nano 两精度变体,WithSeed 三加盐版本:
print(int(hash.u32("hello"))) // 1360413475 print(int(hash.u32("world"))) // 1567078908 —— 输入变则哈希变 print(int(hash.u32WithSeed("hello", uint32(7)))) // 1956780885 —— 加盐变体
注意 int(...) 转换不能省——当前版本直接 print uint32/uint64 值会在链接期失败(坑 3,本章记录的编译器问题)。加密级哈希(SHA 家族)在 crypto 命名空间,别用 hash 做密码学用途。
36.5 codec 与 encoding
codec 是"字节↔文本"的三对编解码加一个百分号变体,全部成对实测:
print(codec.base64Encode("hello")) // aGVsbG8= print(codec.base64Decode("aGVsbG8=")) // hello —— 往返一致 print(codec.hexEncode("hi")) // 6869 print(codec.hexDecode("6869")) // hi print(codec.formUrlEncode("a b+c")) // a+b%2Bc —— 表单风格(空格变 +) print(codec.percentEncode("中文")) // %E4%B8%AD%E6%96%87 —— UTF-8 逐字节转义
每对都有 Bytes 变体(base64EncodeBytes 等)操作 Buffer。formUrl 与 percent 的差异就在空格:表单约定 +,纯百分号约定 %20。encoding 管更大的世界——字符集检测、命名、UTF 互转:
print(encoding.isUtf8("中文")) // true int cs = encoding.detect("hello") print(encoding.name(cs)) // "utf-8" print(encoding.charSize(cs)) // 1 —— 该字符集最小单元字节数 Buffer b = encoding.encodeUtf8("你好") print(b.len()) // 6 —— 两汉字各 3 字节 print(encoding.decodeUtf8(b)) // 你好 —— 往返一致
UTF-16/32 各有 LE/BE 的 encode/decode 四件套加字节序交换函数,9 个字符集常量(encoding.utf8/utf16/bom/autoDetect 等)配合 convertBytes 做任意字符集间转换。中文 Windows 上读老系统 GBK 文件时,convertBytes 就是你的翻译官。
36.6 log 日志系统
16 函数 + 10 常量构成完整日志系统:六个级别(trace/debug/info/warn/error/fatal)、三个输出目标(控制台/文件/滚动文件)、三种格式(text/simple/json)。默认开箱即用——带时间戳、线程号、颜色:
log.info("info msg") 2026-07-19 06:43:01 [INFO ] [tid=23340] [:0 ] info msg log.warn("warn msg") 2026-07-19 06:43:01 [WARN ] [tid=23340] [:0 ] warn msg(黄色)
级别控制实测:默认级别是 info(trace/debug 被压掉),setLevel 动态调整,getLevel/levelName 可回读:
log.setLevel(log.warnLevel) log.info("you should not see this") // 被级别压制,无输出 log.warn("warn after level set") // 正常输出 print(log.levelName(log.getLevel())) // WARN
常量分两组:级别常量 traceLevel(0) 到 offLevel(6),格式常量 textFormat/simpleFormat/jsonFormat。生产用法是 create + addFile/addRollingFile 定制自己的 Logger 实例(滚动文件按大小切分,长期运行的服务必备),flush 强制落盘。log.write 是底层入口(自定义级别与格式串)。相比 print 调试法,log 给了你级别开关——上线后把 setLevel 提到 warn,调试输出一行不用删。
36.7 json 与 xson
json 7 函数覆盖解析/生成/校验/文件/流式,实测往返:
var v = json.parse("{\"name\": \"xlang\", \"version\": 6, \"tags\": [\"a\", \"b\"]}") print(v["name"]) // xlang —— parse 结果是动态容器,下标直取 print(v["version"]) // 6 print(json.valid("{bad json")) // false —— 先校验再解析的好习惯 print(json.stringify(v)) // {"name":"xlang","version":6,"tags":["a","b"]}
文件对 read/write 免手工 IO,stringify/write 的 pretty 参数控制美化;visit 是流式遍历大 JSON 的回调入口(配合 visitComplete/visitStopped/visitInvalid 三个常量判结果),writer 是增量构造器。与 derive json(第 25 章)的分工:derive 处理"已知类型的 class 实例序列化",json.parse/stringify 处理"结构未知或动态的 JSON 数据"——两者底层同一 XRT 引擎。xson 是 xlang 扩展记法,8 函数与 json 对应(多一个 options 构造 XsonOptions),list/set 这类 JSON 表达不了的容器走 xson:
var d = dict{"a": 1, "b": 2} string text = xson.stringify(d) print(text) // {"a":1,"b":2}(此例恰与 JSON 同形) print(xson.valid(text)) // true var back = xson.parse(text) print(back["a"]) // 1 —— 往返一致
36.8 os 与 signal
os 回答"我在哪、我是谁":
print(os.platform()) // "windows" print(os.env("USERPROFILE") != "") // true —— 环境变量读取 print(os.args().length >= 0) // true —— 命令行参数数组
其余成员:cwd/setCwd 当前目录、exec/run/start 系执行外部程序(重型任务走 process 命名空间的管道族)、chain 串接命令。signal 是控制台信号的统一抽象,7 个常量覆盖跨平台交集:INT/TERM/HUP 是 Unix 老朋友,BREAK/CLOSE/LOGOFF/SHUTDOWN 是 Windows 控制台事件——同一套 API 两边跑。官方回归用例展示了完整四件套:回调注册(on/once)、channel 路由(sendTo)、任务组取消(cancelOn)、手工触发(raise):
int hits = 0 func(var) onInt = func(var event) hits = hits + int(event["code"]) end SignalHandle direct = signal.on(signal.INT, onInt) channel<var> events = channel::<var>(2) SignalHandle routed = signal.sendTo(events, signal.INT) // 信号进 channel signal.raise(signal.INT) // 手工触发(测试用) // → 回调执行、channel 收到 {"code", "name"} 事件 signal.count(signal.INT) // ≥ 1 signal.received(signal.INT) // true direct.close() // SignalHandle 用完即关 signal.restore(signal.INT) // 恢复默认处理
实战意义:命令行工具用 signal.on(signal.INT, ...) 把 Ctrl+C 变成"优雅退出"(落盘、关连接);测试用 signal.raise 自触发验证处理逻辑;sendTo 把信号并入第 29 章的 channel 事件循环,cancelOn 把信号接到第 31 章的 TaskGroup 上做整组取消。
36.9 tcc:脚本内编译 C
本章彩蛋。xl.exe 内嵌了完整 TCC(第 1 章讲过 xlang 本身靠它编译),tcc 命名空间把这套能力直接开放给脚本——运行期编译 C 源码并执行:
func main(...) string code = "#include <stdio.h>\nint main(void){ puts(\"hello from embedded C\"); return 42; }\n" int rc = tcc.run(code) println("rc =", rc) end // 输出: // hello from embedded C ← C 程序的 puts 直接打到控制台 // rc = 42 ← C main 的返回值成为 tcc.run 的返回值
还能编译成真实可执行文件——tcc.compile(源码, 输出路径, 模式):
int rc2 = tcc.compile("int main(void){ return 0; }\n", "tcc_out.exe", tcc.EXE) # compile rc = 0 —— 磁盘上多出 3 KB 的 tcc_out.exe,可独立运行
7 个函数分三组:run/runFile(内存编译执行)、compile/compileFile(产出文件,5 个模式常量 MEMORY/EXE/OBJ/DLL/PREPROCESS 可组合)、vfsMount/vfsClear(往编译用虚拟文件系统挂载动态生成的源码,配 runFile/compileFile 用)。Context 创建独立的 TccContext 实例做细粒度控制(加 include 路径、定义宏)。姊妹模块 xlang(3 函数:Context/run/runFile)同理运行 xlang 脚本——xlang 解释执行 xlang。应用场景:插件系统(用户提交 C 热代码)、教学演示(本书这种)、构建脚本里的代码生成器、运行时 JIT 式的热点下沉。安全提示是常识:tcc.run 执行的是任意 C,别编译不可信来源的代码。
36.10 自助阅读源码
标准库的全部真相在一个文件里:src/stdlib/xl6_stdlib_data.c(约 7700 行,随源码发布)。结构从上到下一目了然:
| 区段 | 内容 | 用法 |
|---|---|---|
| 类源码常量 | xl6StdlibRandomClassSource 等——Random/Buffer/Logger/File 等类的完整 xlang 源码 | 看类的字段、方法、native 绑定(第 34 章的知识全用得上) |
| 函数体字符串 | xl6StdJsonStringifyBody 等——每个公开函数的 xlang 薄封装源码 | 看参数校验、默认值、底层调用哪个 __xl_std_/__xrt_ 桥函数 |
| xl6StdlibConsts[] | 115 个常量表:命名空间、名字、类型、值 | 查常量精确值(如 log.warnLevel = 3) |
| xl6StdlibFuncs[] | 914 条函数注册项:命名空间、公开名、内部名、C 符号、参数表、返回类型、函数体 | 查任何函数的精确签名——比文档新、比文档准 |
| 计数宏 | 头文件里 XL6_STDLIB_FUNC_COUNT / CONST_COUNT | 本章 515/115 的出处 |
查询套路:想知道 encoding.charSize 怎么用时,先 grep 注册表找到 {"encoding", "charSize", ...} 一行,记下函数体名 xl6StdEncodingCharSizeBody,再跳到函数体——发现它接 int charset 而非 string(坑 4 就是这么查清的)。914 条注册项 = 515 个唯一公开函数 + 重载 + 内部桥接层(__xrt_string/__xrt_time/__xl_runtime 等下划线命名空间不直接对你开放,是标准库自己的施工脚手架)。
深入细节
标准库调用在编译器里走三层:你写的 math.sqrt(2.0) → 语义检查前命中函数表、改写为内部名 → 注入 extern 声明(double sqrt(double x);)→ 走第 32 章的 C 混编 ABI 路径。设计文档原话:"这保证标准库和 C 混编使用同一条 ABI 路径"——所以标准库函数与你自己 extern 的 C 函数在性能、类型规则上毫无差别。按需注入的副产品:可执行文件体积与标准库总量无关,500 个没用的函数零成本。常量更彻底:math.pi 在重写阶段直接替换为字面量 3.14159265358979323846264338327950288,连符号都不存在。
XRT 很多函数返回"调用方负责 xrtFree"的堆字符串,xlang 的 string 却是非拥有的 const char* 视图——所有权模型天然冲突。标准库的统一解法:底层桥函数返回 point(原始指针),公开薄封装立即调 take_cstr——把字符串复制进 XRT 线程临时内存区,释放原指针,返回普通 string。临时内存随 xlUnit/xrtUnit 或线程退出自动回收,所以"用户不需要也不能手动释放"。这层桥也解释了为什么标准库函数密集调用不会漏内存:每次调用的临时串都在线程临时区登记在册,运行环境收尾时一笔勾销。自己写 extern 对接 XRT 风格 C 库时,可复制这个模式(第 32 章 string 边界的"返回即复制"原则与此同源)。
功能清单文档写"公开函数 488 个、常量 105 个",本章实测注册表是 515/115。差值不是错误,是生长:write/writeln 进全局、http.server 补到 48、xson 增加 visit/writer、signal 补齐 Windows 控制台常量……文档的更新永远滞后于代码。这正是 36.10 教源码阅读法的原因:注册表是唯一实时口径,xl6StdlibFuncs[] 数组长度不会说谎。工程建议:升级 xl.exe 版本后,对注册表 diff 一下(两个版本的 xl6_stdlib_data.c 对比),新增函数一目了然——比翻变更日志可靠。
⚠️ 常见错误
print(math.squrt(2.0)) // ← 报错(第 2 行):squrt 拼写
bad.xl:2:16: [stdlib] standard library namespace has no such symbol
原因:标准库命名空间只在成员访问位置生效,命中命名空间但符号不存在时给专属 [stdlib] 报错——与 undeclared identifier 区分开。正确写法:按 36.2 全景表或 36.10 的注册表查询法核对函数名。
print(math.sqrt("hello")) // ← 报错(第 2 行)
bad.xl:2:21: [sem] 函数调用实参类型和参数类型不匹配: target float, value string
原因:标准库函数同样走完整类型检查,math 族统一收 float。正确写法:数值传 float(int 自动提升);字符串先按第 7 章规则转换。
print(hash.u64("hello")) // 编译过,链接炸:
tcc: error: unresolved reference to 'xl6_xrt_u64_to_string'
原因:本章实测确认的编译器/stdlib 缺口——print 对 uint32/uint64 的值格式化引用了未链接的 xl6_xrt_u64_to_string,random.u32()/hash.u32() 等返回值直接打印都会触发。正确写法:先 int(...) 转换再打印(36.4 实测可行);等官方修复后可去掉转换。
print(encoding.charSize("中")) // ← 报错(第 2 行)
bad.xl:2:29: [sem] 函数调用实参类型和参数类型不匹配: target int, value string
原因:charSize 问的是"某字符集的最小单元字节数",参数是字符集代码 int(detect 的返回值),不是文本。正确写法:encoding.charSize(encoding.detect(text));签名存疑时用 36.10 的注册表查询法,别凭名字猜。
<string>:2: error: missing terminating " character
// tcc.run returned -1 —— C 编译失败
原因:C 源码走两遍转义——xlang 字符串一遍、C 字符串一遍。xlang 写法 \n 在 C 源码里变成真换行,若它落在 C 的字符串字面量内部,C 编译器就报未闭合。本章实测:要在 C 字符串里出现字面 \n,xlang 源码需写四个反斜杠。正确写法:优先用 puts(自带换行)避开内嵌 \n;必须内嵌时按四层转义写,或拆成多行拼接保持可读。
本章小结
| 知识点 | 要点 |
|---|---|
| 规模口径 | 515 公开函数(唯一名)+ 115 常量 + 33 命名空间与全局 4 函数;注册表为唯一实时口径 |
| 设计四原则 | 默认注入免 import;XRT 优先 CRT 兜底;按需注入(cgen 取证);字符串所有权 take_cstr 桥 |
| print 家族 | 不经 CRT stdio:WinAPI 句柄 / write(1) 直写,无缓冲问题 |
| math | 34 函数五族 + 5 常量;统一 float;round 为编译器辅助实现;常量重写期变字面量 |
| random/hash | seed(seed, seq) 双参可复现;Random 类多流;hash 非加密,uint 打印先 int(...) |
| codec/encoding | base64/hex/URL 三对成往返;detect→name→charSize 链;UTF-16/32 LE/BE 全套 |
| log | 六级三目标三格式;默认 info 级;setLevel 动态开关;生产用 create+addRollingFile |
| json/xson | parse→动态容器下标取;valid 先校验;xson 承载 list/set 与 XsonOptions |
| os/signal | platform/env/args;signal 七常量跨平台,on/once/sendTo/cancelOn/raise 四件套 |
| tcc | run/runFile 内存执行、compile/compileFile 产真 exe、vfsMount 动态源码;5 模式常量;亮点:rc 即 C main 返回值 |
| 源码阅读法 | xl6_stdlib_data.c 五区段;914 注册项 = 515 公开 + 重载 + 内部桥;升级后 diff 注册表看新增 |
复习题
- 标准库为什么不需要 import?math.sqrt(2.0) 在生成的 C 里变成什么(提示:一条声明一次直调)?
- "XRT 优先"原则的含义是什么?math 为什么是 CRT 直通的典型例外?
- random.seed 需要几个参数?同参数两次 seed 后 u32 序列有什么性质?直接 print random.u32() 会遇到什么问题、怎么绕行?
- log 的默认级别是什么?setLevel(log.warnLevel) 之后 info/warn 各自的命运?
- tcc.run 与 tcc.compile 的区别是什么?tcc.run 的返回值来自哪里?
编程练习
写一个脚本:读一段硬编码文本,输出它的 byteLen、base64 编码、hex 编码、u32/u64 双哈希(记得 int 转换)、以及 gzip 压缩后的长度(import 第 35 章的扩展库)。要求每个结果用 log.info 输出,最后用 setLevel(log.errorLevel) 验证全部静默。
参考思路:36.4/36.5/36.6 的实测片段直接组合;gzip.compress 接 string 重载;压缩长度用 Buffer.len()。
用 random 生成 100 个 range(1, 100) 的整数放进 array,用 math 族的 min/max 和手写循环求均值,再用 json.stringify 把 {"count", "min", "max", "mean"} 四个字段输出。同一 seed 跑两次验证结果完全一致,然后去掉 seed 跑一次观察差异。
参考思路:均值用 int 累加后 / 100.0 转 float;json 的 dict 用 dict 字面量构造;复现性是 36.4 的核心卖点。
用 tcc.run 执行一段 C:从命令行硬编码两个整数做四则运算并 printf 结果(除法检查除零),C main 返回 0/1 表示成功失败;xlang 侧根据 rc 打印 "C side ok" 或 "C side failed"。进阶:把计算改成 tcc.compile 产出 calc.exe 并用 os.exec 运行它。
参考思路:printf 格式串里的 \n 用四层转义或改用 puts;os.exec 的用法查注册表 {"os", "exec", ...} 一行(36.10 方法实战)。