Ich kann zwei 'some'
Literale im Assembler-Code sehen, der von MSVC generiert wurde, aber nur eines mit clang und gcc. Dies führt zu völlig unterschiedlichen Ergebnissen der Codeausführung.
static const char *A = "some";
static const char *B = "some";
void f() {
if (A == B) {
throw "Hello, string merging!";
}
}
Kann jemand den Unterschied und die Ähnlichkeiten zwischen diesen Kompilierungsausgaben erklären? Warum optimiert clang / gcc etwas, auch wenn keine Optimierungen angefordert werden? Ist das eine Art undefiniertes Verhalten?
Ich stelle auch fest, dass clang / gcc / msvc, wenn ich die Deklarationen in die unten gezeigten ändere, überhaupt keine "some"
im Assembler-Code belässt . Warum ist das Verhalten anders?
static const char A[] = "some";
static const char B[] = "some";
c++
language-lawyer
string-literals
string-interning
Eugene Kosov
quelle
quelle
Antworten:
Dies ist kein undefiniertes Verhalten, sondern ein nicht spezifiziertes Verhalten. Für Stringliterale ,
Das heißt, das Ergebnis von
A == B
könnte seintrue
oderfalse
, von dem Sie sich nicht verlassen sollten.Aus dem Standard [lex.string] / 16 :
quelle
Die anderen Antworten erklärten, warum Sie nicht erwarten können, dass die Zeigeradressen unterschiedlich sind. Sie können dies jedoch leicht so umschreiben, dass dies garantiert
A
undB
nicht gleich ist:Der Unterschied ist , daß
A
undB
sind nun Arrays von Zeichen. Dies bedeutet, dass sie keine Zeiger sind und ihre Adressen genau so unterschiedlich sein müssen wie die von zwei ganzzahligen Variablen. C ++ verwirrt dies, weil es Zeiger und Arrays austauschbar erscheinen lässt (operator*
undoperator[]
sich gleich zu verhalten scheint), aber sie sind wirklich unterschiedlich. ZB ist so etwasconst char *A = "foo"; A++;
vollkommen legal, ist es aberconst char A[] = "bar"; A++;
nicht.Eine Möglichkeit, über den Unterschied nachzudenken, besteht darin,
char A[] = "..."
dass "Geben Sie mir einen Speicherblock und füllen Sie ihn mit den Zeichen,...
gefolgt von\0
", währendchar *A= "..."
"Geben Sie mir eine Adresse, an der ich die Zeichen finden kann, denen...
gefolgt wird\0
".quelle
*p
undp[0]
nicht nur „scheint dasselbe zu verhalten“ , aber per Definition ist identisch (vorausgesetzt , dassp+0 == p
eine Identitätsbeziehung ist , weil0
das neutrale Element in Zeiger-Ganzzahl - Addition). Immerhinp[i]
ist definiert als*(p+i)
. Die Antwort macht jedoch einen guten Punkt.typeof(*p)
undtypeof(p[0])
sind beidechar
so, dass es wirklich nicht mehr viel gibt, was anders sein könnte. Ich stimme zu, dass "sich anscheinend gleich zu verhalten scheint" nicht der beste Wortlaut ist, weil die Semantik so unterschiedlich ist. Ihr Beitrag erinnerte mich an dem besten Weg , um Zugang Elemente von C ++ Arrays:0[p]
,1[p]
,2[p]
etc. Dies ist , wie die Profis tun es, zumindest wenn sie wollen die Menschen verwirren , die nach der Programmiersprache C geboren wurden.Ob ein Compiler denselben String-Speicherort für
A
und verwendet,B
hängt von der Implementierung ab. Formal können Sie sagen, dass das Verhalten Ihres Codes nicht spezifiziert ist .Beide Optionen implementieren den C ++ - Standard korrekt.
quelle
Es ist eine Optimierung, um Platz zu sparen, die oft als "String-Pooling" bezeichnet wird. Hier sind die Dokumente für MSVC:
https://msdn.microsoft.com/en-us/library/s0s0asdt.aspx
Wenn Sie der Befehlszeile / GF hinzufügen, sollte das gleiche Verhalten bei MSVC auftreten.
Übrigens sollten Sie Zeichenfolgen wahrscheinlich nicht über solche Zeiger vergleichen. Jedes anständige statische Analysetool kennzeichnet diesen Code als fehlerhaft. Sie müssen vergleichen, auf was sie zeigen, nicht die tatsächlichen Zeigerwerte.
quelle