2012-06-21 27 views
5

Podczas porównywania literału łańcuchowego z innym literałem ciągu z operatorem == (lub !=), czy wynik jest dobrze zdefiniowany?C++ porównać dwa ciągi literałów

Na przykład, czy zagwarantowane jest trzymanie następujących elementów?

assert("a" == "a"); 
assert("a" != "b"); 

Proszę nie mówić takich rzeczy jak "use std :: string". Chcę tylko poznać ten konkretny przypadek.

+0

Dlaczego miałbyś to zrobić? Aby uzyskać gwarantowaną wartość false, spróbuj 'assert (!" Wiadomość idzie tutaj ");' – chris

+1

@chris: Ciekawość dla jednego. Także w przypadku pomysłu implementacji klasy podobnej do enum. –

+0

możliwy duplikat [Różnica wyjściowa w gcc i turbo C] (http://stackoverflow.com/questions/3289354/output-difference-in-gcc-and-turbo-c) – kennytm

Odpowiedz

15
"a" == "a" 

Wyrażenie to może przynieść true lub false; nie ma żadnych gwarancji. Dwa literały literowe "a" mogą zajmować to samo miejsce lub mogą istnieć w dwóch różnych miejscach w pamięci.

Uważam, że najbliższym językiem w standardzie C++ jest: "Czy wszystkie ciągi znaków są odrębne (to znaczy są przechowywane w nienakładających się obiektach), to implementacja jest zdefiniowana" (C++ 11 §2.14.5/12). Nie ma żadnych innych wymagań ani ograniczeń, więc wynik pozostaje nieokreślony.

"a" != "b" 

Wyrażenie to musi ustąpić false ponieważ nie ma możliwości, że te dwa literały łańcuchowe mogą zajmować tę samą lokalizację w pamięci: "a"[0] != "b"[0].


Porównując literały łańcuchowe w ten sposób, faktycznie porównujesz wskaźniki do początkowych elementów w tablicach.

Ponieważ jesteśmy porównując wskaźniki, relacyjnych porównań (<, >, <= i >=) są jeszcze bardziej problematyczne niż porównań równości (== i !=), ponieważ tylko ograniczony zestaw porównań wskaźnik może być wykonywane przy użyciu relacyjnej porównania. Dwa wskaźniki mogą być porównywane tylko wtedy, gdy oba wskaźniki znajdują się w tej samej tablicy lub wskaźniki w tym samym obiekcie.

Jeżeli oba "a" literałami łańcuch zajmują to samo położenie w pamięci, a następnie "a" < "a" będzie dobrze zdefiniowany i przyniesie false, ponieważ oba wskaźniki wskazują elementu początkowego ('a') w tym samym układzie.

Jednakże, jeśli obie "a" literały ciągów znaków zajmują różne miejsca w pamięci, wynik "a" < "a" jest nieokreślona, ​​ponieważ oba wskaźniki są całkowicie niepowiązanych do punktu obiektów porównywane.

Ponieważ "a" i "b" nigdy nie zajmują tej samej lokalizacji w pamięci, zawsze ma niezdefiniowane zachowanie. To samo dotyczy innych operatorów porównań relacyjnych.

Jeśli z jakiegoś powodu chcesz porównać ze sobą dwa ciągi literałów i uzyskasz dobrze zdefiniowane wyniki, możesz użyć porównywalnika std::less, który zapewnia ścisłe i słabe porządkowanie wszystkich wskaźników. Istnieją również porównywarki std::greater, std::greater_equal i std::less_equal.Biorąc pod uwagę, że literały łańcuchowe o tej samej zawartości mogą nie porównywać równości, nie wiem, dlaczego ktoś chciałby to zrobić, ale można.

+0

Myślę, że byłoby miło, gdybyś mógł również omówić mniej niż porównania, które są mniej zdefiniowane, i 'std :: øess' i przyjaciele –

+0

@ Cheersandhth.-Alf: Dobry pomysł; dodany. Nie wiem też, czy o tym wspomniałem, ale podoba mi się to, w jaki sposób włączyłeś "Wiwaty i hth" do swojego nazwiska, aby obejść najbardziej zrzędliwych współpracowników Stack Overflow. ;-) –

1

Chodzi o to, że w literałach napisanych w C++ są tablice. Ponieważ tablice nie mają operatorów porównania zdefiniowanych dla nich, są porównywane za pomocą następnego najlepszego dopasowania - operatora porównania wskaźnika, ponieważ tablice będą domyślnie zanikać do wskaźników, więc każde porównanie porównuje adres, a nie treść. Ponieważ "a" i "b" nie mogą znajdować się w tej samej lokalizacji pamięci, "a"! = "B" jest prawdziwym stwierdzeniem. Tworzy również poprawną asercję statyczną. Żadna taka gwarancja nie może być wykonana na "a" == "a", chociaż GCC ze -fmerge-stałymi (domniemana przy -O1) może mieć dość duże prawdopodobieństwo i -fmerge-all-constant może dać ci gwarancję (że potencjalnie powoduje niezgodne zachowanie).

Jeśli chcesz porównać treści, zawsze możesz użyć numeru assert(!strcmp("a", "a")). Lub można użyć jakiegoś strcmp oparciu constexpr dla statycznego twierdzenie:

constexpr bool static_strequal_helper(const char * a, const char * b, unsigned len) { 
    return (len == 0) ? true : ((*a == *b) ? static_strequal_helper(a + 1, b + 1, len - 1) : false); 
} 

template <unsigned N1, unsigned N2> 
constexpr bool static_strequal(const char (&str1)[N1], const char (&str2)[N2]) { 
    return (N1 == N2) ? static_strequal_helper(&(str1[0]), &(str2[0]), N1) : false; 
} 

static_assert(static_strequal("asdf", "asdf"), "no error - strings are equal"); 
static_assert(static_strequal("asdf", "jkl;"), "strings are not equal"); 
assert(!strcmp("asdf", "jkl;")); //no compile error - runtime error 
//cannot use strcmp in static assert as strcmp is not constexpr... 

Następnie skompilować z g ++ -std = C++ 0x (lub -std = C++ 11 dla gcc> = 4.7) i ...

error: static assertion failed: "strings are not equal"