strcmp 输出之谜 - strcmp 实际上是如何比较字符串的?

Mystery with strcmp output - How strcmp actually compares the strings?

我想知道为什么 strcmp() returns 在同一个函数中多次使用不同的值。下面是程序。第一种情况我知道它为什么打印 -6。但是在第二种情况下,为什么会打印-1呢?

#include<stdio.h>
#include<string.h>
int main()
{
    char a[10] = "aa";
    char b[10] = "ag";
    printf("%d\n",strcmp(a, b));
    printf("%d\n",strcmp("aa","ag"));
    return 0;
}

它产生的输出如下

[sxxxx@bhlingxxx test]$ gcc -Wall t51.c
[sxxxx@bhlingxxx test]$ ./a.out
    -6
    -1

为什么第二个strcmp()的输出是-1?在这里玩的是Compiler吗?如果是这样,它的具体优化是什么?

来自 https://linux.die.net/man/3/strcmp

The strcmp() function compares the two strings s1 and s2. It returns an integer less than, equal to, or greater than zero if s1 is found, respectively, to be less than, to match, or be greater than s2.

strcmp 函数只承诺 return 上面给出的比较的负值。未指定要 returned 的实际值。

可能发生的情况是,对于 strcmp("aa","ag"),编译器知道结果为负,并将其优化为 -1

C standard 表示以下关于 strcmp 的 return 值:

第 7.24.4.2p3 节:

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

因此,只要结果符合该描述,它就符合 C 标准。这意味着编译器可以执行优化以符合该定义。

如果我们看汇编代码:

.loc 1 7 0
leaq    -32(%rbp), %rdx
leaq    -48(%rbp), %rax
movq    %rdx, %rsi
movq    %rax, %rdi
call    strcmp
movl    %eax, %esi
movl    $.LC0, %edi
movl    [=10=], %eax
call    printf
.loc 1 8 0
movl    $-1, %esi      # result of strcmp is precomputed!
movl    $.LC0, %edi
movl    [=10=], %eax
call    printf

在第一种情况下,数组被传递给 strcmp 以调用 strcmp 并生成对 printf 的调用。然而,在第二种情况下,字符串常量被传递给两者。编译器看到这一点并自行生成结果,优化对 strcmp 的实际调用,并将硬编码值 -1 传递给 printf.

C 标准对 strcmp 的唯一保证是 return 值的符号将指示不等式的方向(如果存在 1,或者如果字符串正好为零)等于。

虽然 return 首先区分 char 的数值之间的差异是一种相当常见的实现方式,但这不是必需的。如果编译器可以查看字符串常量并立即知道 strcmp 的结果是什么,它可能会在其位置添加平面 -110而不是通过实际调用函数的努力。

解决这个问题的方法是不要编写依赖于 strcmp 的特定实现的代码,无论它有多常见。只相信 return 值的符号。