是否有任何编译器和库的 strcmp() returns 值不是 -1 0 和 1?
Is there any compiler and library where strcmp() returns values other than -1 0 and 1?
虽然常识和literature对strcmp()
的行为很清楚:
int strcmp( const char *lhs, const char *rhs );
Negative value if lhs
appears before rhs
in lexicographical order.
Zero if lhs
and rhs
compare equal.
Positive value if lhs
appears after rhs
in lexicographical order.
我似乎无法 return 除了 -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
规范说数字必须是负数、零或正数,但它没有锁定必要的确切值。库本身可能以更具体的方式运行。
规范意味着像这样的代码在技术上是无效的:
if (strcmp(a, b) == 1)
这可能 "work on my machine" 但不是其他使用不同库的人的。
你应该写的地方是:
if (strcmp(a, b) > 0)
这就是它的全部含义:期望 值不只是 1/-1 并相应地编码。
请重新阅读这篇文章
Negative value if lhs appears before rhs in lexicographical order.
-1 是否足以证明此陈述为真?
Zero if lhs and rhs compare equal.
Positive value if lhs appears after rhs in lexicographical order.
1 足以证明此陈述为真吗?
所以示例代码按照规范运行。
编辑
只需测试 return 值是否为零、小于零或大于零。根据规范,这应该适用于所有实现。
编辑 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
这将 return 除 1、-1 或 0 之外的值。
C标准明确说了(C11 §7.24.4.2 The strcmp
function):
The strcmp function returns an integer greater than, equal to, or less than zero, accordingly as the string pointed to by s1 is greater than, equal to, or less than the string pointed to by s2.
它没有说明结果必须比零大多少或小多少;总是 returns -1
, 0
或 +1
符合标准的函数;有时 return 的值的幅度大于 1
的函数也是如此,例如 -27
、0
、+35
。如果您的代码要符合 C 标准,则它不得假定任何一组结果;它可能只假设结果的符号是正确的。
这里是 strcmp()
的实现——在这里命名为 str_cmp()
以便结果可以与 strcmp()
进行比较——它不 return -1
或 +1
:
#include <string.h>
#include <stdio.h>
static int str_cmp(const char *s1, const char *s2)
{
while (*s1 == *s2 && *s1 != '[=10=]')
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 比原始字符串要大)— return 值根据需要小于、等于或大于零。
str_cmp()
函数是一个合法的实现,大致基于 strcmp()
历史上常见的实现。它在 return 值上稍微小心一些,但您可以在 Brian W Kernighan 和 Dennis M Ritchie 的 p106 上找到它的两个小变体
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] == '[=12=]')
return 0;
return s[i] - t[i];
}
int strcmp(char *s, char *t)
{
for ( ; *s == *t; s++, t++)
if (*s == '[=12=]')
return 0;
return *s - *t;
}
如果普通 char
类型被签名并且其中一个字符串包含 'accented characters',则 K&R 代码可能不会 return 预期的结果 -128 .. -1(或 0x80 .. 0xFF,当被视为无符号值时)。我的 str_cmp()
代码中的转换将数据视为 unsigned char
(通过转换);由于分配, (int)
演员并不是真正必要的。将转换为 int
的两个 unsigned char
值相减会产生 -255
.. +255
范围内的结果。但是,现代版本的 C 库不会像 return 那样直接使用 -1
、0
或 +1
.
注意 C11 标准 §7.24.4 String comparison functions 说:
The sign of a nonzero value returned by the comparison functions memcmp
, strcmp
, and strncmp
is determined by the sign of the difference between the values of the first pair of characters (both interpreted as unsigned char
) that differ in the objects being compared.
你可以看看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 位字符的受限整数,减法溢出不是问题。
这里有一些具有 strcmp()
实现的 C 库示例,它们并不总是 return -1
、0
或 +1
:
Bionic libc 有一个基于 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 在它的 generic
目录中有这个 strcmp
的实现,用于异国情调的架构:
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 == '[=12=]')
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 != '[=14=]' && *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 库似乎都遵循相同的模式和 return 字节的差异,这符合规范。但是您测试的实施似乎始终如一 return -1
、0
或 +1
。不要依赖这个。它可能会在未来的版本中发生变化,甚至在使用不同编译标志的同一系统中也会发生变化。
虽然常识和literature对strcmp()
的行为很清楚:
int strcmp( const char *lhs, const char *rhs );
Negative value if
lhs
appears beforerhs
in lexicographical order.Zero if
lhs
andrhs
compare equal.Positive value if
lhs
appears afterrhs
in lexicographical order.
我似乎无法 return 除了 -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
规范说数字必须是负数、零或正数,但它没有锁定必要的确切值。库本身可能以更具体的方式运行。
规范意味着像这样的代码在技术上是无效的:
if (strcmp(a, b) == 1)
这可能 "work on my machine" 但不是其他使用不同库的人的。
你应该写的地方是:
if (strcmp(a, b) > 0)
这就是它的全部含义:期望 值不只是 1/-1 并相应地编码。
请重新阅读这篇文章
Negative value if lhs appears before rhs in lexicographical order.
-1 是否足以证明此陈述为真?
Zero if lhs and rhs compare equal.
Positive value if lhs appears after rhs in lexicographical order.
1 足以证明此陈述为真吗?
所以示例代码按照规范运行。
编辑
只需测试 return 值是否为零、小于零或大于零。根据规范,这应该适用于所有实现。
编辑 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
这将 return 除 1、-1 或 0 之外的值。
C标准明确说了(C11 §7.24.4.2 The strcmp
function):
The strcmp function returns an integer greater than, equal to, or less than zero, accordingly as the string pointed to by s1 is greater than, equal to, or less than the string pointed to by s2.
它没有说明结果必须比零大多少或小多少;总是 returns -1
, 0
或 +1
符合标准的函数;有时 return 的值的幅度大于 1
的函数也是如此,例如 -27
、0
、+35
。如果您的代码要符合 C 标准,则它不得假定任何一组结果;它可能只假设结果的符号是正确的。
这里是 strcmp()
的实现——在这里命名为 str_cmp()
以便结果可以与 strcmp()
进行比较——它不 return -1
或 +1
:
#include <string.h>
#include <stdio.h>
static int str_cmp(const char *s1, const char *s2)
{
while (*s1 == *s2 && *s1 != '[=10=]')
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 比原始字符串要大)— return 值根据需要小于、等于或大于零。
str_cmp()
函数是一个合法的实现,大致基于 strcmp()
历史上常见的实现。它在 return 值上稍微小心一些,但您可以在 Brian W Kernighan 和 Dennis M Ritchie 的 p106 上找到它的两个小变体
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] == '[=12=]')
return 0;
return s[i] - t[i];
}
int strcmp(char *s, char *t)
{
for ( ; *s == *t; s++, t++)
if (*s == '[=12=]')
return 0;
return *s - *t;
}
如果普通 char
类型被签名并且其中一个字符串包含 'accented characters',则 K&R 代码可能不会 return 预期的结果 -128 .. -1(或 0x80 .. 0xFF,当被视为无符号值时)。我的 str_cmp()
代码中的转换将数据视为 unsigned char
(通过转换);由于分配, (int)
演员并不是真正必要的。将转换为 int
的两个 unsigned char
值相减会产生 -255
.. +255
范围内的结果。但是,现代版本的 C 库不会像 return 那样直接使用 -1
、0
或 +1
.
注意 C11 标准 §7.24.4 String comparison functions 说:
The sign of a nonzero value returned by the comparison functions
memcmp
,strcmp
, andstrncmp
is determined by the sign of the difference between the values of the first pair of characters (both interpreted asunsigned char
) that differ in the objects being compared.
你可以看看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 位字符的受限整数,减法溢出不是问题。
这里有一些具有 strcmp()
实现的 C 库示例,它们并不总是 return -1
、0
或 +1
:
Bionic libc 有一个基于 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 在它的 generic
目录中有这个 strcmp
的实现,用于异国情调的架构:
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 == '[=12=]')
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 != '[=14=]' && *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 库似乎都遵循相同的模式和 return 字节的差异,这符合规范。但是您测试的实施似乎始终如一 return -1
、0
或 +1
。不要依赖这个。它可能会在未来的版本中发生变化,甚至在使用不同编译标志的同一系统中也会发生变化。