没有 strcmp() 的字符串比较
String comparision without strcmp()
strcmp
比较字符串内容,因此它优于 if (str1 == str2)
,后者比较字符串的基地址。
如果是这样,为什么 if
条件在下面的 C 代码中得到满足:
char *p2="sample1";
char* str[2]={"sample1","sample2"};
if(p2==str[0])
{
printf("if condition satisfied\n");
}
GDB:
(gdb) p p2
= 0x4005f8 "sample1"
(gdb) p str[0]
= 0x4005f8 "sample1"
(gdb) p &p2
= (char **) 0x7fffffffdb38
(gdb) p &str[0]
= (char **) 0x7fffffffdb20
(gdb) p *p2
= 115 's'
0x4005f8
到底是什么,我该如何打印它?
是否将相同的字符串文字分配不同的存储空间,或者使用相同的存储空间来指示字符串文字的所有用法,未指定。
在你的例子中,字符串文字 "Sample1"
只有一个副本,并且相同的地址分配给 p2
和 str[0]
。但是,标准并不能保证这一点。
引用 C11
,第 6.4.5 章
It is unspecified whether these arrays are distinct provided their elements have the
appropriate values. [...]
C 语言允许静态字符串在程序中是唯一的。这意味着,允许编译器决定是否优化静态字符串 "sample1".
的分配(一次而不是两次)
您将 p 初始化为指向它存放的区域,并且 str[0] 也是指向同一静态字符串的指针。因此,它们是否相等取决于实现,并且检查是否相等的结果是不确定的。
引自 6.4.5p6,字符串文字
It is unspecified whether these arrays are distinct provided their elements have the appropriate values.
其他问题已经涵盖了为什么你的字符串相等(在==
运算符意义上),这里我想直接解决你的问题。
0x4005f8
是存放字符串常量的地址。您可以使用 printf
类型转换 "%p"
打印它,它需要一个 void*
参数,您的完整语句将是:
printf("p2 = %p\n", (void*)p2);
printf("str[0] = %p\n", (void*)str[0]);
对 void*
的转换对于 GCC 不是必需的,但您可以包含它们以删除警告。 是必要的,因为当您将指针作为一个变量参数列表,编译器不会隐式地将它转换为 void *
,就像对于采用原型 void *
参数的函数那样。
您已经声明了三个字符串:
sample1
,由 p2
指向
sample1
,由 str[0]
指向
sample2
,由 str[1]
指向
因为这些都是“字符串字面值”,所以无法更改,存储在read-only.
允许编译器识别您实际上只有两个唯一的字符串,因此只存储这两个字符串(这取决于实现)。
What exactly is 0x4005f8
?
你会在记忆中找到的大概是这样的:
0x0000004005f8 's'
0x0000004005f9 'a'
0x0000004005fa 'm'
0x0000004005fb 'p'
0x0000004005fc 'l'
0x0000004005fd 'e'
0x0000004005fe '1'
0x0000004005ff '[=10=]'
0x000000400600 's'
0x000000400601 'a'
0x000000400602 'm'
0x000000400603 'p'
0x000000400604 'l'
0x000000400605 'e'
0x000000400606 '2'
0x000000400607 '[=10=]'
...
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
- 内存地址
0x4005f8
是sample1
字符串的开始,即:s
字符
- 内存地址
0x4005f9
是sample1
字符串的下一个字符,即:a
字符
- ...
0x4005fa
是 m
- ...
0x4005fb
是 p
- ...
0x4005fc
是 l
- ...
0x4005fd
是 e
- ...
0x4005fe
是 1
- ...
0x4005ff
是 [=47=]
或“nul”,它终止字符串
当你测试p2 == str[0]
时,你测试两个变量中存储的值是否相同。这些值是字符串的基地址。它们持有“相同的字符串,因此持有相同的值。
将“相同”字符串(即:相同的文本)存储在两个不同的内存位置是完全可行的,在这种情况下,此测试将失败。
你在这里实际上是在说这两个字符串是“相同的实例”,它们驻留在内存中的相同位置,因此必须具有相同的内容。
... and how do I print it?
您可以使用 x/1c
一次打印单个字符,或者使用 x/1s
作为 nul-terminated 字符串打印(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/Whosebug/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
= 0x400684 "sample1"
(gdb) print str[0]
= 0x400684 "sample1"
(gdb) print str[1]
= 0x40068c "sample2"
从地址 0x400684
:
打印三个“strings”
(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 '[=15=]0'
0x40068c: 115 's' 97 'a' 109 'm' 112 'p' 108 'l' 101 'e' 50 '2' 0 '[=15=]0'
打印存储在p2
、str[0]
和str[1]
的地址:
(gdb) x/1a &p2
0x7fffffffe308: 0x400684
(gdb) x/1a &str[0]
0x7fffffffe310: 0x400684
(gdb) x/1a &str[1]
0x7fffffffe318: 0x40068c
strcmp
比较字符串内容,因此它优于 if (str1 == str2)
,后者比较字符串的基地址。
如果是这样,为什么 if
条件在下面的 C 代码中得到满足:
char *p2="sample1";
char* str[2]={"sample1","sample2"};
if(p2==str[0])
{
printf("if condition satisfied\n");
}
GDB:
(gdb) p p2
= 0x4005f8 "sample1"
(gdb) p str[0]
= 0x4005f8 "sample1"
(gdb) p &p2
= (char **) 0x7fffffffdb38
(gdb) p &str[0]
= (char **) 0x7fffffffdb20
(gdb) p *p2
= 115 's'
0x4005f8
到底是什么,我该如何打印它?
是否将相同的字符串文字分配不同的存储空间,或者使用相同的存储空间来指示字符串文字的所有用法,未指定。
在你的例子中,字符串文字 "Sample1"
只有一个副本,并且相同的地址分配给 p2
和 str[0]
。但是,标准并不能保证这一点。
引用 C11
,第 6.4.5 章
It is unspecified whether these arrays are distinct provided their elements have the appropriate values. [...]
C 语言允许静态字符串在程序中是唯一的。这意味着,允许编译器决定是否优化静态字符串 "sample1".
的分配(一次而不是两次)您将 p 初始化为指向它存放的区域,并且 str[0] 也是指向同一静态字符串的指针。因此,它们是否相等取决于实现,并且检查是否相等的结果是不确定的。
引自 6.4.5p6,字符串文字
It is unspecified whether these arrays are distinct provided their elements have the appropriate values.
其他问题已经涵盖了为什么你的字符串相等(在==
运算符意义上),这里我想直接解决你的问题。
0x4005f8
是存放字符串常量的地址。您可以使用 printf
类型转换 "%p"
打印它,它需要一个 void*
参数,您的完整语句将是:
printf("p2 = %p\n", (void*)p2);
printf("str[0] = %p\n", (void*)str[0]);
对 void*
的转换对于 GCC 不是必需的,但您可以包含它们以删除警告。 是必要的,因为当您将指针作为一个变量参数列表,编译器不会隐式地将它转换为 void *
,就像对于采用原型 void *
参数的函数那样。
您已经声明了三个字符串:
sample1
,由p2
指向
sample1
,由str[0]
指向
sample2
,由str[1]
指向
因为这些都是“字符串字面值”,所以无法更改,存储在read-only.
允许编译器识别您实际上只有两个唯一的字符串,因此只存储这两个字符串(这取决于实现)。
What exactly is
0x4005f8
?
你会在记忆中找到的大概是这样的:
0x0000004005f8 's'
0x0000004005f9 'a'
0x0000004005fa 'm'
0x0000004005fb 'p'
0x0000004005fc 'l'
0x0000004005fd 'e'
0x0000004005fe '1'
0x0000004005ff '[=10=]'
0x000000400600 's'
0x000000400601 'a'
0x000000400602 'm'
0x000000400603 'p'
0x000000400604 'l'
0x000000400605 'e'
0x000000400606 '2'
0x000000400607 '[=10=]'
...
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
- 位于地址
- 内存地址
0x4005f8
是sample1
字符串的开始,即:s
字符 - 内存地址
0x4005f9
是sample1
字符串的下一个字符,即:a
字符 - ...
0x4005fa
是m
- ...
0x4005fb
是p
- ...
0x4005fc
是l
- ...
0x4005fd
是e
- ...
0x4005fe
是1
- ...
0x4005ff
是[=47=]
或“nul”,它终止字符串
当你测试p2 == str[0]
时,你测试两个变量中存储的值是否相同。这些值是字符串的基地址。它们持有“相同的字符串,因此持有相同的值。
将“相同”字符串(即:相同的文本)存储在两个不同的内存位置是完全可行的,在这种情况下,此测试将失败。
你在这里实际上是在说这两个字符串是“相同的实例”,它们驻留在内存中的相同位置,因此必须具有相同的内容。
... and how do I print it?
您可以使用 x/1c
一次打印单个字符,或者使用 x/1s
作为 nul-terminated 字符串打印(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/Whosebug/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
= 0x400684 "sample1"
(gdb) print str[0]
= 0x400684 "sample1"
(gdb) print str[1]
= 0x40068c "sample2"
从地址 0x400684
:
(gdb) x/3s 0x400684
0x400684: "sample1"
0x40068c: "sample2"
0x400694: "true"
从地址 0x400684
:
(gdb) x/16c 0x400684
0x400684: 115 's' 97 'a' 109 'm' 112 'p' 108 'l' 101 'e' 49 '1' 0 '[=15=]0'
0x40068c: 115 's' 97 'a' 109 'm' 112 'p' 108 'l' 101 'e' 50 '2' 0 '[=15=]0'
打印存储在p2
、str[0]
和str[1]
的地址:
(gdb) x/1a &p2
0x7fffffffe308: 0x400684
(gdb) x/1a &str[0]
0x7fffffffe310: 0x400684
(gdb) x/1a &str[1]
0x7fffffffe318: 0x40068c