Почему не ("Maya" == "Maya") верно в C ++? - PullRequest
23 голосов
/ 21 июля 2010

Есть идеи, почему в результате этого кода я получаю "Майя не Майя"?

if ("Maya" == "Maya") 
   printf("Maya is Maya \n");
else
   printf("Maya is not Maya \n");

Ответы [ 8 ]

48 голосов
/ 21 июля 2010

Поскольку вы на самом деле сравниваете два указателя - используйте вместо этого, например, один из следующих:

if (std::string("Maya") == "Maya") { /* ... */ } 
if (std::strcmp("Maya", "Maya") == 0) { /* ... */ }

Это потому, что C ++ 03, §2.13.4 говорит:

Обычный строковый литерал имеет тип «массив из n const char»

... и в вашем случае применяется преобразование в указатель.

См. Также этот вопрос о том, почему вы не можете предоставить перегрузку для == для этого случая.

18 голосов
/ 21 июля 2010

Вы не сравниваете строки, вы сравниваете равенство адресов указателей.

Чтобы быть более явным -

"foo baz bar" неявно определяет аноним const char[m]. Это определяется реализацией относительно того, будет ли идентичный анонимный const char [m] указывать на одно и то же место в памяти (концепция, именуемая interning ).

Функция, которую вы хотите - в C - это strmp (char *, char *), которая возвращает 0 при равенстве.

Или, в C ++, вы могли бы сделать

#include <string>

std::string s1 = "foo"

std::string s2 = "bar"

, а затем сравните s1 и s2 с оператором ==, который интуитивно определен для строк.

9 голосов
/ 21 июля 2010

Вывод вашей программы определяется реализацией.

Строковый литерал имеет тип const char[N] (то есть это массив). Независимо от того, представлен ли каждый строковый литерал в вашей программе уникальным массивом, определяется реализацией . (§2.13.4 / 2)

Когда вы делаете сравнение, массивы распадаются на указатели (на первый элемент), и вы делаете сравнение указателей. Если компилятор решает сохранить оба строковых литерала как один и тот же массив, указатели сравнивают true; если у каждого из них есть свое собственное хранилище, они сравнивают ложь.

Чтобы сравнить строки, используйте std::strcmp(), например:

if (std::strcmp("Maya", "Maya") == 0) // same

Обычно вы используете стандартный класс строк, std::string. Он определяет operator==. Вам нужно сделать один из ваших литералов std::string, чтобы использовать этот оператор:

if (std::string("Maya") == "Maya") // same
8 голосов
/ 21 июля 2010

То, что вы делаете, сравнивает адрес одной строки с адресом другой. В зависимости от компилятора и его настроек иногда идентичные литеральные строки будут иметь один и тот же адрес, а иногда - нет (как вы, очевидно, обнаружили).

6 голосов
/ 21 июля 2010

Любая идея, почему в результате я получаю "Maya не Maya"

Поскольку в C и, следовательно, в C ++ строковые литералы имеют тип const char[], что неявнопреобразуется в const char*, указатель на первый символ, при попытке сравнить их. И сравнение указателей - это сравнение адресов.
То, будут ли сравниваться два строковых литерала, зависит от того, объединяет ли ваш компилятор (используя ваши текущие настройки) строковые литералы.Это разрешено делать, но это не нужно.,

Чтобы сравнить строки в C, используйте strcmp() из заголовка <string.h>.(Это std::strcmp() из <cstring> в C ++.)

Чтобы сделать это в C ++, проще всего превратить один из них в std::string (из заголовка <string>), который поставляется свсе операторы сравнения, включая ==:

#include <string>

// ...

if (std::string("Maya") == "Maya") 
   std::cout << "Maya is Maya\n";
else
   std::cout << "Maya is not Maya\n";
1 голос
/ 22 июля 2010

Действительно, «поскольку ваш компилятор в данном случае не использует пул строк», это технически правильный, но не особо полезный ответ:)

Это одна из многих причин, по которым класс std :: string в стандартной библиотеке шаблонов теперь существует для замены этого более раннего типа строк, когда вы хотите сделать что-нибудь полезное со строками в C ++, и является проблема почти всех, кто когда-либо изучал C или C ++, спотыкается довольно рано в своих исследованиях.

Позвольте мне объяснить.

В принципе, еще во времена C все строки работали так. Строка - это просто набор символов в памяти. Строка, которую вы встраиваете в исходный код C, преобразуется в набор байтов, представляющих эту строку в работающем машинном коде при выполнении вашей программы.

Важнейшей частью здесь является то, что хорошая старомодная «строка» в стиле C - это массив символов в памяти. На этот блок памяти часто ссылается указатель - адрес начала блока памяти. Обычно, когда вы ссылаетесь на «строку» в C, вы ссылаетесь на этот блок памяти или указатель на него. C не имеет типа string ; строки - это просто набор символов с подряд.

Когда вы пишете это в своем коде:

"wibble"

Затем компилятор предоставляет блок памяти, который содержит байты, представляющие символы 'w', 'i', 'b', 'b', 'l', 'e' и '\ 0' в этом порядке. (компилятор добавляет нулевой байт в конце, «нулевой терминатор». В C стандартной строкой является строка с нулевым символом в конце: блок символов, начинающийся с данного адреса памяти и продолжающийся до следующего нулевого байта.)

И когда вы начинаете сравнивать такие выражения, происходит следующее:

if ("Maya" == "Maya")

В момент этого сравнения компилятор - в ваш случай, в частности; см. мое объяснение объединения строк в конце - создано два отдельных блока памяти для хранения двух разных наборов символов, для которых оба установлены на «M», «a», «y», «a», «\ 0». .

Когда компилятор видит строку в кавычках, подобную этой, «под капотом» он создает массив символов, а сама строка «Maya» действует как имя массива символов , Поскольку имена массивов фактически являются указателями, указывающими на первый символ массива, тип выражения «Maya» - это указатель на символ .

Когда вы сравниваете эти два выражения, используя "==", вы фактически сравниваете указатели , адреса памяти начала этих двух различных блоков памяти, Вот почему в вашем конкретном случае сравнение ложно с вашим конкретным компилятором.

Если вы хотите сравнить две старые добрые C-строки, вы должны использовать функцию strcmp () . Это проверит содержимое памяти, обозначенной двумя «строками» (которые, как я уже объяснил, являются просто указателями на блок памяти), и пройдет через байты, сравнивая их один за другим, и скажет вам действительно ли они действительно одинаковы.

Теперь, как я уже сказал, это своего рода немного удивительный результат, который кусал начинающих C с задницы со времен прошлого. И это одна из причин, по которой язык развивался с течением времени. Теперь в C ++ есть класс std :: string , который будет содержать строки и работать так, как вы ожидаете. Оператор "==" для std :: string фактически сравнивает содержимое двух std :: strings .

По умолчанию, однако, C ++ разработан для обратной совместимости с C, т.е. программа на C обычно компилируется и работает под компилятором C ++ так же, как в компиляторе C, а это означает, что строки старомодны «подобные вещи в вашем коде» все равно будут указывать на биты памяти, которые дадут новичку неочевидные результаты, когда вы начнете их сравнивать.

О, а тот "пул струн", о котором я упоминал в начале? Вот тут-то и может возникнуть некоторая сложность. Умный компилятор, который будет эффективен со своей памятью, может заметить, что в вашем случае строки одинаковы и не могут быть изменены, и поэтому выделяются только one блок памяти, с обоими вашими именами "Майя", указывающими на него. В этот момент сравнение «строк» ​​- указателей - покажет вам, что они , фактически равны. Но больше удачей, чем дизайном!

Это поведение «объединения строк» ​​будет меняться от компилятора к компилятору и часто будет отличаться в зависимости от режимов отладки и выпуска одного и того же компилятора, поскольку режим выпуска часто включает в себя такие оптимизации, которые делают код вывода более компактным ( должен иметь только один блок памяти с «Maya», а не два, так что сохраняется пять - запомните этот нулевой терминатор! - байтов в объектном коде.) И это такое поведение, которое может свести с ума человека, если они не знают, что происходит:)

Если ничего другого, этот ответ может дать вам много поисковых терминов для тысяч статей, которые уже есть в сети, пытаясь объяснить это. Это немного больно, и все проходят через это. Если вы сможете разобраться с указателями, вы в конечном итоге станете намного лучше программистом на C или C ++, независимо от того, решите вы использовать вместо него std :: string или нет!

1 голос
/ 21 июля 2010

C и C ++ делают это сравнение через сравнение указателей;похоже, ваш компилятор создает отдельные экземпляры ресурсов для строк "Maya" и "Maya" (возможно, из-за отключения оптимизации).

1 голос
/ 21 июля 2010

Мой компилятор говорит, что они являются одинаковыми; -)

еще хуже, мой компилятор, конечно, сломан. Это очень простое уравнение:

printf("23 - 523 = %d\n","23"-"523");

производит:

23 - 523 = 1
...