Dlaczego „a”! = „A” w C?

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

Dlaczego jest wyjście No, not equal?

Javed Akram
źródło
100
void main??? Ew ...
Paul R
47
Wbudowane kompilatory C zezwalają na void main (), ponieważ może nie istnieć żaden system operacyjny, któremu można by podać kod powrotu.
Jeanne Pindar
26
Jak to możliwe, że takie pytanie jest tak często brane pod uwagę? To naprawdę nie jest takie interesujące ... Mam na myśli to, że łańcuchy to tablice, a tablice to wskaźniki, to naprawdę stara czapka w C, prawda?
Felix Dombek
64
@Felix, to zwięźle napisane pytanie, które dotyczy częstego zamieszania wśród nowicjuszy w tym języku. SO nie jest tylko dla ekspertów - jest również dla początkujących, a ukierunkowane pytania, takie jak to, są dobre do odniesienia się do początkujących w przyszłości.
bdonlan,
37
@Felix: Mylisz się. tablice nie są wskaźnikami
John Dibling

Odpowiedzi:

209

Porównujesz dwa adresy pamięci dla różnych ciągów, które są przechowywane w różnych lokalizacjach. Zasadniczo wygląda to następująco:

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

Użyj poniższego kodu, aby porównać dwie wartości ciągów:

#include <string.h>

...

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

Ponadto "a" == "a"może rzeczywiście zwrócić wartość true, w zależności od kompilatora, który może łączyć równe ciągi w czasie kompilacji w jeden, aby zaoszczędzić miejsce.

Kiedy porównujesz dwie wartości znakowe (które nie są wskaźnikami), jest to porównanie liczbowe. Na przykład:

'a' == 'a' // always true
Tim Cooper
źródło
12
GCC posiada również opcje -fmerge-constantsi -fno-merge-constantswłączyć / wyłączyć ciąg i zmiennoprzecinkowej stałej łączących w poprzek jednostek tłumaczeniowych, choć na niektórych GCCs wydaje się, że stała łączenie jest zawsze włączony, niezależnie od wybranej opcji.
Adam Rosenfield,
2
To zadziała, jeśli użyjesz „a” zamiast „a”. Pierwsza to znak, który w rzeczywistości jest wartością liczbową.
GolezTrol
@GolezTrol: w C dosłowne „a” ma inttyp. :-) Ponadto wskaźniki nie muszą być wartościami liczbowymi.
Bastien Léonard
intjest też numeryczny, prawda? Ale myślałem, że znaki to bajty. Int to 4 bajty. Same wskaźniki również są liczbami całkowitymi. Zawierają adres zbioru danych (danych, które rzeczywiście nie muszą być numeryczne).
GolezTrol
'a' == 'A' // not true... MySQL ma inne zdanie.
Steven
52

Trochę spóźniłem się na przyjęcie, ale i tak odpowiem; technicznie te same bity, ale z nieco innej perspektywy (język C poniżej):

W języku C wyrażenie "a"oznacza literał łańcuchowy , który jest statyczną nienazwaną tablicą o const chardługości dwóch - tablica składa się ze znaków 'a'i'\0' - kończący znak null sygnalizuje koniec ciągu.

Jednak w C w ten sam sposób nie można przekazywać tablic do funkcji według wartości - ani przypisywać do nich wartości ( po inicjalizacji ) - nie ma przeciążonego operatora ==dla tablic, więc nie jest możliwe ich bezpośrednie porównanie. Rozważać

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

Jeśli narzędzie ==nie porównuje tablic, to co właściwie robi? W C prawie we wszystkich kontekstach - w tym w tym - tablice rozpadają się na wskaźniki (które wskazują na pierwszy element tablicy) - a porównywanie wskaźników równości robi to, czego można się spodziewać. Tak skutecznie, kiedy to robisz

"a" == "a"

faktycznie porównujesz adresy pierwszych znaków w dwóch nienazwanych tablicach . Zgodnie ze standardem C, porównanie może dać prawdę lub fałsz (tj. 1 lub 0) - "a"s mogą w rzeczywistości oznaczać tę samą tablicę lub dwie zupełnie niepowiązane tablice. Z technicznego punktu widzenia wynikowa wartość jest nieokreślona , co oznacza, że ​​porównanie jest dozwolone (tj. Nie jest to niezdefiniowane zachowanie ani błąd składniowy), ale żadna z wartości jest prawidłowa, a implementacja (Twój kompilator) nie jest wymagana do udokumentowania tego, co faktycznie się wydarzy.

Jak zauważyli inni, aby porównać „łańcuchy c” (tj. Łańcuchy zakończone znakiem null), należy skorzystać z wygodnej funkcji strcmpznajdującej się w standardowym pliku nagłówkowym string.h. Funkcja zwraca wartość 0równych ciągów; za dobrą praktykę uważa się jawne porównywanie zwracanej wartości 0zamiast używania operatora `! ´, tj

strcmp(str1, str2) == 0 // instead of !strcmp(str1, str2)
eq-
źródło
47

Zgodnie z C99 (sekcja 6.4.5 / 6)

Literały strunowe

Nie jest określone, czy te tablice są różne, pod warunkiem, że ich elementy mają odpowiednie wartości .

W tym przypadku nie jest określone, czy oba "a"są różne. Zoptymalizowany kompilator może przechowywać pojedynczy plik "a"w lokalizacji tylko do odczytu i oba odwołania mogą się do tego odnosić.

Sprawdź wyjście na gcc tutaj

Prasoon Saurav
źródło
19

Ponieważ są to 2 oddzielne const char*wskaźniki, brak rzeczywistych wartości. Mówisz coś takiego, 0x019181217 == 0x0089178216co oczywiście zwraca NIE

Użyj strcmp()zamiast==

Antwan van Houdt
źródło
7
Literały łańcuchowe nie są wskaźnikami, są tablicami. Jednak w porównaniu rozpadają się do wskaźników.
GManNickG,
@Gman prawda, przepraszam, że nie jestem tego pewien, zwykle o tym zapominam :)
Antwan van Houdt
9

Mówiąc najprościej, C nie ma wbudowanego operatora porównania ciągów. W ten sposób nie może porównywać łańcuchów.

Zamiast tego, łańcuchy są porównywane przy użyciu standardowych procedur bibliotecznych, takich jak strcmp (), lub przez pisanie kodu w celu wykonania pętli przez każdy znak w ciągu.

W języku C ciąg tekstu w cudzysłowach zwraca wskaźnik do ciągu. Twój przykład to porównanie wskaźników i najwyraźniej twoje dwie wersje ciągu istnieją pod różnymi adresami.

Ale nie jest to porównywanie samych strun, jak można się spodziewać.

Jonathan Wood
źródło
3

Wskaźniki.

Pierwsza "a"to wskaźnik do łańcucha ASCII zakończonego znakiem null.

Drugi "a"to wskaźnik do innego łańcucha ASCII zakończonego znakiem null.

Jeśli używasz kompilatora 32-bitowego, spodziewałbym się "a"=="a"-4. Jednak właśnie wypróbowałem to z tcc / Win32 i otrzymałem "a"=="a"-2. No cóż...

Nico57
źródło
6
Dlaczego miałbyś oczekiwać, że łańcuchy będą wyrównane do 4-bajtowej granicy? Nie są intersami. 2 jest tym, czego bym się spodziewał (jeśli kompilator ich nie połączy), ponieważ każdy ciąg ma długość dwóch bajtów, łącznie z terminatorem null.
Siergiej Tachenov
Pewien stopień dopasowania może na przykład pozwolić strcmpna uruchamianie kilku bajtów jednocześnie. Niektórzy kompilatorzy to robią, niektórzy nie, niektórzy robią to tylko dla stringów dłuższych niż jakieś minimum ...
zwolnij
@Zack: skąd mieliby znać długość łańcucha, zanim faktycznie je porównają?
Joachim Sauer
Miałem na myśli, że niektóre kompilatory wyrównują ciągi dłuższe niż jakieś minimum.
zwol
1

Porównujesz dwa adresy pamięci, więc wynik nie zawsze będzie prawdziwy. Próbowałeś if('a' == 'a'){...}?

SK9
źródło
1

to pytanie wyznacza bardzo dobrą ścieżkę wyjaśnień dla wszystkich początkujących ...
pozwólcie, że się do tego przyczynię .....

jak wszyscy powyżej wyjaśniali, dlaczego otrzymujesz takie wyjście.

teraz, jeśli chcesz swojego prog. Aby wydrukować „tak równe”

albo użyj

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

}

lub
nie używaj „a” jako łańcuchów, użyj ich jako znaków ....

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

w znakach C to 1-bajtowa krótka liczba całkowita .......

N-JOY
źródło
Znaki zajmują tylko 1 bajt, ale literały znaków, takie jak 'a', są w rzeczywistości liczbami całkowitymi.
Spidey
0

Niektóre kompilatory mają opcję „merge strings”, której można użyć do wymuszenia, aby wszystkie stałe ciągi miały ten sam adres. Gdybyś tego użył, "a" == "a"byłby true.

Daniel Mošmondor
źródło
0

jeśli porównanie między znakami jest zawsze w pojedynczym cudzysłowie, np

if('a' == 'a')

a C nie obsługuje porównywania ciągów, takich jak "abc" == "abc"

Zrobione strcmp("abc","abc")

Bhavin Patel
źródło
-5

Ten facet nie używa zmiennych. Zamiast tego używa tymczasowo tablic tekstowych: ai a. Powód dlaczego

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

oczywiście nie działa, to że nie porównujesz zmiennych.
Jeśli tworzysz zmienne takie jak:

char * text = "a";
char * text2 = "a";

następnie można porównać textz text2, i powinno to być prawdą

Może nie powinieneś zapomnieć o użyciu {i }=)

void main() {
    if("a" == "a")
    {
      printf("Yes, equal");
    }
    else
    {
      printf("No, not equal");
    }
}
D. Ace
źródło
1
" i tak powinno być prawdą ” - Nie. Nie jest określone, czy literały łańcuchowe będą przechowywane w tej samej lokalizacji pamięci. Przeczytaj pozostałe odpowiedzi.
Spikatrix