分享一个用 AI 学习 C 语言的例子 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
henix
V2EX    C

分享一个用 AI 学习 C 语言的例子

  •  
  •   henix 202 天前 2947 次点击
    这是一个创建于 202 天前的主题,其中的信息可能已经有所发展或是发生改变。

    以下来自我跟 DeepSeek 的对话:

    我的提问

    我要为 C 语言的 qsort 编写一个比较函数,比较两个 uint64_t ,考虑以下两种实现:

    int qcmp_uint64(const void* pa, const void* pb) { uint64_t a = *(uint64_t*)pa; uint64_t b = *(uint64_t*)pb; if (a < b) return -1; if (a > b) return 1; return 0; }  
    int qcmp_uint64_2(const void* pa, const void* pb) { uint64_t a = *(uint64_t*)pa; uint64_t b = *(uint64_t*)pb; return a - b; } 

    从直觉上看,似乎第二种实现效率更高,但由于整数相减可能溢出,我对第二种方案的正确性产生了怀疑

    第二种方案是否正确?如何高效实现这一比较函数?

    DeepSeek 回答

    对于第二种实现 qcmp_uint64_2,直接返回 a - b 的确存在隐患。原因如下:

    1. 无符号整数减法不会溢出,但结果始终为非负数。

      • 当 a < b 时,a - b 会得到一个极大的无符号值(模运算结果),例如 1 - 2 = UINT64_MAX 。
    2. 转换为 int 可能导致符号错误:

      • 若 a - b 的值超过 INT_MAX ,转换为 int 时会触发实现定义行为(通常为截断高位,导致结果为负数)。此时 qsort 会误判 a < b ,造成排序错误。

    例如:

    • 若 a = 1, b = 2 ,则 a - b = UINT64_MAX 。
    • 转换为 int 时,假设 int 为 32 位,结果为 -1 (正确)。
    • 但若 a = 0x80000000, b = 0 ,在 32 位 int 下,转换后为负数(错误)。

    高效且安全的实现 利用 比较操作的布尔值隐式转换为 0/1 ,可写出无分支的高效代码:

    int qcmp_uint64(const void* pa, const void* pb) { uint64_t a = *(const uint64_t*)pa; uint64_t b = *(const uint64_t*)pb; return (a > b) - (a < b); // 若 a > b 则 1-0=1 ; a < b 则 0-1=-1 ;相等则为 0 } 

    优点:

    • 完全避免溢出风险:不依赖数值运算,仅用比较结果。
    • 无分支:现代编译器(如 GCC/Clang )会将其优化为无跳转指令,性能与 a - b 相近。
    • 代码简洁:符合严格比较逻辑。

    性能验证

    在 x86-64 架构下,上述代码的典型汇编输出为:

    mov rax, [rdi] cmp [rsi], rax setb al seta cl movzx eax, al movzx ecx, cl sub eax, ecx ret 

    完全无分支,效率与减法方案相当,且绝对安全。

    结论:不要使用 a - b 方案,优先选择无分支的布尔比较实现。

    点评

    这个 (a > b) - (a < b) 的写法太巧妙了!

    感觉这类惯用法( idiom )很难通过看书或网络学习,正是 AI 的强项

    只搜到一个相关的: https://stackoverflow.com/questions/3886446/problem-trying-to-use-the-c-qsort-function

    11 条回复    2025-04-24 21:48:44 +08:00
    jinsongzhaocn
        1
    jinsongzhaocn  
       202 天前
    确实巧妙,AI 辅助编程是趋势,让 AI 收集优秀的算法,比人快。
    kaedeair
        2
    kaedeair  
       202 天前
    开 O1 优化在 clang 下第一种方案和 ai 给出的方案得到的汇编是一致的,但是 gcc 就不行
    Shatyuka
        3
    Shatyuka  
       202 天前
    arielherself
        4
    arielherself  
       202 天前
    在我看来之所以一般不会学到这种写法,是因为平时写程序没有到苛求比较函数性能的地步。而且楼上也说了编译器优化可以处理好这种问题。

    1. 如果目的是学习 C 语言,则完全没必要知道这个;
    2. 如果目的是编写高性能的程序,可能会先去检查程序的性能瓶颈,然后调查不同排序算法之间的性能差异,最后才看到这里可以优化的地方;
    3. 如果是对各种 trick 感兴趣,也许多泡一泡各种社区会有帮助。
    AFOX
        5
    AFOX  
       201 天前
    本质上只是把这一段优化成了一行代码:
    if (a < b) return -1;
    if (a > b) return 1;
    return 0;
    bbao
        6
    bbao  
       201 天前   3
    就 up 这个例子而言:
    1 ,所谓的性能,运行无数次也不会造成性能瓶颈,也不会额外提升多少性能指标;无意义优化
    2 , [return (a > b) - (a < b); // 若 a > b 则 1-0=1 ; a < b 则 0-1=-1 ;相等则为 0 ] 就这段逻辑而言,如果不加注释,可读性没有第一种来的直观,而且非常规写法,不影响性能的情况下,「可读性」为第一考虑要义


    总结:不建议
    MoYi123
        7
    MoYi123  
       201 天前
    @bbao 别的地方也就算了, 排序里的比较函数也能没性能瓶颈吗?
    bbao
        8
    bbao  
       201 天前
    @MoYi123 你压力测试一下楼主这第一个函数和优化后的结果函数看看结果呢?
    craftsmanship
        9
    craftsmanship  
       201 天前 via Android   1
    楼主掉坑里去了 当年刷 LC 的时候我也喜欢总结这种看上去很聪明的奇技淫巧 然而在实际工程里面这么写 绝对会被打死 像楼上各位所言 毫无意义 会喜欢这种写法只能说明 缺乏阅读足够量的优秀代码
    henix
        10
    henix  
    OP
       201 天前
    我本来是想发出来看看有没有人能发散一下这种写法的历史,或者在其他知名项目中看到过之类的……因为这么底层(常用)的,而且又很漂亮的写法,我之前竟然完全不知道……

    我又找了一下,只在 stackoverflow 上找到两个帖子提到:

    * https://stackoverflow.com/questions/49663753/qsort-function-compare-confused-me
    * https://stackoverflow.com/questions/3886446/problem-trying-to-use-the-c-qsort-function

    外国人都说是“common idiom”,说明这种写法确实是一种惯用法,但他们没有指出是谁最先这么用。如果是惯用法,个人认为并不会对可读性造成较大伤害

    而且我观察到 gcc 和 msvc 这两种编译器默认都无法将我的第一个比较函数优化成第三种,只有 clang 可以,可见这种写法确实能产生更优的编译结果,我认为还是很有意义的

    P.S. 发散一下:上面的第一个链接指出如果是比较两个 int 也不能直接相减,而且这种直接相减的写法出自《 The C Programming Language 》第五章
    MrKrabs
        11
    MrKrabs  
       201 天前
    实际用在排序函数里就会优化成一样的了
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5329 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 34ms UTC 07:46 PVG 15:46 LAX 23:46 JFK 02:46
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86