将字符串作为指针或文字传递时,strcmp()返回值不一致

时间:2015-01-03 02:41:12

标签: c++ c linux strcmp

当我注意到这一点时,我正在玩strcmp,这是代码:

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

int main(){

    //passing strings directly
    printf("%d\n", strcmp("ahmad", "fatema"));

    //passing strings as pointers 
    char *a= "ahmad";
    char *b= "fatema";
    printf("%d\n",strcmp(a,b));

    return 0;

}

输出是:

-1
-5

strcmp不应该一样吗?当我将字符串作为"ahmad"char* a = "ahmad"传递时,为什么给予不同的值。将值传递给函数时,它们是否在其堆栈中分配?

2 个答案:

答案 0 :(得分:46)

您很可能会看到编译器优化的结果。如果我们test the code using gcc on godbolt-O0优化级别,我们可以看到第一种情况,它不会调用strcmp

movl    $-1, %esi   #,
movl    $.LC0, %edi #,
movl    $0, %eax    #,
call    printf  #

由于您使用常量作为strcmp的参数,编译器能够执行constant folding并在编译时调用compiler intrinsic并生成-1,而不是必须在运行时调用strcmp,这在标准库中实现,并且具有不同的实现,然后可能更简单的编译时间strcmp

在第二种情况下,它会生成对strcmp的调用:

call    strcmp  #
movl    %eax, %esi  # D.2047,
movl    $.LC0, %edi #,
movl    $0, %eax    #,
call    printf  #

这与gcc has a builtin for strcmp这一事实是一致的,test using -O1 optimization level or greatergcc在常量折叠过程中将使用的事实。

如果我们进一步-fno-builtin flag gcc能够折叠这两种情况,那么两种情况的结果都是-1

movl    $-1, %esi   #,
movl    $.LC0, %edi #,
xorl    %eax, %eax  #
call    printf  #
movl    $-1, %esi   #,
movl    $.LC0, %edi #,
xorl    %eax, %eax  #
call    printf  #

启用了更多优化选项后,优化器能够确定ab指向编译时已知的常量,并且还可以为此计算strcmp的结果在编译期间也是如此。

我们可以通过使用{{3}}进行构建来确认gcc正在使用内置函数,并观察将为所有情况生成对strcmp的调用。

clang略有不同,因为它根本不会使用-O0折叠,但会在-O1及以上折叠。

请注意,任何否定结果都是完全一致的,我们可以通过转到草案C99标准部分7.21.4.2看到strcmp函数(强调我的):

int strcmp(const char *s1, const char *s2);
     

strcmp函数返回一个大于,等于,或更小的整数   因此,当s1指向的字符串大于时,<零> ,   等于或小于s2指向的字符串

technosurus指出strcmp被指定为将字符串视为由 unsigned char 组成,这在7.21.1下的C99中有所说明:

  

对于本子条款中的所有功能,每个字符应为   解释为它有类型unsigned char(因此每个   可能的对象表示是有效的,并且具有不同的值。)

答案 1 :(得分:13)

我认为您认为strcmp返回的值应该以某种方式依赖于传递给它的输入字符串,而这种输入字符串不是由函数规范定义的。这不正确。例如,参见POSIX定义:

http://pubs.opengroup.org/onlinepubs/009695399/functions/strcmp.html

  

完成后,如果s1指向的字符串分别大于,等于或小于s2指向的字符串,strcmp()将返回大于,等于或小于0的整数。

这正是你所看到的。该实现不需要对精确返回值做任何保证 - 只有在适当时小于零,等于零或大于零。