Почему не (“язык майя” == “язык майя”) верен в C++?

Какая-либо идея, почему я получаю "язык майя, не майя" в результате этого кода?

if ("Maya" == "Maya") 
   printf("Maya is Maya \n");
else
   printf("Maya is not Maya \n");
23
задан metamorphosis 17 May 2018 в 06:30
поделиться

8 ответов

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

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

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

Обычный строковый литерал имеет тип "array of n const char"

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

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

48
ответ дан 29 November 2019 в 00:41
поделиться

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

Чтобы быть более точным -

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

В языке C вам нужна функция strmp (char *, char *), которая возвращает 0 при равенстве.

Или, в C ++, вы можете сделать следующее:

#include

std :: string s1 = "foo"

std :: string s2 = "bar"

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

18
ответ дан 29 November 2019 в 00:41
поделиться

Результат вашей программы зависит от реализации.

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

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

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

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

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

if (std::string("Maya") == "Maya") // same
9
ответ дан 29 November 2019 в 00:41
поделиться

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

8
ответ дан 29 November 2019 в 00:41
поделиться

Любая идея, почему я получаю «Maya is not Maya» в результате

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

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

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

#include <string>

// ...

if (std::string("Maya") == "Maya") 
   std::cout << "Maya is Maya\n";
else
   std::cout << "Maya is not Maya\n";
6
ответ дан 29 November 2019 в 00:41
поделиться

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

1
ответ дан 29 November 2019 в 00:41
поделиться

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

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

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

дает:

23 - 523 = 1
1
ответ дан 29 November 2019 в 00:41
поделиться

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

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

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

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

Ключевым моментом здесь является то, что старая добрая «строка» в стиле C - это массив символов в памяти. Этот блок памяти часто упоминается с помощью указателя - адреса начала блока памяти.Обычно, когда вы ссылаетесь на «строку» в C, вы имеете в виду этот блок памяти или указатель на него. В C нет типа string как такового; строки - это просто набор из char подряд.

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

"wibble"

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

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

if ("Maya" == "Maya")

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

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

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

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

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

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

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

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

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

1
ответ дан 29 November 2019 в 00:41
поделиться
Другие вопросы по тегам:

Похожие вопросы: