关于char *和数组字符串之间的字符串比较差异的说明

时间:2014-06-29 12:44:34

标签: c arrays string pointers

好吧,我以为我知道关于指针和内存操作的一切,但有一点让我好奇。到目前为止,我一直在用strcmp比较字符串,但是......

这个表达是正确的:

#include <stdio.h>

int main()
{
    char* str1 = "I love StackOverflow"; // dram memory alocated
    char* str2 = "I love StackOverflow";

    if(str1 == str2) printf("%s and %s are equal", str1, str2);
    else printf("%s and %s are not equal", str1, str2);

    return 1;
}

哪个应该在str1和str2的每个内存块之间进行比较? 在这种情况下..如果我们使用:

char str1[] = "I love StackOverflow"; // saving them on stack
char str2[] = "I love StackOverflow";
相反,它不会输出它们是相等的。为什么呢?

4 个答案:

答案 0 :(得分:5)

在第一个例子中,绝对不能保证两个指针是相等的。这是由编译器利用字符串文字在C中不可变的事实执行的优化。

C99理由文件说:

  

“此规范允许实现共享具有相同文本的字符串副本,将字符串文字放在只读内存中,并执行某些优化”

您不应该依赖于这一点,如果您想在第一个或第二个代码段中比较字符串,请使用strcmp / strncmp函数。

答案 1 :(得分:1)

某些编译器尝试通过存储相同字符串文字的单个副本来减少内存需求。

在您的情况下,编译器可能只选择存储"I love StackOverflow"一次,使str1str2都指向它。因此,当您比较str1 == str2时,基本上您将指针与字符串文字的第一个元素(而不是字符串本身)进行比较,这可能指向上述相同的位置,因此给出两个字符串文字相等的结果。你不能依赖它。

答案 2 :(得分:1)

如果char*变量,编译器可以perofm字符串'unification'(抱歉,我不确定它是如何实际调用的),这意味着它检测到相同的字符串常量并且只分配它们一次。这意味着第一个代码编译为

char common_string_detected_1[] = "I love StackOverflow";

char* str1 = common_string_detected_1;
char* str2 = common_string_detected_1;

str1str2包含相同的指针,是数组中“I”的地址。

在后一种情况下,您显式声明了两个数组,并且编译器将它们分开。

答案 3 :(得分:1)

我可以向您展示汇编列表,在gcc上编译;

C ++

char* str1 = "I love StackOverflow";
char* str2 = "I love StackOverflow";

if(str1 == str2) printf("%s and %s are equal", str1, str2);
else printf("%s and %s are not equal", str1, str2);

ASM

LC0: // LC0 - LC1 - LC2 these are labels
    .ascii "I love StackOverflow\0"
LC1:
    .ascii "%s and %s are equal\0"
LC2:
    .ascii "%s and %s are not equal\0"

...

mov DWORD PTR [esp+28], OFFSET FLAT:LC0
mov DWORD PTR [esp+24], OFFSET FLAT:LC0 // moves exact same address into stack
mov eax, DWORD PTR [esp+28] // immediately moves one of them into eax
cmp eax, DWORD PTR [esp+24] // now compares the exact same addresses (LC0)
jne L2 // (jump if not equal)
// followed by code that prints if equal then L2 label(followed by code that prints if not equal)

现在使用[]

LC0:
    .ascii "%s and %s are not equal\0"

...

mov DWORD PTR [esp+43], 1869357129
mov DWORD PTR [esp+47], 1394632054
mov DWORD PTR [esp+51], 1801675124
mov DWORD PTR [esp+55], 1919252047
mov DWORD PTR [esp+59], 2003790950
mov BYTE PTR [esp+63], 0
mov DWORD PTR [esp+22], 1869357129
mov DWORD PTR [esp+26], 1394632054
mov DWORD PTR [esp+30], 1801675124
mov DWORD PTR [esp+34], 1919252047
mov DWORD PTR [esp+38], 2003790950
mov BYTE PTR [esp+42], 0

lea eax, [esp+22]
mov DWORD PTR [esp+8], eax
lea eax, [esp+43]
mov DWORD PTR [esp+4], eax 
// loads the effective address off the stack of the data for both strings
// notice these two address are different, because both strings sit in different places on the stack
// it doesn't even bother comparing them and has removed the "is equal" string
mov DWORD PTR [esp], OFFSET FLAT:LC0
call    _printf