没有strcmp()的字符串比较

时间:2019-06-06 09:54:03

标签: c string string-literals

strcmp会比较字符串的内容,因此比if (str1 == str2)会比较字符串的基地址。

如果是,为什么在下面的C代码中满足if条件:

    char *p2="sample1";
    char* str[2]={"sample1","sample2"};


    if(p2==str[0])
    {
            printf("if condition satisfied\n");
    }

GDB:

(gdb) p p2
$1 = 0x4005f8 "sample1"
(gdb) p str[0]
$2 = 0x4005f8 "sample1"
(gdb) p &p2
$3 = (char **) 0x7fffffffdb38
(gdb) p &str[0]
$4 = (char **) 0x7fffffffdb20
(gdb) p *p2
$5 = 115 's'

0x4005f8到底是什么,我该如何打印?

4 个答案:

答案 0 :(得分:4)

未指定是否为相同的字符串文字分配不同的存储空间,或者是否将相同的存储空间用于指示字符串文字的所有用法。

在这种情况下,字符串文字"Sample1"仅具有一个副本,并且为p2str[0]分配了相同的地址。但是,标准并不能保证这一点。

引用C11,第6.4.5章

  

不确定这些数组是否不同,只要它们的元素具有   适当的值。 [...]

答案 1 :(得分:2)

C语言允许静态字符串在程序中唯一。这意味着编译器可以决定是否优化静态字符串“ sample1”的分配(一次而不是一次)。

您初始化p使其指向要存放的区域,并且str [0]也是指向相同静态字符串的指针。因此,它们是否相等取决于实现方式,并且检查相等性的结果不确定。

引用6.4.5p6,字符串文字

  

如果这些数组的元素具有适当的值,则不确定这些数组是否不同。

答案 2 :(得分:2)

您已经声明了三个字符串:

    sample1指向的
  • p2
  • sample1指向的
  • str[0]
  • sample2指向的
  • str[1]

这些都是“ 字符串文字”,因此无法更改,并且以只读方式存储。

允许编译器识别出您实际上只有两个唯一的字符串,因此仅存储了两个字符串(取决于实现)。


  

0x4005f8到底是什么?

您在内存中发现的可能是这样的:

0x0000004005f8  's'
0x0000004005f9  'a'
0x0000004005fa  'm'
0x0000004005fb  'p'
0x0000004005fc  'l'
0x0000004005fd  'e'
0x0000004005fe  '1'
0x0000004005ff  '\0'
0x000000400600  's'
0x000000400601  'a'
0x000000400602  'm'
0x000000400603  'p'
0x000000400604  'l'
0x000000400605  'e'
0x000000400606  '2'
0x000000400607  '\0'
...
0x7fffffffdb20  0xf8
0x7fffffffdb21  0x05
0x7fffffffdb22  0x40
0x7fffffffdb23  0x00
0x7fffffffdb24  0x00
0x7fffffffdb25  0x00
0x7fffffffdb26  0x00
0x7fffffffdb27  0x00
...
0x7fffffffdb38  0xf8
0x7fffffffdb39  0x05
0x7fffffffdb3a  0x40
0x7fffffffdb3b  0x00
0x7fffffffdb3c  0x00
0x7fffffffdb3d  0x00
0x7fffffffdb3e  0x00
0x7fffffffdb3f  0x00

也就是说:

  • p2变量:
    • 位于地址0x7fffffffdb38
    • 值为0x4005f8
  • str[0]变量:
    • 位于地址0x7fffffffdb20
    • 值为0x4005f8
  • 内存地址0x4005f8sample1字符串的开头,即s字符
  • 内存地址0x4005f9sample1字符串的下一个字符,即a字符
  • ... 0x4005fam
  • ... 0x4005fbp
  • ... 0x4005fcl
  • ... 0x4005fde
  • ... 0x4005fe1
  • ... 0x4005ff\0或“ nul ”,它终止字符串

测试p2 == str[0]时,将测试两个变量中存储的值是否相同。值是字符串的基地址。它们具有“ 相同”字符串,因此具有相同的值。

将“ 相同”字符串(即相同的文本)存储在两个不同的内存位置中是完全可行的,在这种情况下,该测试将失败。

您实际上在说两个字符串是“ 相同实例”,它们位于内存中的同一位置,因此必须具有相同的内容。

  

...以及如何打印?

您可以使用x/1c一次打印为单个字符,也可以使用x/1s打印为以n终止的字符串(gdb可以正确处理C字符串)。


main.c

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {
        char *p2 = "sample1";
        char *str[2] = { "sample1", "sample2" };

        if (p2 == str[0]) {
                printf("true\n");
        }

        return 0;
}

编译:

gcc main.c -o main -g

运行:

$ gdb ./main
[...]
(gdb) start
Temporary breakpoint 1 at 0x4005a5: file main.c, line 4.
Starting program: /home/attie/stackoverflow/56475101/main

Temporary breakpoint 1, main (argc=1, argv=0x7fffffffe418) at main.c:4
4       int main(int argc, char *argv[]) {
(gdb) list
1       #include <stdio.h>
2       #include <stdlib.h>
3
4       int main(int argc, char *argv[]) {
5               char *p2 = "sample1";
6               char *str[2] = { "sample1", "sample2" };
7
8               if (p2 == str[0]) {
9                       printf("true\n");
10              }
(gdb) b 8
Breakpoint 2 at 0x4005cc: file main.c, line 8.
(gdb) c
Continuing.

Breakpoint 2, main (argc=1, argv=0x7fffffffe418) at main.c:8
8               if (p2 == str[0]) {
(gdb) print p2
$1 = 0x400684 "sample1"
(gdb) print str[0]
$2 = 0x400684 "sample1"
(gdb) print str[1]
$3 = 0x40068c "sample2"

从地址0x400684打印三个“ 字符串”:

(gdb) x/3s 0x400684
0x400684:       "sample1"
0x40068c:       "sample2"
0x400694:       "true"

从地址0x400684打印16个字符:

(gdb) x/16c 0x400684
0x400684:       115 's' 97 'a'  109 'm' 112 'p' 108 'l' 101 'e' 49 '1'  0 '\000'
0x40068c:       115 's' 97 'a'  109 'm' 112 'p' 108 'l' 101 'e' 50 '2'  0 '\000'

打印存储在p2str[0]str[1]上的地址:

(gdb) x/1a &p2
0x7fffffffe308: 0x400684
(gdb) x/1a &str[0]
0x7fffffffe310: 0x400684
(gdb) x/1a &str[1]
0x7fffffffe318: 0x40068c

答案 3 :(得分:1)

其他问题涵盖了为什么您的字符串相等(从==运算符的角度来看),在这里我想直接解决您的问题。

0x4005f8是存储字符串常量的地址。您可以使用printf类型转换"%p"打印它,该转换需要一个void*参数,您的完整语句为:

printf("p2 = %p\n", (void*)p2);
printf("str[0] = %p\n", (void*)str[0]);

在GCC中,对void*的强制转换不是必需的,但是可以包含它们以删除警告。是必需的,因为当您将指针作为变量参数列表的一部分传递时,编译器不会像使用原型void *参数的函数那样将其隐式转换为void *