【问题标题】:Optimized strcmp implementation优化的 strcmp 实现
【发布时间】:2013-11-29 01:05:44
【问题描述】:

这个函数被发现here。这是strcmp的实现:

int strcmp(const char* s1, const char* s2)
{
    while (*s1 && (*s1 == *s2))
        s1++, s2++;
    return *(const unsigned char*)s1 - *(const unsigned char*)s2;
}

除了最后一行,我都明白了,总之最后一行发生了什么?

【问题讨论】:

  • 这个实现没有任何“优化”。

标签: c string-comparison strcmp


【解决方案1】:
return *(const unsigned char*)s1-*(const unsigned char*)s2;

OP:简而言之,最后一行发生了什么?

A:比较第一个潜在的字符串差异。根据规范的要求,chars 都被引用为 unsigned char。将 2 提升为 int 并返回差值。


注意事项:

1 返回值的符号(0)是最有意义的部分。它是 C 规范中唯一指定的部分。

2 在某些系统上,charsigned(更常见)。在其他人身上,charunsigned。定义最后一个比较的“符号”可以促进可移植性。注意fgetc() 获取的字符为unsigned char

3 除了字符串以\0 结尾之外,所采用的字符编码(如 ASCII - 最常见)在二进制级别上没有任何区别。如果在 2 个字符串中不同的第一个 chars 具有值 65 和 97,则第一个字符串将小于第二个字符串,即使字符编码是非 ASCII 也是如此。 OTOH,strcmp("A", "a") 将在字符编码为 ASCII 时返回一个负数,但可能在不同的字符编码中返回一个正数,因为它们的底层值和顺序不是由 C 定义的。

【讨论】:

    【解决方案2】:

    这个实现绝对不是对内置strcmp的优化,它只是另一种实现,我相信它的性能很可能会比内置版本更差。

    如果被比较的值相等,比较函数应该返回 0,如果第一个值较小,则返回任何负数,如果第一个值较大,则返回任何正数。这就是最后一行发生的事情。

    最后一行的想法是将字符转换为无符号字符,我相信作者的意思是在标准字符(ASCII 代码 0-127)之后对非标准字符进行排序。

    编辑:代码中没有错误,如果s1 指向的值小于s2 指向的值,代码中没有错误,它可以并且将返回负值,在代码128 及以上的字符之前排序标准字符。

    【讨论】:

    • 是的,我想知道为什么演员会在那里。那么strcmp的真正实现是什么呢?
    • @el.pescado 转换为 int 将在计算值后发生。该值已经下溢 0。
    • 真正的实现是here,但大多数架构实际上用更好、更具体的版本(如this)覆盖了它。
    • 我不能订阅这个答案。 ideone.com/crYyX7我误会了什么?
    • @CharlieBurns 我明白你的意思,我已经编辑了我的答案。我仍然不确定这是如何工作的。
    【解决方案3】:

    我更喜欢这段代码:

    int strcmp(const char *str1, const char *str2)
    {
        int s1;
        int s2;
        do {
            s1 = *str1++;
            s2 = *str2++;
            if (s1 == 0)
                break;
        } while (s1 == s2);
        return (s1 < s2) ? -1 : (s1 > s2);
    }
    

    对于 ARMv4,它编译为:

    strcmp:
        ldrsb   r3, [r0], #1 ;r3 = *r0++
        ldrsb   r2, [r1], #1 ;r2 = *r1++
        cmp     r3, #0       ;compare r3 and 0
        beq     @1           ;if r3 == 0 goto @1
        cmp     r3, r2       ;compare r3 and r2
        beq     strcmp       ;if r3 == r2 goto strcmp
    ;loop is ended
    @1:
        cmp     r3, r2     ;compare r3 and r2
        blt     @2         ;if r3 < r2 goto @2
        movgt   r0, #1     ;if r3 > r2 r0 = 1
        movle   r0, #0     ;if r3 <= r2 r0 = 0
        bx      lr         ;return r0
    @2:
        mov     r0, #-1    ;r0 = -1
        bx      lr         ;return r0
    

    如您所见,循环下只有 6 条指令 + 最后最多 5 条指令。所以复杂度是 6 * (strlen+1) + 5。

    将 (s1 == 0) 移动到 while 条件会导致 ARM 的机器代码更差(我不知道为什么)。

    【讨论】:

    • 您应该将字符转换为无符号字符:s1 = (unsigned char)*str1++; 以实现strcmp() 的确切语义。
    【解决方案4】:

    这个实现可以进一步优化,减少一些比较:

    int strcmp(const char *s1, const char *s2) {
        unsigned char c1, c2;
        while ((c1 = *s1++) == (c2 = *s2++)) {
            if (c1 == '\0')
                return 0;
        }
        return c1 - c2;
    }
    

    如果字符串与终止空字节相同,则返回值为0。返回值的符号是第一个不同字符之间的差异,按照 C 标准转换为 unsigned char

      1234563 987654328@ 并且保证此差异适合int 类型的范围。
    • sizeof(int) == 1 的系统上,返回值应该这样计算:

      return (c1 < c2) ? -1 : 1;
      

    【讨论】:

      【解决方案5】:

      strcmp 返回哪个字符串大于另一个,而不仅仅是它们是否相等。

      最后一行减去第一个不匹配的字符,看看哪个更大。如果整个字符串匹配,那么它将是0-0=0,它给出“相等”的结果。

      这个实现并没有得到很好的优化,因为这需要汇编代码和缓存行、加载大小等方面的知识。

      【讨论】:

        猜你喜欢
        • 2016-04-24
        • 1970-01-01
        • 2012-11-12
        • 2014-06-30
        • 1970-01-01
        • 1970-01-01
        • 2018-12-31
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多