Почему «а»! = «А» в C?


110
void main() {
    if("a" == "a")
      printf("Yes, equal");  
    else
      printf("No, not equal");
}

Почему на выходе No, not equal?


100
void main??? Фу ...
— Пол Р

47
Встроенные компиляторы C позволяют использовать void main (), потому что может не быть какой-либо операционной системы, которой можно было бы передать код возврата.
— Жанна Пиндар

26
Как можно так часто голосовать за такой вопрос? Это действительно не так интересно ... Я имею в виду, что строки - это массивы, а массивы - указатели - это действительно старая шляпа в C, не так ли?
— Феликс Домбек

64
@Felix, это кратко написанный вопрос, который устраняет общую путаницу для новичков в этом языке. SO не только для экспертов - это также для новичков, и подобные целевые вопросы хороши для направления новичков в будущем.
— bdonlan

37
@ Феликс: Вы ошибаетесь. массивы не являются указателями
— Джон Диблинг

Ответы:


209

Вы сравниваете два адреса памяти для разных строк, которые хранятся в разных местах. По сути, это выглядит так:

if(0x00403064 == 0x002D316A) // Two memory locations
{
    printf("Yes, equal");
}

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

#include <string.h>

...

if(strcmp("a", "a") == 0)
{
    // Equal
}

Кроме того, "a" == "a"действительно может возвращать истину, в зависимости от вашего компилятора, который может объединять одинаковые строки во время компиляции в одну для экономии места.

Когда вы сравниваете два символьных значения (которые не являются указателями), это числовое сравнение. Например:

'a' == 'a' // always true

12
GCC также имеет параметры -fmerge-constantsи -fno-merge-constantsдля включения / отключения слияния строк и констант с плавающей запятой в единицах перевода, хотя в некоторых GCC кажется, что слияние констант всегда включено независимо от этой опции.
— Адам Розенфилд

2
Это сработает, если вы используете «а» вместо «а». Первый - это символ, который на самом деле является числовым значением.
— GolezTrol

@GolezTrol: в C буквальный "a" действительно имеет intтип. :-) Кроме того, указатели не обязательно должны быть числовыми значениями.
— Bastien Léonard

intтоже числовое, не так ли? Но я думал, что символы были байтами. Int - 4 байта. Сами указатели тоже целочисленные. Они содержат адрес группы данных (данные, которые действительно не обязательно должны быть числовыми).
— GolezTrol

'a' == 'A' // not true... MySQL требует отличия.
— Стивен

52

Я немного опоздал на вечеринку, но все равно отвечу; технически те же биты, но с немного другой точки зрения (язык C ниже):

В языке C это выражение "a"обозначает строковый литерал , который является статическим безымянным массивом const char, длиной два - массив состоит из символов 'a'и '\0'- завершающий нулевой символ сигнализирует о конце строки.

Однако в C точно так же вы не можете передавать массивы функциям по значению - или присваивать им значения ( после инициализации ) - ==для массивов нет перегруженного оператора , поэтому сравнивать их напрямую невозможно. Рассматривать

int a1[] = {1, 2, 3};
int a2[] = {3, 4, 5};
a1 == a2 // is this meaningful? Yes and no; it *does* compare the arrays for
         // "identity", but not for their values. In this case the result
         // is always false, because the arrays (a1 and a2) are distinct objects

Если ==он не сравнивает массивы, что тогда он на самом деле делает? В C почти во всех контекстах, включая этот, массивы распадаются на указатели (которые указывают на первый элемент массива), и сравнение указателей на равенство делает то, что вы ожидаете. Так эффективно, делая это

"a" == "a"

вы фактически сравниваете адреса первых символов в двух безымянных массивах . Согласно стандарту C, сравнение может дать истинное или ложное значение (т.е. 1 или 0) - "a"s может фактически обозначать один и тот же массив или два совершенно не связанных массива. С технической точки зрения результирующее значение не указано , что означает, что сравнение разрешено (т. Е. Это не неопределенное поведение или синтаксическая ошибка), но любое значение является допустимым, и реализация (ваш компилятор) не требуется для документирования того, что на самом деле произойдет.

Как указывали другие, для сравнения «c-строк» ​​(т.е. строк, оканчивающихся нулевым символом) вы используете вспомогательную функцию, strcmpнайденную в стандартном файле заголовка string.h. Функция имеет возвращаемое значение 0для одинаковых строк; считается хорошей практикой явно сравнивать возвращаемое значение с 0вместо использования оператора `! ´, т.е.

strcmp(str1, str2) == 0 // instead of !strcmp(str1, str2)

47

Согласно C99 (Раздел 6.4.5 / 6)

Строковые литералы

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

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

Посмотрите вывод на gcc здесь


19

Потому что это 2 отдельных const char*указателя, без фактических значений. Вы говорите что-то вроде того, 0x019181217 == 0x0089178216что конечно возвращает НЕТ

Используйте strcmp()вместо==


7
Строковые литералы - это не указатели, это массивы. Однако при сравнении они превращаются в указатели.
— GManNickG

@Gman правда, извините за то, что не совсем ясно об этом, как правило, забываю об этом :)
— Antwan van Houdt

9

Проще говоря, в C нет встроенного оператора сравнения строк. Таким образом нельзя сравнивать строки.

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

В C строка текста в двойных кавычках возвращает указатель на строку. Ваш пример сравнивает указатели, и, очевидно, ваши две версии строки существуют по разным адресам.

Но это не сравнение самих струн, как вы, кажется, ожидаете.


3

Указатели.

Первый "a"- это указатель на строку ASCII с завершающим нулем.

Второй "a"- указатель на другую строку ASCII с завершающим нулем.

Если вы используете 32-битный компилятор, я бы ожидал "a"=="a"-4. Я только что попробовал с tcc / Win32, и у меня получилось "a"=="a"-2. Ну что ж...


6
Почему вы ожидаете, что строки будут выровнены по 4-байтовой границе? Они не целые. 2 - это то, что я ожидал (если компилятор не объединяет их), поскольку каждая строка имеет длину два байта, включая нулевой терминатор.
— Сергей Таченов

Некоторая степень выравнивания может, например, позволить strcmpзапускать несколько байтов за раз. Некоторые компиляторы делают это, некоторые нет, некоторые делают это только для строк длиннее минимума ...
— zwol

@Zack: как они узнают длину строки, прежде чем сравнивать их?
— Joachim Sauer

Я имел в виду, что некоторые компиляторы выравнивают строки длиннее минимума.
— zwol

1

Вы сравниваете два адреса памяти, поэтому результат не всегда будет правдой. Вы пробовали if('a' == 'a'){...}?


1

этот вопрос дает очень хорошее объяснение всем начинающим ....
позвольте мне также внести свой вклад в это .....

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

теперь если хочешь свою прогу. Чтобы вывести «да равно», тогда

либо использовать

if(strcmp("a", "a") == 0)
{

}

или
не используйте "a" как строки, используйте их как символы ....

if('a'=='a')  
{  
printf ("yes Equal");  
}  

в символах C - это короткое целое число размером 1 байт .......


Символы занимают только 1 байт, но символьные литералы, такие как 'a', на самом деле являются целыми числами.
— Spidey

0

В некоторых компиляторах есть опция «объединить строки», которую можно использовать, чтобы заставить все константные строки иметь один и тот же адрес. Если бы вы использовали это, "a" == "a"было бы true.


0

если сравнение между символами всегда в одинарных кавычках, например

if('a' == 'a')

и C не может поддерживать сравнение строк, например "abc" == "abc"

Это сделано с strcmp("abc","abc")


-5

Этот парень не использует переменные. Вместо этого он временно использует текстовые массивы: aи a. Причина почему

void main() 
{
    if("a" == "a")
      printf("Yes, equal");  
    else
      printf("No, not equal");
}

не работает конечно, это то, что вы не сравниваете переменные.
Если бы вы создали такие переменные, как:

char * text = "а";
char * text2 = "а";

то вы могли бы сравнить textс text2, и это должно быть правдой

Может не стоит забывать использовать {и }=)

void main() {
    if("a" == "a")
    {
      printf("Yes, equal");
    }
    else
    {
      printf("No, not equal");
    }
}

1
" и это должно быть правдой " - Нет. Не указано, будут ли строковые литералы храниться в том же месте памяти. Прочтите другие ответы.
— Spikatrix 05
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.