C语言中strcmp()函数的返回值只有这一个吗?

4
我正在学习C语言,目前正在学习字符串处理。在我的学习材料中,strcmp()被定义为:

这是一个比较两个字符串是否相同或不同的函数。两个字符串逐个字符进行比较,直到出现不匹配或其中一个字符串结束,以先出现的情况为准。如果两个字符串相同,则strcmp()返回值为零。如果它们不同,则返回第一组不匹配字符的ASCII值之间的数字差。

提供了一个示例程序,这就是我的问题所在-

main( )
{
    char string1[ ] = "Jerry" ;
    char string2[ ] = "Ferry" ;
    int i, j, k ;
    i = strcmp ( string1, "Jerry" ) ;
    j = strcmp ( string1, string2 ) ;
    k = strcmp ( string1, "Jerry boy" ) ;
    printf ( "\n%d %d %d", i, j, k ) ;
}

我在我的Windows(64位)机器上使用Dev-C++运行了此程序,并获得了以下输出结果:0 1 -1 现在,该书给出的输出结果为0 4 -32,并给出了以下解释:
在第一次调用strcmp()时,两个字符串是相同的——“Jerry”和“Jerry”,strcmp()返回的值为零。在第二次调用中,“Jerry”的第一个字符与“Ferry”的第一个字符不匹配,结果为4,这是ASCII值‘J’和‘F’之间的数字差值。在第三次调用strcmp()中,“Jerry”与“Jerry boy”不匹配,因为“Jerry”的末尾空字符与“Jerry boy”中的空格不匹配。返回的值为-32,这是空字符的值减去空格的ASCII值,即‘\0’减去‘ ’,等于-32。
为了确认该书的说法,我添加了这段代码到我的程序中,只是为了验证J和F之间的ASCII差异:
printf("\n Ascii value of J is %d", 'J' );
printf("\n Ascii value of F is %d", 'F' );

然后我相应地在输出中得到了这个 -
 Ascii value of J is 74
 Ascii value of F is 70

这是根据书上所说的,但是你可以看到,当字符串不匹配时,我得到了不同的j和k值。我查找了一些类似的SO问题,并且得到了其中一些,但无法得到关于不同输出(当它返回 1和-1 时)的明确答案,因此我决定提出新问题。
这个here问题似乎有些相似,问题描述包含有关strcmp()的以下信息-

如果找到s1(或其前n个字节)分别小于、等于或大于s2,则strcmp()和strncmp()函数返回小于、等于或大于零的整数

在其中一个答案中,我遇到了this link,其中记录了strcmp()的功能。它进一步说道-
strcmp()函数将比较s1指向的字符串和s2指向的字符串。非零返回值的符号将由两个字符串中不同的第一对字节(均解释为unsigned char类型)的值之差的符号确定。完成后,如果s1指向的字符串大于、等于或小于s2指向的字符串,则strcmp()将返回大于、等于或小于0的整数。因此,阅读所有这些内容后,我倾向于认为无论使用哪种实现/平台,都应该仅将strcmp()函数的返回值视为三个类别之一(0、正数和负数),而不是依赖于确切的返回值。我的理解是否正确?

2
阅读完所有内容后,我倾向于认为0、1或-1是strcmp()的唯一可能结果。你是如何得出这个结论的?请再次阅读你引用自POSIX的段落,这不是规定的内容。 - ouah
你从哪里得到了“ascii码差异”的定义?我从未见过除了“如果相等则返回0,如果第一个字符串小于第二个字符串则返回负值,如果第一个字符串大于第二个字符串则返回正值”之外的任何东西。请参阅:http://en.wikibooks.org/wiki/C_Programming/C_Reference/string.h/strcmp - Jan Spurny
“非零返回值的符号应通过……确定”这个词组意味着结果的符号与……之间的差异的符号相同,而不是结果必须为-1、0或1。 - Pascal Cuoq
3
你的学习书籍在这里做了错误的假设:“它返回ASCII值之间的数字差异”。你可以向出版商提交错误报告。具体细节请参考此特定主题线程 :-) - Jongware
1
那么,就所有实际目的而言,我应该关注从strcmp()返回的整数的零和/或符号,对吧?比如说密码检查器的实现。如果返回零,则密码匹配等等。当比较两个字符串时,返回的整数的符号应该决定哪个字符串比另一个更大/更多? - Manish Giri
显示剩余5条评论
4个回答

4

下面是来自苹果libc的C语言strcmp()的简单实现:

int
strcmp(const char *s1, const char *s2)
{
    for ( ; *s1 == *s2; s1++, s2++)
        if (*s1 == '\0')
            return 0;
    return ((*(unsigned char *)s1 < *(unsigned char *)s2) ? -1 : +1);
}

FreeBSD的libc实现:

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

以下是 GNU libc 的实现,它返回字符之间的差异:
int
strcmp (p1, p2)
     const char *p1;
     const char *p2;
{
  const unsigned char *s1 = (const unsigned char *) p1;
  const unsigned char *s2 = (const unsigned char *) p2;
  unsigned char c1, c2;

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

  return c1 - c2;
}

这就是为什么我读到的大多数比较都是用< 0== 0> 0来写的,如果不需要知道字符串中字符之间的确切差异。

在苹果的开源库中,它是相同的:http://opensource.apple.com/source/Libc/Libc-262/ppc/gen/strcmp.c - phuclv
我认为上面的 unsigned 是错误的,应该删除它的两个出现。在某些系统中,char 可能是有符号的,而在其他系统中则不是。 - Basile Starynkevitch
你的最后一段话有误导性,因为它似乎暗示所有实现都会返回-1、0或+1,这是不正确的。 - interjay
1
@BasileStarynkevitch 标准规定在比较字符串时将字符解释为“无符号字符”。 - interjay
是的,抱歉,我错了! - Basile Starynkevitch
@interjay 我已经说过,我看到过一些实现返回这三个值而不是差异。无论如何,我已经更新了GNU libc实现,它返回不同字符之间的差异。 - denisvm

3

C语言规范是一份用英文写成的文件

标准化委员会的成员精心选择他们的措辞,以允许实现者进行自己的实现选择。

在某些硬件(或实现)上,返回任何整数(遵守规范的约束条件)可能比仅返回-1、0、1更快(或更简单、代码更小)(如dvm's answer中提出的函数)。顺便说一下,musl-libc's strcmp.c更短,并且可以返回-1、0、1之外的整数;但它符合标准。

顺便提一下,使用GCCGNU libc(例如在大多数Linux系统上),strcmp函数可能会被处理为一个编译器builtin- __builtin_strcmp,尤其是在优化时...有时它可以被一些非常有效的代码替换。

尝试编译以下函数(在文件abc.c中)

#include <string.h>
int isabc(const char*s) { return strcmp(s, "abc"); }

启用优化并查看汇编代码。在我的Debian/Sid/x86-64上,使用GCC 4.9.1编译gcc -fverbose-asm -S -O2 abc.c,我在生成的abc.s中看不到任何函数调用(但isabc可能返回除-1、0、1之外的其他数字)。
你应该关注可移植性代码,因此不应期望特定值(只要您的供应商的strcmp遵守其不精确和模糊的规范)。
还应阅读未定义行为,这是一个相关的想法:语言规范故意不精确,以允许各种实现者做出不同的实现选择。

3
完成后,strcmp()函数将返回一个整数,如果由s1指向的字符串大于、等于或小于由s2指向的字符串,则该整数大于、等于或小于0。因为实际返回整数的值没有指定,只有它的符号,所以经过阅读,我倾向于认为0、1或-1是strcmp()函数可能出现的唯一结果。

我不确定你在这个声明中的意思是什么:“为什么?实际返回的整数值没有指定,只有它的符号。”你能详细解释一下吗?另外,为什么我会得到我得到的输出? - Manish Giri
@DarkKnight,你不理解其中的什么吗?你得出了与第一段相反的结论。它说返回值必须是正数、负数或0。它并没有说如果是负数就必须是-1,如果是正数就必须是+1。 - The Paramagnetic Croissant
好的,我理解这段话的意思了。但是我不明白我的输出背后的逻辑。为什么对于第二和第三种情况我得到了+1和-1? - Manish Giri
@DarkKnight 我不知道具体的实现细节。它是有效的和符合标准的,这就是最重要的。 - The Paramagnetic Croissant
1
这个“答案”并没有回答问题,它只是表明OP不知道答案。 - anatolyg
@anatolyg 什么?怎么回事? - The Paramagnetic Croissant

1

0, 1, -1 就像是 标准 值; 然而你应该将它们看作: 零、正数、负数

在这种情况下,它们的意义是:

  • (0) 意味着字符串相等。
  • 负数 (-1 或其它任何负数) 意味着第一个字符串比第二个字符串
  • 正数 (1 或其它任何正数) 意味着第一个字符串比第二个字符串

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接