strcmp函数返回的奇怪值

7

在检查strcmp函数的返回值时,我发现gcc存在一些奇怪的行为。下面是我的代码:

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

char str0[] = "hello world!";
char str1[] = "Hello world!";

int main() {
    printf("%d\n", strcmp("hello world!", "Hello world!"));
    printf("%d\n", strcmp(str0, str1));
}

当我使用clang编译时,两次调用strcmp函数均返回32。但是,当我使用gcc编译时,第一次调用返回1,第二次调用返回32。我不明白为什么在使用gcc编译时,第一次和第二次调用strcmp会返回不同的值。
以下是我的测试环境:
  • Ubuntu 18.04 64位
  • gcc 7.3.0
  • clang 6.0.0

4
这里有什么“奇怪”的地方? - melpomene
3
这个标准只规定了结果的符号,没有规定大小。如果字符串相等,则结果为零;如果第一个字符串排在第二个字符串之前,则结果为负数;如果第二个字符串排在第一个字符串之后,则结果为正数。在您的例子中,1和32都是正数;就该标准而言,它们的结果是相同的,因此您的代码应该写成对这些差异不敏感。 - Jonathan Leffler
顺便提一下,'h''H'之间的差异是32。巧合吗? - Christian Gibbons
不要关注返回的值本身,只需要关注它是否为负数、零或正数。如果您坚持想了解具体细节,请尝试检查所使用库的源代码(gnu libc: https://www.gnu.org/software/libc/)。 - pmg
1
谢谢回复,但我已经查看了man页面和ISO文档,所以我知道确切的返回值未指定。我只想知道为什么在GCC中字面字符串和数组字符串之间会有差异。 - fips197
2
很可能是重复的问题:在将字符串作为指针或字面值传递时,strcmp()返回值不一致……简而言之,两种方法都是有效的,你看到的是优化的效果。 - Shafik Yaghmour
4个回答

9

看起来你没有启用优化(例如-O2)。

从我的测试结果来看,即使在没有优化的情况下(-O0),gcc也始终能够识别具有常量参数的strcmp并进行优化。而Clang需要至少-O1才能这样做。

这就是差异所在:由clang生成的代码调用了两次strcmp,但由gcc生成的代码在第一种情况下只执行printf(“%d \ n”,1),因为它知道'h' > 'H'(按ASCII码比较)。这实际上只是常量折叠。

示例:https://godbolt.org/z/8Hg-gI

正如其他答案所解释的那样,任何正数都可以表示第一个字符串大于第二个字符串,因此编译器优化程序只选择1strcmp库函数显然使用不同的值。


3
虽然让Clang和GCC编译程序时能够产生相同输出或不同输出的结果很有趣,但我不喜欢将其解释为优化或缺乏优化是导致输出差异的原因。更好的做法是将其概括为“实现细节”,因为优化只是导致结果可能不同的原因之一,无论是在特定情况下还是(更多地)在一般情况下。 - John Bollinger
您还可以使用-fbo-builtin来观察其中一些效果。 - Shafik Yaghmour

6

标准定义了strcmp的结果。如果lhs在词典序中出现在rhs之前,则结果为负数,如果它们相等,则为零,否则为正数,表示lhs在词典序中出现在rhs后面。

如何实现以及返回什么完全取决于实现方式。在程序中不能依赖特定的值,否则它们将不具备可移植性。只需使用比较符(<、>、==)进行检查即可。

请参见https://en.cppreference.com/w/c/string/byte/strcmp

背景

一种简单的实现可能只需计算每个字符c1 - c2的差异,并一直执行此操作,直到结果不为零或其中一个字符串结束。结果将是第一个字符的数字差异,在这个字符中两个字符串不同。

例如,这是GLibC的实现:https://sourceware.org/git/?p=glibc.git;a=blob_plain;f=string/strcmp.c;hb=HEAD


5

strcmp函数只会返回大于零、零或小于零的值,但没有规定这些正值和负值应该是什么。


3

strcmp返回的确切值,如果字符串不相等,则未指定。根据man页

#include <string.h>
int strcmp(const char *s1, const char *s2);
int strncmp(const char *s1, const char *s2, size_t n);

The strcmp() and strncmp() functions return an integer less than, equal to, or greater than zero if s1 (or the first n bytes thereof) is found, respectively, to be less than, to match, or be greater than s2.

自从str1str2更大,因此该值必须为正数,在这两种情况下都是如此。
至于两个编译器之间的区别,似乎clang返回对应不匹配字符的ASCII值之间的差异,而gcc则选择简单的-1、0或1。两者都是有效的,因此您的代码只需要检查值是否为0、大于0或小于0即可。

1
有趣的一点是,当传入字符串文字时,gcc只会返回1。我怀疑它可能是一种优化,因为它知道结果总是相同的。 - Christian Gibbons

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