Warum gibt es jetzt einen Unterschied zwischen “{static const char a[]={…}” und “{const char a[]={…}”?

Lesezeit: 5 Minuten

Benutzer-Avatar
John Carter

Schauen Sie sich diesen kleinen Ausschnitt an C-Code oder C++-Code auf gottbolzen…

void b( char const *c);

void a(void)
{
   char const z[] = {0xd, 0xe, 0xa, 0xd, 0xb, 0xe, 0xe, 0xf, 0xa};

   b(z);
}

void c(void)
{
   static char const z[] = {0xd, 0xe, 0xa, 0xd, 0xb, 0xe, 0xe, 0xf, 0xa};

   b(z);
}

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?

    – 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[],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

    – zufällige Bewertung

    30. Oktober 2018 um 5:04 Uhr

C++ hat folgendes Regel:

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.

Sehen Sie sich jetzt diesen Code an:

#include <stdio.h>

void c();

void b(const char *a) {
    static const char *p = 0;

    if (!p) {
        p = a;
        c();
    } else {
        if (a==p) {
            printf("problem!\n");
        }
    }
}

void c() {
    const char a[] = { 0xd, 0xe, 0xa, 0xd, 0xb, 0xe, 0xe, 0xf };

    b(a);
}

int main() {
    c();
}

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

Benutzer-Avatar
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

1362930cookie-checkWarum gibt es jetzt einen Unterschied zwischen “{static const char a[]={…}” und “{const char a[]={…}”?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy