V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
henix
V2EX  ›  C

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

  •  
  •   henix · 1 天前 · 1157 次点击

    以下来自我跟 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  
       23 小时 58 分钟前
    确实巧妙,AI 辅助编程是趋势,让 AI 收集优秀的算法,比人快。
    kaedeair
        2
    kaedeair  
       23 小时 35 分钟前
    开 O1 优化在 clang 下第一种方案和 ai 给出的方案得到的汇编是一致的,但是 gcc 就不行
    Shatyuka
        3
    Shatyuka  
       23 小时 30 分钟前
    arielherself
        4
    arielherself  
       23 小时 28 分钟前
    在我看来之所以一般不会学到这种写法,是因为平时写程序没有到苛求比较函数性能的地步。而且楼上也说了编译器优化可以处理好这种问题。

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


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

    我又找了一下,只在 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  
       15 小时 11 分钟前
    实际用在排序函数里就会优化成一样的了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3279 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 43ms · UTC 04:59 · PVG 12:59 · LAX 21:59 · JFK 00:59
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.