【问题标题】:Can I please get some feedback for this strcmp() function I implemented in C?我可以为我在 C 中实现的这个 strcmp() 函数获得一些反馈吗?
【发布时间】:2010-10-24 12:17:04
【问题描述】:

我正在学习 C。

当我尝试一些事情并从该语言的知名程序员那里收到反馈时,我发现我学习编程很好。

我决定编写自己的strcmp() 函数,只是因为我认为我可以:)

int strcompare(char *a, char *b) {
    while (*a == *b && *a != '\0') {
        a++;
        b++;
    }
    return *a - *b;
}

我试图通过在while 的条件下增加指针来使其工作,但无法弄清楚如何执行return。我打算使用 C 风格 代码,尽可能多地在一行上做 :)

我能从成熟的 C 程序员那里得到一些反馈吗?这段代码可以改进吗?我有什么坏习惯吗?

谢谢。

【问题讨论】:

  • “尽可能在一条线上做”当然是一种风格,但不应该被鼓励!
  • 对你的代码有一个评论,不过:你应该将函数参数设为const char *
  • @Oli Charlesworth 我同意,但从阅读 K&R 来看,它看起来像是一个 C 习语。也许这是我已经养成的一个坏习惯!
  • @Alex:是的,这确实是一个相当普遍的习惯用法,尤其是在遗留代码中。人们对此有不同的看法,但恕我直言,做while(*a++ == *b++);之类的事情几乎没有任何借口!
  • @Chris:请停止宣传 NULL 应该被视为函数的有效参数的荒谬想法。是的,如果将 NULL 传递给函数,它应该具有未定义的行为,就像传递 (char *)1 或任何其他无意义的指针一样。

标签: c strcmp


【解决方案1】:

如果你想在while语句中做所有事情,你可以写

while (*a != '\0' && *a++ == *b++) {}

我个人并不是这种编程风格的忠实拥护者 - 无论如何,读者在试图理解它时需要在心理上“解包”操作顺序(并确定代码是否有错误)。内存错误在 C 语言中尤其隐蔽,在 C 中覆盖内存超过或超过一个字节可能会导致各种莫名其妙的崩溃或错误,而不是最初的原因。

现代风格的 C 编程强调正确性、一致性和纪律性,而不是简洁性。简洁的表达式功能,如前增量和后增量操作,最初是让编译器生成更好的机器代码的一种方式,但现在优化器可以轻松地自己做到这一点。

正如@sbi 所写,我更喜欢const char * 参数而不是普通的char * 参数。

【讨论】:

  • 谢谢道格!我真的想过这样做。我绝对理解您必须 unpack 操作顺序的意思。 +1
  • 我很惊讶你想出了一个可以工作的循环条件版本,但即使在看了一段时间之后,我也不会打赌它在所有条件下都能正常工作。所以我完全同意你的出色分析,+1 来自我。
【解决方案2】:
  1. 该函数不会改变ab的内容。它可能应该通过指向const 字符串的指针来宣布这一点。
  2. 大多数 C 风格比许多其他语言的风格简洁得多,但不要试图太聪明。 (在您的代码中,循环条件中有几个条件与,我认为没有办法在其中添加增量,所以这甚至不是样式问题,而是正确性问题。)

【讨论】:

  • strcmp 只需返回大于、等于或小于零。
  • @Oli:啊,好吧,我错了。 (我是一名 C++ 开发人员。)已修复。
  • 谢谢你 - 正是我想要的。你能否详细说明一下 1. 我试过ba 开头(我认为你的意思)但它仍然有效。 3. 是一个很好的观点,但我只是忘了这样做:)
  • @alex:我原来的四个点中有两个没有实际意义(一个是我自己发现的,一个是 Oli 发现的),所以我删除了它们。那一定是在您在评论中提出该问题之前的片刻。我想您对#3 的引用指的是现在的#1,const 问题。是的,这经常被遗忘,这很可悲。使用const 严格允许编译器找到你犯的错误,这比运行时发生的错误更受欢迎,因为任何导致编译失败的错误都没有机会出现在你客户的机器上。跨度>
【解决方案3】:

我不知道什么时候尽可能多地被认为是 C 风格......我宁愿将(混淆)Perl 与它联系起来......

请不要这样做。最好的办法是每行一个命令。当您尝试调试代码时,您会明白为什么:)

对于你的实现:对我来说似乎很好,但我会设置 *b 也不是 '\0' 的条件,因为你不能知道 a 总是大于 b...否则你会冒险正在读取未分配的内存...

【讨论】:

  • b 的长度大于a(在我的测试中)时,它似乎仍然有效。我是否依赖未定义的行为?
  • 据我所知,是的...毕竟你不知道缓冲区有多大,除非你也传递它们的长度,剩下的唯一方法就是寻找 \0在所有字符串中,否则结果可能是垃圾......
  • 您只需要在一个字符串中测试零,因为如果在另一个字符串中遇到零,它将是不匹配并停止迭代。
【解决方案4】:

您可能会发现这很有趣,来自eglibc-2.11.1。它与您自己的实现没有太大区别。

/* Compare 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
strcmp (p1, p2)
     const char *p1;
     const char *p2;
{
  register const unsigned char *s1 = (const unsigned char *) p1;
  register const unsigned char *s2 = (const unsigned char *) p2;
  unsigned reg_char c1, c2;

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

  return c1 - c2;
}

【讨论】:

  • 哇,太复杂了。这看起来像是为 80 年代不知道如何优化的编译器编写的。如果你要做一些花哨的事情,至少要让它有用(例如一次比较整个单词)。
【解决方案5】:

一个非常微妙的错误:strcmp 比较解释为unsigned char 的字节,但您的函数将它们解释为char(在大多数实现上签名)。这将导致非 ascii 字符在 ascii 之前而不是之后排序。

【讨论】:

    【解决方案6】:

    如果由于整数溢出,(insigned) char 的限制等于或大于 int 的限制,则此函数将失败。

    例如,如果您在 DSP 上编译它,它有 16 位字符,限制为 0...65536 和 16 位整数,限制为 -32768...32767,那么如果您尝试比较字符串,例如 "/uA640" 和 "A" 结果会是负数,这是不正确的。

    这是一个奇异而奇怪的问题,但在您编写通用实现时会出现。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多