有人可以解释为什么这个功能是这样的吗?

时间:2013-12-15 08:28:52

标签: c gnu strncmp

我正在查看GNU C库中的函数glibc-2.18,这是我为strncmp.c找到的代码。

看着它我不明白为什么这样写。这个循环是否展开? 为什么不使用5或10而不是4?为什么他们这样写而不是使用更简单的方法?

/* Compare no more than N characters of S1 and S2,
   returning less than, equal to or greater than zero
   if S1 is lexicographically less than, equal to or
   greater than S2.  */
int
STRNCMP (const char *s1, const char *s2, size_t n)
{
  unsigned char c1 = '\0';
  unsigned char c2 = '\0';

  if (n >= 4)
    {
      size_t n4 = n >> 2;
      do
    {
      c1 = (unsigned char) *s1++;
      c2 = (unsigned char) *s2++;
      if (c1 == '\0' || c1 != c2)
        return c1 - c2;
      c1 = (unsigned char) *s1++;
      c2 = (unsigned char) *s2++;
      if (c1 == '\0' || c1 != c2)
        return c1 - c2;
      c1 = (unsigned char) *s1++;
      c2 = (unsigned char) *s2++;
      if (c1 == '\0' || c1 != c2)
        return c1 - c2;
      c1 = (unsigned char) *s1++;
      c2 = (unsigned char) *s2++;
      if (c1 == '\0' || c1 != c2)
        return c1 - c2;
    } while (--n4 > 0);
      n &= 3;
    }

  while (n > 0)
    {
      c1 = (unsigned char) *s1++;
      c2 = (unsigned char) *s2++;
      if (c1 == '\0' || c1 != c2)
    return c1 - c2;
      n--;
    }

  return c1 - c2;
}

有人可以解释代码背后的逻辑吗?

感谢。

3 个答案:

答案 0 :(得分:2)

是的,它是部分展开的循环。

4:

1)一般尺寸:在更多分支(小数量)和更大代码尺寸(大数字)之间总是需要权衡。

2)特定大小(4,而不是5):正如Joachim Isaksson在循环之前指出>>,如果选择5而不是4,则需要将其更改为分区。他说某些(例如嵌入式)CPU变得重要。 (通常我们只考虑循环成本,而不是它的设置,但他可能是正确的,因为大多数字符串往往很小!)

请注意,如果您想将循环展开为任意数字(无论是否为2的幂),那么您可以 - 而不是进行除法并维护n4 - 您可以计算s1的结束指针,并在最后检查s1与它的对比:

char* s1end = s1 + n;
do {...} while(s1 < s1end);

答案 1 :(得分:1)

这种方式有更少的分支(if语句),它允许编译器优化管道衬里。

答案 2 :(得分:1)

是的,这是循环展开。

为什么4而不是5或10.可能是因为这是为了缓存友好并确保调用与内存页/行对齐。

问题可能是为什么不是8,16或32.也许是代码大小,也许更容易为一些旧处理器生成优化的程序集,当它为4时。或者实际代码太大而无法适应典型的指令缓存。