编程语言研究书库

附录 — 创建库、Unicode 与 UTF-8、日期格式符

附录 — 创建库、Unicode 与 UTF-8、日期格式符

这一章讲什么: 三个附录打包:附录 A 教你亲手造库(第 3 章 起一直在用 libspl.a,这里是它的出生证明);附录 B 把 Unicode 和 UTF-8 一次讲透——为什么全世界的文字能在 Linux 文件里共存;附录 C 是 strftime 格式符的速查表。

1. 附录 A:创建库

静态库:ar rcs 六步

  1. 写头文件(放原型);2. gcc -c 编译出 .o;3. ar rcs libutils.a timestuff.o errors.o——r=插入、 c=无存档则建、s=建索引;4. 装到 ~/lib、头文件到 ~/include(最贴近的祖先把原则);5. LIBRARY_PATH 加库目录;6. CPATH 加头文件目录。使用: gcc -o myprog myprog.c -lutils(-l 要跟在引用它的文件 后面)1。

三个坑,原书都给了警示框:① 静态库和共享库别放同一目录 ——gcc 默认优先链共享库,同名时你的静态库会被无声忽略; ② cannot find -lutils = LIBRARY_PATH 没配对(export 了吗?); ③ 别用 gcc -static——它要求所有库(包括 libc)都有 静态版,而现代系统根本不带 libc.a2。

共享库:三重名字 + 六步

共享库有三个名字,libacl 的真实目录布局就是教科书:

  libacl.so        → 软链 →  libacl.so.1       ← linker name(编译器用)
  libacl.so.1      → 软链 →  libacl.so.1.1.0   ← soname(接口版本号)
  libacl.so.1.1.0                     真实文件   ← real name(带 minor.release)

主版本号变 = 接口变了;修 bug 只动 minor/release3。建造六步: ① gcc -fPIC -c(位置无关代码——别用 -fpic,不保证通用) ② gcc -shared -Wl,-soname,libgoodstuff.so.1 -o libgoodstuff.so.1.0.1 stuff.o tools.o(-Wl 把选项透传给链接器,逗号两侧不留空白) ③ 拷到 ~/lib;④ ldconfig -n ~/lib 自动建 soname→real 的 软链;⑤ 手动 ln -s 建 linker name;⑥ 升级时放新 release 再跑 ldconfig,软链自动指到最新4。

用共享库:两步链接 + 运行时找库

链接期照常 -L~/lib -lgoodstuff;运行期动态链接器要能找到 文件——ldd myprog 验货,看到 libgoodstuff.so.1 => not found 就是找不到。三条路:LD_LIBRARY_PATH=~/lib(bashrc 配置, 最省事但不被推荐)、-rpath 编译期烧进路径、 LD_RUN_PATH=~/lib(ld 的等价物)5。

2. 附录 B:Unicode 与 UTF-8

码位 ≠ 字节

历史线:ASCII(7 位,94 可打印 + 33 控制 + 空格)→ IBM 的 EBCDIC(另一套 8 位映射)→ 各家民族编码互相撞车。1989 年 ISO 启动 UCS(ISO 10646),1991 年与 Unicode 联盟合并推进—— 码位(code point)只是分配给字符的数值(U+03C6 是「ϕ」的 号,不是两个字节),码位→字节序列的映射才叫编码 (encoding form),再加分端序(字节序)才是完整的编码方案 (encoding scheme)6。

UCS-2/UCS-4 直接按数值写字节,会产出含 \0 和 / 的序列—— Unix 文件系统和 C 库全都完蛋。UTF-8(Ken Thompson 1992 年 发明)救场:变长 1–6 字节,规则四条7:

  1. U+0000–U+007F = 单字节 0x00–0x7F——纯 ASCII 文件就是 合法的 UTF-8 文件,这是它统治 Unix 的原因;
  2. 非 ASCII 字符 = 首字节 0xC0–0xFD(几个 1 跟几个后续字节: 110xxxxx 表 1 个跟随,1110xxxx 表 2 个)+ 跟随字节一律 10xxxxxx(0x80–0xBF);
  3. 编码模板表:0xxxxxxx / 110xxxxx 10xxxxxx / 1110xxxx 10xxxxxx 10xxxxxx / ……(1 到 6 字节);
  4. 0xFE/0xFF 永不出现——自同步 + 不含 /。

手算一例:U+05E7(0080–07FF 区间 → 2 字节)二进制 0000 0101 1110 0111:右 6 位 100111 填第二字节(前缀 10) → 10 100111;余 5 位 10111 填首字节(前缀 110)→ 110 10111——两字节序列 11010111 10100111(0xD7 0xA7)8。 码位到字节数的区间表(Unicode 16):1 字节 ≤7F;2 字节 ≤07FF; 3 字节 ≤FFFF(BMP 全部);4 字节到 1FFFFF(实际只到 10FFFF, UTF-8 最多 4 字节)9。

3. 附录 C:日期时间格式符速查

strftime/strptime 的 % 格式符全表,示例基准取 2038-01-19 03:14:07 UTC(刻意选 32 位 time_t 溢出瞬间, 附录 C 的冷幽默)。常考的十个:%a 缩写星期(Mon)、%A 全名、 %b/%B 月名、%c locale 日期时间、%d 月内日、%D = %m/%d/%y、%F = %Y-%m-%d、%H 24 小时、%I 12 小时、 %j 年积日、%m 月、%M 分、%S 秒、%Y 四位年、%Z 时区名、%% 百分号。完整表见原书附录 C,locale 改变时 %a/%A/%b/%B/%c 的输出随之变(第 3 章 spl_date 的实验基座)10。

4. 判断与边界

判断: 「别用 -fpic/-static」两条禁令都是踩坑经验的凝结; UTF-8 的「ASCII 兼容」作为它胜出的主因,论据硬47。 边界: soname 机制在 macOS 上是 install_name、Windows 是 DLL 名字规则——跨平台要重查;UTF-8 的四字节上限与实际 码位空间 10FFFF 的对应、以及 surrogate 代理对的 UTF-16 细节,附录点到为止;附录 C 只管 strftime 家族,date(1) 的 GNU 扩展格式另有一套。可带走的: ar rcs 六步;静态/共享 分目录;三重命名 + ldconfig;LD_LIBRARY_PATH/ldd 排障; 码位≠字节;UTF-8 四条规则 + 区间表 + 手算流程;十大格式符。

5. 原文地图

主题原书章原文位置
静态库六步与 LIBRARY_PATHAppendix A: Creating Librariestext/26-apx-a-appendix-a-creating-libraries.txt:132(搜「ar rcs」) · :175(搜「LIBRARY_PATH」)
同名共享库优先的坑同上:220(搜「shared library of the same name」)
-static 陷阱同上:246(搜「cannot find -lc」)
三重命名同上:262(搜「soname」) · :287(搜「libacl.so」)
共享库六步同上:302(搜「-fPIC」) · :319(搜「-Wl,-soname」) · :338(搜「ldconfig」)
ldd/LD_LIBRARY_PATH同上:403(搜「ldd」) · :425(搜「LD_LIBRARY_PATH」)
码位与编码形式Appendix B: Unicode and UTF-8text/27-apx-b-appendix-b-unicode-and-utf-8.txt:100(搜「numeric values」) · :111(搜「UCS-2」)
UTF-8 四规则同上:126(搜「scheme」) · :137(搜「110xxxxx」) · :155(搜「0xFE」)
区间表与手算同上:168(搜「Table B-1」) · :193(搜「U+05E7」)
格式符示例基准Appendix C: Date and Time Format Specifierstext/28-apx-c-appendix-c-date-and-time-format-specifiers.txt:5(搜「2038」)

Footnotes

  1. 出处:「Appendix A: Creating Libraries」第 118–193 段(text/26-apx-a-appendix-a-creating-libraries.txt:132,搜「ar rcs」) · :175(搜「LIBRARY_PATH」)。 ↩

  2. 出处:「Appendix A: Creating Libraries」第 192–253 段(text/26-apx-a-appendix-a-creating-libraries.txt:220,搜「shared library of the same name」) · :246(搜「cannot find -lc」)。 ↩

  3. 出处:「Appendix A: Creating Libraries」第 261–295 段(text/26-apx-a-appendix-a-creating-libraries.txt:262,搜「soname」) · :287(搜「libacl.so」)。 ↩

  4. 出处:「Appendix A: Creating Libraries」第 297–375 段(text/26-apx-a-appendix-a-creating-libraries.txt:302,搜「-fPIC」) · :319(搜「-Wl,-soname」) · :338(搜「ldconfig」)。 ↩ ↩2

  5. 出处:「Appendix A: Creating Libraries」第 376–446 段(text/26-apx-a-appendix-a-creating-libraries.txt:403,搜「ldd」) · :425(搜「LD_LIBRARY_PATH」)。 ↩

  6. 出处:「Appendix B: Unicode and UTF-8」第 100–120 段(text/27-apx-b-appendix-b-unicode-and-utf-8.txt:100,搜「numeric values」) · :111(搜「UCS-2」)。 ↩

  7. 出处:「Appendix B: Unicode and UTF-8」第 121–155 段(text/27-apx-b-appendix-b-unicode-and-utf-8.txt:126,搜「scheme」) · :155(搜「0xFE」)。 ↩ ↩2

  8. 出处:「Appendix B: Unicode and UTF-8」第 181–209 段(text/27-apx-b-appendix-b-unicode-and-utf-8.txt:193,搜「U+05E7」)。 ↩

  9. 出处:「Appendix B: Unicode and UTF-8」第 156–180 段(text/27-apx-b-appendix-b-unicode-and-utf-8.txt:168,搜「Table B-1」)。 ↩

  10. 出处:「Appendix C: Date and Time Format Specifiers」第 1–40 段(text/28-apx-c-appendix-c-date-and-time-format-specifiers.txt:5,搜「2038」)。 ↩

On this page