Чому в мові "a"! = "A"?


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, це стисло написане питання, яке стосується загальної точки плутанини для новачків мови. Отож, це не лише для експертів - це також для початківців, і такі цілеспрямовані питання, як це, добре підходять для початківців у майбутньому.
бдонлан

37
@Felix: Ви помиляєтесь. масиви не є покажчиками
John Dibling

Відповіді:


209

Ви порівнюєте дві адреси пам'яті для різних рядків, які зберігаються в різних місцях. Це по суті виглядає так:

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

Використовуйте наступний код для порівняння двох рядкових значень:

#include <string.h>

...

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

Крім того, "a" == "a"може дійсно повернути true, залежно від вашого компілятора, який може поєднувати рівні струни за час компіляції в один, щоб заощадити місце.

Якщо ви порівнюєте два значення символів (які не є вказівниками), це числове порівняння. Наприклад:

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

12
У GCC також є варіанти -fmerge-constants та -fno-merge-constantsвмикати / вимикати постійне з’єднання рядків та плаваючої крапки через одиниці перекладу, хоча в деяких GCC, здається, що постійне злиття завжди вмикається незалежно від цього параметра.
Адам Розенфілд

2
Це буде працювати, якщо ви використовуєте "a" замість "a". Перший - це знак, який є фактично числовим значенням.
GolezTrol

@GolezTrol: в мові буква буква 'a' фактично має intтип. :-) Також покажчики не повинні бути числовими значеннями.
Бастієн Леонард

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

Оскільки вони є двома окремими 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-байтної межі? Вони не є ints. 2 - це те, що я очікував (якщо компілятор не зливає їх), оскільки кожен рядок має два байти, включаючи нульовий термінатор.
Сергій Таченов

Наприклад, деяка ступінь вирівнювання може дозволяти strcmpзапускати кілька байтів одночасно. Деякі компілятори роблять це, деякі ні, деякі роблять лише для рядків, довших за мінімальний ...
zwol

@Zack: як би вони дізналися довжину рядка, перш ніж насправді порівнювати їх?
Йоахім Зауер

Я мав на увазі, що деякі компілятори вирівнюють рядки довше ніж мінімум.
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 = "a";
char * text2 = "a";

то ви могли б порівняти textз text2, і це повинно бути правдою

Можливо, ви не повинні забути використовувати {і }=)

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

1
", і це повинно бути правдою " - Ні. Не визначено, чи зберігатимуться рядкові літерали в тому самому місці пам'яті. Прочитайте інші відповіді.
Spikatrix
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.