是否有任何编译器和库中strcmp()返回的值不是-1 0和1?

时间:2020-01-16 22:59:47

标签: c strcmp

尽管常识和literature对于strcmp()的行为很清楚:

int strcmp( const char *lhs, const char *rhs );

如果lhs按字典顺序出现在rhs之前,则为负值。

如果lhsrhs比较相等,则为零。

如果按字典顺序lhs出现在rhs之后,则为正值。

我似乎无法使其返回-101以外的任何值。

可以肯定的是,该行为与定义一致,但是我期望值大于或小于1-1,因为定义断言结果将是<00>0,而不是-101

我在几个编译器和库中对此进行了测试,结果相同。我想看看不是这样的例子。

sample code

#include <stdio.h> 
#include <string.h> 

  
int main() 
{  
   printf("%d ", strcmp("a", "a"));
   printf("%d ", strcmp("abc", "aaioioa"));
   printf("%d ", strcmp("eer", "tsdf"));
   printf("%d ", strcmp("cdac", "cdac"));
   printf("%d ", strcmp("zsdvfgh", "ertgthhgj"));
   printf("%d ", strcmp("abcdfg", "rthyuk"));
   printf("%d ", strcmp("ze34", "ze34"));
   printf("%d ", strcmp("er45\n", "io\nioa"));
   printf("%d", strcmp("jhgjgh", "cdgffd"));
}

Result: 0 1 -1 0 1 -1 0 -1 1

4 个答案:

答案 0 :(得分:4)

规范说数字必须为负数,零或正数,但是并不能锁定必要的确切值。该库本身可能以更特定的方式运行。

该规范意味着这样的代码在技术上是无效的:

if (strcmp(a, b) == 1)

这可能“在我的机器上工作”,但不是使用其他库的其他人。

您应该写的是:

if (strcmp(a, b) > 0)

这就是所有的意思:期望值不是1 / -1,而是相应地进行编码。

答案 1 :(得分:4)

C标准明确指出(C11 §7.24.4.2 The strcmp function):

strcmp函数返回一个大于,等于或小于零的整数,因此s1指向的字符串大于,等于或小于s2指向的字符串。

它没有说结果必须大于或小于零。始终返回-10+1的函数符合标准;有时返回大于1的值的函数也是如此,例如-270+35。如果您的代码符合C标准,则不得假设任何一组结果;只能假设结果的符号正确。

这里是strcmp()的实现-在这里命名为str_cmp(),因此可以将结果与strcmp()进行比较-不返回-1+1

#include <string.h>
#include <stdio.h>

static int str_cmp(const char *s1, const char *s2)
{
    while (*s1 == *s2 && *s1 != '\0')
        s1++, s2++;
    int c1 = (int)(unsigned char)*s1;
    int c2 = (int)(unsigned char)*s2;
    return (c1 - c2);
}

int main(void) 
{  
   printf("%d ", strcmp("a", "a"));
   printf("%d ", strcmp("abc", "aAioioa"));
   printf("%d\n", strcmp("eer", "tsdf"));

   printf("%d ", str_cmp("a", "a"));
   printf("%d ", str_cmp("abc", "aAioioa"));
   printf("%d\n", str_cmp("eer", "tsdf"));
   return 0;
}

在Mac(macOS Mojave 10.14.6; GCC 9.2.0; Xcode 11.13.1)上运行时,我得到了输出:

0 1 -1
0 33 -15

我确实更改了您的数据-"aaioioa"变成了"aAioioa"。总体结果没有什么不同(但是值33大于原始字符串所得到的值)—返回值根据需要小于,等于或大于零。

str_cmp()函数是合法的实现,并且宽松地基于历史上常见的strcmp()的实现。它在返回值上稍加注意,但是您可以在p106上找到Brian W Kernighan和Dennis M Ritchie的两个较小变体。 The C Programming Language, 2nd Edn(1988)-一种使用数组索引,另一种使用指针:

int strcmp(char *s, char *t)
{
    int i;
    for (i = 0; s[i] == t[i]; i++)
        if (s[i] == '\0')
            return 0;
    return s[i] - t[i];
}

int strcmp(char *s, char *t)
{
    for ( ; *s == *t; s++, t++)
        if (*s == '\0')
            return 0;
    return *s - *t;
}

如果对普通的char类型进行了签名,并且其中一个字符串包含“重音字符”,则这些字符可能不会返回预期结果,该字符的范围是-128 .. -1(或0x80 ..视为无符号值时为0xFF)。我的str_cmp()代码中的强制类型转换将数据视为unsigned char(通过强制类型转换);由于分配的原因,(int)强制转换实际上并不是必需的。将两个unsigned char值相减后转换为int会产生-255 .. +255范围内的结果。但是,现代版本的C库不会像直接返回-10+1那样使用直接减法。

请注意,C11标准§7.24.4 String comparison functions表示:

比较函数memcmpstrcmpstrncmp返回的非零值的符号由第一对字符值之间的差的符号(两者都解释为unsigned char),只是比较对象不同。

您可以查看How do I check if a value matches a string?。那里的轮廓显示:

if (strcmp(first, second) == 0)    // first equal to second
if (strcmp(first, second) <= 0)    // first less than or equal to second
if (strcmp(first, second) <  0)    // first less than second
if (strcmp(first, second) >= 0)    // first greater than or equal to second
if (strcmp(first, second) >  0)    // first greater than second
if (strcmp(first, second) != 0)    // first unequal to second

请注意,与零进行比较的方式与您进行的测试如何使用相同的比较运算符。

您可以(但可能不应该)写:

if (strcmp(first, second) <= -1)    // first less than second
if (strcmp(first, second) >= +1)    // first greater than second

您仍然会得到相同的结果,但是这样做并不明智。始终将零进行比较会更容易,更统一。

您可以使用以下方法获得-1、0,+ 1的结果:

unsigned char c1 = *s1;
unsigned char c2 = *s2;
return (c1 > c2) - (c1 < c2);

对于不受限制的整数(而不是限制为0 .. 255的整数),这是安全的,因为它避免了整数溢出,而减法给出了错误的结果。对于包含8位字符的受限整数,减法溢出不是问题。

答案 2 :(得分:2)

请重新阅读此位

如果lhs以字典顺序出现在rhs之前,则为负值。

-1是否足以使该陈述成立?

如果lhs和rhs比较相等,则为零。

如果lhs按字典顺序在rhs之后出现,则为正值。

1是否足以使该陈述成立?

因此示例代码按照规范运行。

编辑

只需测试返回值是否为零,小于零或大于零。按照规范,这应该在所有实现中都有效。

编辑2

我认为这将满足规格-尚未测试:-(

 for (size_t i = 0; s1[i] && s2[i] &&s1[i] == s2[i]; ++i) {
     // Empty
   }
   return s2[i] - s1[i]; // This may be the wrong way around

这将返回1,-1或0以外的值。

答案 3 :(得分:1)

以下是带有strcmp()实现的C库的一些示例,这些实现并不总是返回-10+1

仿生库具有基于BSD的strcmp()实现:

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

Dietlibc 执行相同的操作。如果为WANT_SMALL_STRING_ROUTINES配置,它甚至是不一致的版本:

int
strcmp (const char *s1, const char *s2)
{
#ifdef WANT_SMALL_STRING_ROUTINES
    while (*s1 && *s1 == *s2)
        s1++, s2++;
    return (*s1 - *s2);
#else
    // a more advanced, conforming implementation that tests multiple characters
    // at a time but still return the difference of characters as unsigned bytes
#endif
}

Glibc 在其strcmp目录中具有generic的此实现,用于特殊的体系结构:

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;
}

Musl C库的实现非常紧凑:

int strcmp(const char *l, const char *r)
{
    for (; *l==*r && *l; l++, r++);
    return *(unsigned char *)l - *(unsigned char *)r;
}

newlib 具有以下实现:

int
_DEFUN (strcmp, (s1, s2),
    _CONST char *s1 _AND
    _CONST char *s2)
{
#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__)
  while (*s1 != '\0' && *s1 == *s2)
    {
      s1++;
      s2++;
    }

  return (*(unsigned char *) s1) - (*(unsigned char *) s2);
#else
  // a more advanced approach, testing 4 bytes at a time, still returning the difference of bytes
#endif
}

许多替代C库似乎遵循相同的模式,并返回与规范匹配的字节差。但是您测试的实现似乎始终返回-10+1。不要依赖这个在将来的版本中,甚至在使用不同编译标志的同一系统中,它都可能会更改。