c/test/builtin_dup_api.c · 2907 字节 · 下载原文
/* builtin_dup_api.c —— `builtins.c` 那批"影子副本"统一之后的判据。
* builtin_dup_api.c — criterion for the unified "shadow copies" in `builtins.c`.
*
* ## 背景
* ## Background
*
* `strlen` / `gcd` / `factorial` / `lcm` / `is_prime` / `memcmp` / `memcpy` / `memset` / `sign`
* 在本仓**各有两份实现**(`builtins.c` 一份、"专名模块"一份),而**两份并不相同**:
* 有的这边多一层保护、有的那边多一层保护。链接器对重名裸标签**只取一份** ⇒
* 「哪一份的保护生效」全看链接顺序。
*
* 2026-10-08 的处置是**统一**(不是删一份):把两处都写成**较好的那一份**,无论取哪份都一样。
*
* ## 判据
* ## Criteria
*
* 挑的是**能分辨"取的是哪一份"**的取值:
* · `strlen(0)` —— 只有带空指针保护的那份会返回 0(另一份直接解引用 ⇒ 崩)
* · `gcd(-4, 6)` —— 只有带负数处理的那份得 2(另一份得负数)
* · `factorial(20)` —— 只有带溢出保护的那份得 0(13! 已超 32 位;另一份给溢出的垃圾)
*
* 跑法(桌面,秒级):
* dotnet scripts/vmlcli/bin/Release/net10.0/vmlcli.dll third_party/vml/Examples/c/test/builtin_dup_api.c
*/
int strlen(const char* s);
int gcd(int a, int b);
int factorial(int n);
int lcm(int a, int b);
int is_prime(int n);
int fails = 0;
void ck(char* name, int got, int want) {
if (got == want) {
printf("OK %s = %d\n", name, got);
} else {
printf("FAIL %s: got %d, want %d\n", name, got, want);
fails = fails + 1;
}
}
int main(void) {
/* ① 空指针保护:只有那一份会返回 0(另一份解引用 0 ⇒ 崩)。
* ⚠ 实参必须是**运行时才知道**的值 —— 写 `strlen(0)` 会被前端**常量折叠**掉,
* 于是这一条无论链接到哪一份都是 0(我第一版就是这么写的,反证时它没响)。 */
char* np = 0;
np = np + 0;
ck("strlen(null) 有保护", strlen(np), 0);
ck("strlen(\"abc\")", strlen("abc"), 3);
/* ② 负数处理:`gcd(-4, 6)` 应为 2 */
int na = 0 - 4;
int nb = 6;
ck("gcd(-4,6)", gcd(na, nb), 2);
ck("gcd(12,18)", gcd(12, 18), 6);
/* ③ 溢出保护:13! 已超 32 位 ⇒ 返回 0,而不是一个溢出后的垃圾 */
int big = 20;
ck("factorial(20) 溢出保护", factorial(big), 0);
ck("factorial(5)", factorial(5), 120);
/* ④ lcm:任一为 0 ⇒ 0(不靠 gcd 的返回值去判) */
ck("lcm(4,6)", lcm(4, 6), 12);
ck("lcm(0,5)", lcm(0, 5), 0);
/* ⑤ is_prime */
ck("is_prime(97)", is_prime(97), 1);
ck("is_prime(91)", is_prime(91), 0);
printf("BUILTIN_DUP %s (fails=%d)\n", fails == 0 ? "PASS" : "FAIL", fails);
return fails;
}