strcmp()的模糊行为

strcmp()的模糊行为,c,string,strcmp,C,String,Strcmp,请注意,我已经检查了本标题的相关问题,但从我的角度来看,这些问题与本问题无关 起初,我认为program1和program2会给我相同的结果 //Program 1 char *a = "abcd"; char *b = "efgh"; printf("%d", strcmp(a,b)); //Output: -4 我能发现的唯一区别是,在program2中,我传递了字符串文字,而在program中,我传递了char*作为strcmp()函数的参数 为什么这些看似相同的程序的行为之间存

请注意,我已经检查了本标题的相关问题,但从我的角度来看,这些问题与本问题无关

起初,我认为program1和program2会给我相同的结果

//Program 1

char *a = "abcd";
char *b = "efgh";
printf("%d", strcmp(a,b));


//Output: -4

我能发现的唯一区别是,在program2中,我传递了字符串文字,而在program中,我传递了
char*
作为
strcmp()
函数的参数

为什么这些看似相同的程序的行为之间存在差异

平台:LinuxMint 编译程序:g++


编辑:实际上,程序1总是打印第一个不匹配字符的ascii码差,但是如果string2中第一个不匹配字符的ascii码大于string1的ascii码,程序2则打印-1,反之亦然。

对于这些调用,
strcmp
返回两个不同的值,这确实令人惊讶,但它并不与C标准不兼容:

strcmp()。-4和-1都是负值

正如其他人所指出的,为不同调用生成的代码是不同的:

  • 编译器在第一个程序中生成对库函数的调用
  • 编译器能够确定比较结果,并为第二种情况生成显式结果
    -1
    ,其中两个参数都是字符串文本
为了执行此编译时评估,
strcmp
必须在
中以子文件的方式定义,以便编译器可以确定程序引用的是C库的实现,而不是行为可能不同的替代方案。在最近的GNU libc include文件中跟踪相应的原型有点困难,因为许多嵌套的宏最终导致隐藏的原型

请注意,gcc和clang的最新版本将在这两种情况下执行优化,可以在上进行测试,但这两个版本都不会将此优化与
printf
的优化相结合,以生成更紧凑的代码
put(“-1”)。它们似乎只针对不带参数的字符串文字格式将
printf
转换为
put

这是您的C代码:

int x1()
{
  char *a = "abcd";
  char *b = "efgh";
  printf("%d", strcmp(a,b));
}

int x2()
{
  printf("%d", strcmp("abcd", "efgh"));
}
这是两个函数生成的汇编输出:

.LC0:
        .string "abcd"
.LC1:
        .string "efgh"
.LC2:
        .string "%d"
x1:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     QWORD PTR [rbp-8], OFFSET FLAT:.LC0
        mov     QWORD PTR [rbp-16], OFFSET FLAT:.LC1
        mov     rdx, QWORD PTR [rbp-16]
        mov     rax, QWORD PTR [rbp-8]
        mov     rsi, rdx
        mov     rdi, rax
        call    strcmp              // the strcmp function is actually called
        mov     esi, eax
        mov     edi, OFFSET FLAT:.LC2
        mov     eax, 0
        call    printf
        nop
        leave
        ret

x2:
        push    rbp
        mov     rbp, rsp
        mov     esi, -1             // strcmp is never called, the compiler
                                    // knows what the result will be and it just
                                    // uses -1
        mov     edi, OFFSET FLAT:.LC2
        mov     eax, 0
        call    printf
        nop
        pop     rbp
        ret
当编译器看到strcmp(“abcd”,“efgh”)
时,它会事先知道结果,因为它知道
“abcd”
“efgh”
之前

但是如果它看到
strcmp(a,b)
,它不知道,因此会生成实际调用
strcmp
的代码


使用另一个编译器或不同的编译器设置,情况可能会有所不同。你真的不应该关心这些细节,至少在初学者的水平上是这样。

我相信(需要查看(并解释)机器代码)一个版本在不调用库中的代码的情况下工作(就像你编写了
printf(“%d”、-1);
)。

strcmp
返回一个<0、0或>0的值。除0外,未指定实际值。它们都是正确的。其余的都无关紧要。[但如果你真的想知道:检查汇编程序的输出]@AjayMishra the bahaviour并不含糊。它返回一个负值,这是规范要求它应该做的。它没有任何含糊不清的地方。标准保证的唯一一件事是返回值将小于、等于或大于0。没有人关心某些特定示例的确切值。它们是不相关的,您不能假设-4或-1更“正确”来编写代码。这不是含糊不清的,它是无可争议的<0。编写编译器代码的人不会在意,他们会返回最简单的重要值。没有一致性要求。-4是第一个不匹配的ascii字符之间的差异letter@AjayMishra在这种情况下,这是真的,但它可能是任何东西。它甚至可能是
INT\u MIN
,尽管这不太可能。之前知道如何影响行为?@AjayMishra
strcmp(“abcd”,“efgh”)
在任何情况下都会返回负值。如果编译器足够聪明,能够理解这一点,那么只需将对strcmp的调用替换为返回负值的代码,在本例中为-1;它也可以有返回-2,这也是一个负值。编译器不应生成您编写的C代码的一对一翻译的代码,但应生成与您编写的C代码行为相同的代码。@Jabberwocky+1回答不错,添加编译器不应生成您编写的C代码的一对一翻译的代码,但预期它将生成与您编写的C代码相同的代码。从你的评论到你的答案,都会让它变得完美:-)
.LC0:
        .string "abcd"
.LC1:
        .string "efgh"
.LC2:
        .string "%d"
x1:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     QWORD PTR [rbp-8], OFFSET FLAT:.LC0
        mov     QWORD PTR [rbp-16], OFFSET FLAT:.LC1
        mov     rdx, QWORD PTR [rbp-16]
        mov     rax, QWORD PTR [rbp-8]
        mov     rsi, rdx
        mov     rdi, rax
        call    strcmp              // the strcmp function is actually called
        mov     esi, eax
        mov     edi, OFFSET FLAT:.LC2
        mov     eax, 0
        call    printf
        nop
        leave
        ret

x2:
        push    rbp
        mov     rbp, rsp
        mov     esi, -1             // strcmp is never called, the compiler
                                    // knows what the result will be and it just
                                    // uses -1
        mov     edi, OFFSET FLAT:.LC2
        mov     eax, 0
        call    printf
        nop
        pop     rbp
        ret