Почему «а»! = «А» в 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.