Сравнение строк без strcmp () - PullRequest
4 голосов
/ 06 июня 2019

strcmp сравнивает содержимое строки, поэтому его предпочтительнее, чем if (str1 == str2), который сравнивает базовый адрес строк.

Если это так, почему условие if будет выполнено в приведенном ниже коде C:

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


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

ГБД:

(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 ]

4 голосов
/ 06 июня 2019

Не указано, будут ли одни и те же строковые литералы выделены разным хранилищам, или же будет использоваться одно и то же хранилище для указания на все случаи использования строкового литерала.

В нашем случае строковый литерал "Sample1" имеет толькоодна копия, и один и тот же адрес назначается как p2, так и str[0].Однако это не гарантируется стандартом.

Цитирование C11, глава 6.4.5

Не определено, различаются ли эти массивы при условии, что их элементы имеютсоответствующие значения.[...]

2 голосов
/ 06 июня 2019

Вы объявили три строки:

  • 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
  • Адрес памяти 0x4005f8 является началом строки sample1, то есть: s символ
  • Памятьy адрес 0x4005f9 - следующий символ строки sample1, т. е. символ a
  • ... 0x4005fa равен m
  • ... 0x4005fb is p
  • ... 0x4005fc is l
  • ... 0x4005fd is e
  • ... 0x4005fe is1
  • ... 0x4005ff равно \0 или " nul ", что завершает строку

При тестировании p2 == str[0],Вы проверяете, что значения, хранящиеся в обеих переменных, одинаковы.Значения являются базовым адресом строки.Они содержат « одинаковую строку» и, таким образом, содержат одинаковые значения.

Вполне возможно сохранить строку « same » (т. Е. Тот же текст)в двух разных местах памяти, и в такой ситуации этот тест не пройден.

То, что вы фактически говорите здесь, что две строки - это " один и тот же экземпляр ", они находятся вто же место в памяти, и, следовательно, должно иметь одинаковое содержимое.

... и как его распечатать?

Вы можете распечатать как один символза один раз, используя x/1c, или как строку с нулевым символом в конце, используя x/1s (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"

Печать16 символов из адреса 0x400684:

(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'

Печать адресов, хранящихся в 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
2 голосов
/ 06 июня 2019

C язык позволяет статическим строкам быть уникальными в программе.Это означает, что компилятору разрешено решать, оптимизирует ли он выделение (один раз вместо twince) для статической строки «sample1».

Вы инициализируете p, чтобы указать область, в которой он хранится, и str [0] также является указателем на ту же статическую строку.Следовательно, если они равны или нет, это зависит от реализации, и результат проверки на равенство не определен.

Цитата из 6.4.5p6, Строковые литералы

Не определено, являются ли эти массивыразличаются при условии, что их элементы имеют соответствующие значения.

1 голос
/ 06 июня 2019

Другие вопросы охватили , почему ваши строки равны (в смысле оператора ==), здесь я хочу обратиться непосредственно к вашему вопросу.

0x4005f8 - этоадрес, по которому хранится строковая константа.Вы можете напечатать его с помощью printf преобразования типа "%p", который ожидает аргумент void*, ваше полное утверждение будет следующим:

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

Приведение к void* не является необязательнымс GCC, но вы можете включить их для удаления предупреждения. необходимо, потому что, когда вы передаете указатель как часть списка переменных аргументов, компилятор неявно преобразует его в void *, как это было бы для функции, котораяпринял прототип void * аргумент.

...