尽管常识和literature对于strcmp()
的行为很清楚:
int strcmp( const char *lhs, const char *rhs );
如果
lhs
按字典顺序出现在rhs
之前,则为负值。如果
lhs
和rhs
比较相等,则为零。如果按字典顺序
lhs
出现在rhs
之后,则为正值。
我似乎无法使其返回-1
,0
和1
以外的任何值。
可以肯定的是,该行为与定义一致,但是我期望值大于或小于1
或-1
,因为定义断言结果将是<0
,0
或>0
,而不是-1
,0
或1
。
我在几个编译器和库中对此进行了测试,结果相同。我想看看不是这样的例子。
#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
答案 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指向的字符串。
它没有说结果必须大于或小于零。始终返回-1
,0
或+1
的函数符合标准;有时返回大于1
的值的函数也是如此,例如-27
,0
,+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库不会像直接返回-1
,0
或+1
那样使用直接减法。
请注意,C11标准§7.24.4 String comparison functions表示:
比较函数
memcmp
,strcmp
和strncmp
返回的非零值的符号由第一对字符值之间的差的符号(两者都解释为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库的一些示例,这些实现并不总是返回-1
,0
或+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库似乎遵循相同的模式,并返回与规范匹配的字节差。但是您测试的实现似乎始终返回-1
,0
或+1
。不要依赖这个在将来的版本中,甚至在使用不同编译标志的同一系统中,它都可能会更改。