为什么 FreeBSD 的 memchr 实现会在其条件下增加其指针?

Why does FreeBSD's implementation of memchr increment its pointer in its condition?

FreeBSD's generic implementation of memchr 是:

void *
memchr(const void *s, int c, size_t n)
{
    if (n != 0) {
        const unsigned char *p = s;

        do {
            if (*p++ == (unsigned char)c)
                return ((void *)(p - 1));
        } while (--n != 0);
    }
    return (NULL);
}

这对我来说似乎不必要地复杂;初始 n != 0 检查 do-while 只是为了避免 p 声明似乎完全没有意义。但是,我特别感兴趣的是循环体为什么会这样:

if (*p++ == (unsigned char)c)
    return ((void *)(p - 1));

而不是更直接的:

if (*p == (unsigned char)c)
    return ((void *) p);
++p;

使用条件内联 post 增量对某些 compiler/platform 有优化好处吗?

首先:这纯属猜测。我没有写过这段代码,也无法验证我的猜测。

这两个版本的代码之间存在一个非常重要的语义差异:

// Version A
if (*p++ == (unsigned char)c)
    return ((void *)(p - 1));

// Version B
if (*p == (unsigned char)c)
    return ((void *) p);
++p;

在版本 A 中,增量排在 if 的代码块之前,而在版本 B 中,它排在该块之后。

因此在版本 A 中,增量代码将被放置在可能从 if 生成的分支指令之前。至少我们可以在 IMO 假设编写代码时(1988 年?)的编译器将 C 代码相对直接地转换为汇编代码。

Does inlining the post-increment with the condition have some optimization benefit for some compiler/platform?

在分支之前增加增量允许在其分支指令具有 delay slot 的体系结构上进行相对简单的优化:您可以将增量移动到该延迟槽而不是在那里有一个 NOP。

所以版本 A 比版本 B 每次循环迭代需要一条指令,代价是函数 returns 时单次递减。这是一个(微)优化。

指针post增量的情况是为了让那个时代的编译器(例如PCC)在DEC机器中使用自动增量寻址模式(就像Mark Plotnick在评论中提到的那样)。

由于所有DEC机器都支持自增寻址,这种编码循环的方式曾经很普遍(顺便说一句,m68k支持同样的优化)。

另一方面,do-while 循环仅仅是因为在一个简单的编译器上它往往会产生更好的代码,而不仅仅是为了避免设置 p.

都不应该对现代编译器有任何影响。