Frühere Versionen von gcc kompilieren sowohl a() als auch c() in zwei Anweisungen, Adresse von z laden, zu b springen.
Alle modernen Compiler Ich habe versucht, a() zu “pessimisieren”, um “Stapelrahmen zu erstellen, z auf den Stapel zu kopieren, b aufzurufen, Stapelrahmen herunterzureißen, aber c() als die einfache Version mit zwei Anweisungen zu belassen.
In der Tat hat sich nichts geändert, in der Praxis sind moderne Compiler für diesen Anwendungsfall jetzt langsamer…..
Hat jemand eine Ahnung warum?
Notiz: c()‘s b(z) übergibt einen Zeiger auf ein Array, das lange danach gültig ist b() ist komplett. Nicht so mit a()‘s b(z). Neugierig, fügt hinzu restrict zu void b(char const * restrict c); Sachen ändern?
static char const z[],meint z ist statisch. Statische Variable muss im Datenabschnitt sein. Der Compiler verwendet also zwei Anweisungen. Zum char const z[], z ist eine konstante Variable, ich denke, der Compiler kann sie in den Datenabschnitt oder nur in den Stapel legen. Ich denke, das ist nur die Wahl des Compilers, aber ich bin mir da nicht wirklich sicher. Wenn verwenden char z[]dann wird kompilieren z nur in den Stapel legen, dann hat die Funktion a mehr Anweisungen.
– zufällige Bewertung
30. Oktober 2018 um 4:15 Uhr
Das Interessante ist, dass gcc verwendet wird, um die Wahl zu treffen, es einfach im Datenabschnitt zu belassen … und dann geändert wird, um es langsamer zu machen. Darüber hinaus verwenden alle anderen neueren Compiler, die ich auf Godbolt ausprobiert habe, auch die langsamere Version. Dies deutet darauf hin, dass es einen guten Grund gibt … Leider kann ich mir nicht vorstellen, was es ist.
– John Carter
30. Oktober 2018 um 4:21 Uhr
Ich habe gesehen, dass Sie die Kompilieroption ‘-Os’ verwenden. Ich versuche den Clang-Compiler mit den Compiler-Optionen ‘-std=c99’. Bei unterschiedlichen Clang-Versionen sind die Anweisungen unterschiedlich, aber anscheinend setzen sie alle Array z in den Datenabschnitt. klirren mit std
Sofern ein Objekt kein Bitfeld oder Unterobjekt der Größe Null ist, ist die Adresse dieses Objekts die Adresse des ersten Bytes, das es belegt. Zwei Objekte mit überlappender Lebensdauer die keine Bitfelder sind, können dieselbe Adresse haben, wenn eines in dem anderen verschachtelt ist oder wenn mindestens eines ein Unterobjekt der Größe Null ist und sie von unterschiedlichem Typ sind; ansonsten sie unterschiedliche Adressen haben und disjunkte Speicherbytes belegen.
Hier, c wird einmal rekursiv aufgerufen, also nach der Regel das Array a sollten in jeder Rekursionsebene unterschiedliche Adressen haben. b Shops a Beim ersten Aufruf und beim zweiten Aufruf prüft es, ob es dasselbe ist oder nicht. Mit einem konformen Compiler sollte kein “Problem!” ausgegeben werden. Aber tatsächlich gibt es bei einem alten Compiler (GCC 4.1, Clang 6.0) “Problem!” aus, also verletzen diese Compiler den Standard.
Ein Compiler darf machen a statisch nur in dem Fall, dass nachgewiesen werden kann, dass diese Änderung nicht der Fall ist beobachtbar:
Unter der „Als-ob“-Regel ist es einer Implementierung erlaubt, zwei Objekte an derselben Maschinenadresse zu speichern oder ein Objekt überhaupt nicht zu speichern, wenn das Programm den Unterschied nicht erkennen kann
Ich kann nur einmal positiv abstimmen, aber dies ist im Wesentlichen dasselbe Beispiel b Funktion, an die ich dachte, und eine nette Erklärung, die zugänglich und technisch korrekt balanciert.
– Aschepler
30. Oktober 2018 um 12:13 Uhr
Der C-Standard ist diesbezüglich nicht ganz so explizit, aber ich denke, dass die gleiche Anforderung von 6.2.4/6 (Nicht-VLA) und 6.2.4/7 (VLA) impliziert wird: “Wenn die [block/scope] rekursiv eingegeben wird, wird jedes Mal eine neue Instanz des Objekts erstellt.”
– Aschepler
30. Oktober 2018 um 12:19 Uhr
Nun, vielleicht möchten Sie die eine Ausnahme erwähnen: Alle String-Literale können unabhängig davon Leerzeichen teilen.
– Deduplizierer
30. Oktober 2018 um 12:22 Uhr
@Deduplicator Jedes angegebene String-Literal ist ein statisches Objekt. Ob mehrere Zeichenfolgenliterale mit demselben Wert unterschiedliche Objekte sind, ist nicht angegeben
– Caleth
30. Oktober 2018 um 12:35 Uhr
@Caleth Das würde bedeuten, dass, wenn ein String-Literal ein richtiges Suffix eines anderen ist, sie keinen Platz teilen könnten. Es gibt keine solche Einschränkung.
– Deduplizierer
30. Oktober 2018 um 13:17 Uhr
dascandy
Ich erwarte, dass die Antwort lautet, dass der Compiler das tut, was Sie in Ihrem Code angeben. Es muss ein funktionslokales Array mit automatischem Speicher vorhanden sein, das nicht mit anderen Threads geteilt wird, das an andere Funktionen übergeben werden soll. Zuvor konnte der Compiler die Als-ob-Regel verwenden, um dies zu entfernen und an anderer Stelle zu platzieren, da die Sprache keine Threads als ein Ding hatte, das in ihrem Modell vorhanden war, aber da Threads jetzt vorhanden sind, muss er sicherstellen, dass er nicht versehentlich false verursacht mit anderen teilen. Es hätte es wahrscheinlich Thread-lokal machen können, aber das ist schlimmer, als nur lokal zu funktionieren.
Beachten Sie, dass GCC die Optimierung nie durchgeführt hat, Clang jedoch nach 6.0.0 damit aufgehört hat. Es könnte sogar ein Clang-Fehler sein, diese Optimierung verwendet zu haben.
Aber das Array ist constalso kann es keine Racebedingung geben.
– QUentin
30. Oktober 2018 um 9:44 Uhr
13629300cookie-checkWarum gibt es jetzt einen Unterschied zwischen “{static const char a[]={…}” und “{const char a[]={…}”?yes
Notiz:
c()
‘sb(z)
übergibt einen Zeiger auf ein Array, das lange danach gültig istb()
ist komplett. Nicht so mita()
‘sb(z)
. Neugierig, fügt hinzurestrict
zuvoid b(char const * restrict c);
Sachen ändern?– chux – Wiedereinsetzung von Monica
30. Oktober 2018 um 3:24 Uhr
Nö. gcc.godbolt.org/z/cB5-w7
– John Carter
30. Oktober 2018 um 3:46 Uhr
static char const z[]
,meintz
ist statisch. Statische Variable muss im Datenabschnitt sein. Der Compiler verwendet also zwei Anweisungen. Zumchar const z[]
, z ist eine konstante Variable, ich denke, der Compiler kann sie in den Datenabschnitt oder nur in den Stapel legen. Ich denke, das ist nur die Wahl des Compilers, aber ich bin mir da nicht wirklich sicher. Wenn verwendenchar z[]
dann wird kompilieren z nur in den Stapel legen, dann hat die Funktion a mehr Anweisungen.– zufällige Bewertung
30. Oktober 2018 um 4:15 Uhr
Das Interessante ist, dass gcc verwendet wird, um die Wahl zu treffen, es einfach im Datenabschnitt zu belassen … und dann geändert wird, um es langsamer zu machen. Darüber hinaus verwenden alle anderen neueren Compiler, die ich auf Godbolt ausprobiert habe, auch die langsamere Version. Dies deutet darauf hin, dass es einen guten Grund gibt … Leider kann ich mir nicht vorstellen, was es ist.
– John Carter
30. Oktober 2018 um 4:21 Uhr
Ich habe gesehen, dass Sie die Kompilieroption ‘-Os’ verwenden. Ich versuche den Clang-Compiler mit den Compiler-Optionen ‘-std=c99’. Bei unterschiedlichen Clang-Versionen sind die Anweisungen unterschiedlich, aber anscheinend setzen sie alle Array z in den Datenabschnitt. klirren mit std
– zufällige Bewertung
30. Oktober 2018 um 5:04 Uhr