2012-06-21 7 views
5

Wenn ein String-Literal mit einem anderen String-Literal mit dem Operator == (oder !=) verglichen wird, ist das Ergebnis gut definiert?C++ zwei String-Literale vergleichen

Zum Beispiel, sind die folgenden garantiert zu halten?

Bitte sagen Sie nicht Sachen wie "verwenden Sie std :: string" statt. Ich möchte nur diesen speziellen Fall wissen.

+0

Warum sollten Sie das tun? Für ein garantiert falsch, versuchen Sie 'assert (!" Nachricht geht hier ");' – chris

+1

@chris: Neugier für eins. Auch für eine enum-ähnliche Klassenimplementierungsidee. –

+0

mögliches Duplikat von [Ausgabedifferenz in gcc und turbo C] (http://stackoverflow.com/questions/3289354/output-difference-in-gcc-and-turbo-c) – kennytm

Antwort

15
"a" == "a" 

Dieser Ausdruck true oder false ergeben kann; Es gibt keine Garantien. Die beiden Zeichenfolgenliterale "a" können den gleichen Speicher belegen oder sie können an zwei verschiedenen Speicherorten vorhanden sein.

Ich denke, dass die nächste Sprache in der C++ - Standard ist: "Ob alle Zeichenfolgenliterale unterschiedlich sind (dh in nonoverlapping Objekte gespeichert sind) ist die Implementierung definiert" (C++ 11 §2.14.5/12). Es gibt keine weiteren Anforderungen oder Einschränkungen, daher bleibt das Ergebnis unbestimmt.

"a" != "b" 

Dieser Ausdruck muss false ergeben, weil es keine Möglichkeit gibt, dass diese beiden Stringliterale die gleiche Stelle im Speicher belegen kann: "a"[0] != "b"[0].

Wenn Sie String-Literale auf diese Weise vergleichen, vergleichen Sie die Zeiger tatsächlich mit den Anfangselementen in den Arrays.

Weil wir Zeiger, die relationalen Vergleiche zu vergleichen (<, >, <= und >=) sind noch problematischer als die Gleichheitsvergleiche (== und !=), da nur eine begrenzte Menge von Zeigern Vergleichen mit dem relationalen durchgeführt werden kann Vergleiche. Zwei Zeiger können nur dann verglichen werden, wenn sie beide Zeiger in dasselbe Array oder Zeiger in dasselbe Objekt sind.

Wenn die beiden "a" Stringliterale die gleiche Stelle im Speicher einnehmen, dann würde "a" < "a" sein gut definiert und false ergeben würde, weil beiden Zeiger auf das Anfangselement Punkt ('a') desselben Arrays.

Wenn jedoch die beiden "a" Stringliterale verschiedene Stellen im Speicher belegen, ist das Ergebnis "a" < "a" undefiniert, weil die beiden Zeiger Punkt in völlig unabhängige Objekte verglichen werden.

Da "a" und "b" niemals die gleiche Position im Speicher belegen können, hat "a" < "b" immer undefiniertes Verhalten. Das gleiche gilt für die anderen relationalen Vergleichsoperatoren.

Wenn Sie aus irgendeinem Grund zwei String-Literale relational vergleichen möchten und gut definierte Ergebnisse haben, können Sie den std::less Comparer verwenden, der eine strikt-schwache Reihenfolge über alle Zeiger bietet. Es gibt auch std::greater, std::greater_equal und Vergleiche.Angesichts der Tatsache, dass String-Literale mit den gleichen Inhalten nicht gleichwertig sind, weiß ich nicht, warum man das jemals tun möchte, aber Sie können.

+0

Ich denke, es wäre schön, wenn Sie auch weniger als Vergleiche diskutieren könnten, die weniger gut definiert sind, und 'std :: øess' & friends –

+0

@ Cheersandthth.-Alf: Gute Idee; hinzugefügt. Ich weiß auch nicht, ob ich das erwähnt habe, aber ich mag es, wie Sie "Cheers and hth" in Ihren Namen integriert haben, um die gruseligeren Stack Overflow-Mitwirkenden zu unterstützen. ;-) –

1

Die Idee ist, dass in C++ String-Literale Arrays sind. Da für Arrays keine Vergleichsoperatoren definiert sind, werden sie mit der nächstbesten Anpassung verglichen - dem Zeigervergleichsoperator, da Arrays implizit zu Zeigern zerfallen, so dass jeder Vergleich die Adresse und nicht den Inhalt vergleicht. Da "a" und "b" nicht am gleichen Speicherort sein können, ist "a"! = "B" eine wahre Behauptung. Es bildet auch eine gültige statische Behauptung. Eine solche Garantie kann nicht für "a" == "a" gegeben werden, obwohl GCC mit-fmerge-Konstanten (impliziert bei -O1) eine einigermaßen starke Wahrscheinlichkeit und -fmerge-all-constants eine Garantie geben kann (die möglicherweise führt zu nichtkonformem Verhalten).

Wenn Sie einen inhaltlichen Vergleich wünschen, können Sie immer assert(!strcmp("a", "a")) verwenden. Oder Sie können eine Art von constexpr basierend strcmp für eine statische Behauptung verwenden:

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... 

Dann kompilieren mit g ++ -std = C++ 0x (oder -std = C++ 11 für gcc> = 4.7) und ...

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