实测:`extern "C"` 头配 C++ 实现时,报错是 `undefined reference to 'calc'`,而 `nm` 显示请求侧是 `U calc`、供给侧是 `T _Z4calci`;反过来 C++ 调用 C 实现时 ld 默认把名字反修饰成 `undefined reference to 'calc(int)'`——**好看的报错反而藏住了你要 grep 的符号名**,加 `-Wl,--no-demangle` 才还原成 `_Z4calci`。`-Wl,-y,符号` 在本机**对 .o 和归档两种来源都没有输出**, 能用的是 `-Wl,--trace`(它按顺序列出 crt2.o / crtbegin.o / … / libstdc++.dll.a / libmingw32.a / libgcc_s.a / libmsvcrt.a / libkernel32.a / crtend.o)。运行时"找不到 DLL"和"找不到导出函数" 在 Git Bash 与 cmd 下都报 **127**,用看门狗 `GetExitCodeProcess` 才拿到真值: 前者 `0xC0000135`(3221225781),后者同样是非 0,而全局构造里空指针写是 `0xC0000005`(3221225477)。
实测:这台机器上有三个 make 但没有一个叫 `make`(`mingw32-make.exe` 是 GNU Make 4.2.1 x86_64, `C:\Strawberry\c\bin\gmake.exe` 是 4.1 **32 位**);同一行 `OBJS = $(wildcard *.c:.c=.o)` 让前者**零输出退出 127**、让后者抛出 `Interrupt/Exception caught (code = 0xc0000005)` 退出 255; 没设 `SHELL` 且 PATH 只剩 System32 时,`rm -rf` 变成 `process_begin: CreateProcess(NULL, rm -rf build, ...) failed. make (e=2)`;改一个被 3 个 .c 包含的头文件,手写依赖的 Makefile 重建 **0 个** .o,加 `-MMD -MP` 后重建 3 个;同一份 Makefile, `-j1` 三次全绿、`-j8` 五次全红;201 个文件全量 34.1 s、`-j8` 13.0 s、只改一个文件 2.4 s、 什么都不改 1.7 s。
实测:两个 .o 共 1488 字节,打进 `libab.a` 是 1698 字节(容器+索引 210 字节);`main.o liba.a libb.a` 链接成功,把两个库顺序调换成 `main.o libb.a liba.a` 就报 `liba.a(a.o):a.c:(.text+0x9): undefined reference to 'b_fn'`,同一个归档里则两种顺序都成功; `--start-group` 能救回来。两个成员都定义 `b_fn` 时,只用得到一个符号的场景**静默取第一个**(退出码证明), 强制两个都进来才报 `multiple definition`。一个只导出一个函数的 DLL 是 36293 字节,配套导入库 `libfoo.dll.a` 只有 1496 字节;`--version-script` 在 PE 上被**完全忽略**(`local: add3` 之后导出表还是 1 条,退出码 0); exe 同目录的 DLL 赢过 PATH 里的同名 DLL,两份都没有时 Git Bash 报 127。
实测:26 行源码预处理后变 2012 行/114685 字节(180 倍);`-O2` 把 600 个函数从 62205 字节的 .o 压成 1480 字节(42 倍),可同一份小文件的 .o 反而比 -O0 大 29%——因为段从 7 个变成 10 个;`-ffinite-math-only` 把运行时的 `isnan()` 折成恒 0,而 `-Ofast` 走的是另一条路:它先把 `x/x` 简化成常量 1,NaN 根本不再产生。;`.txt` 后缀会被当成链接输入并以退出码 0 静默产出零个文件。