In Schleife eine Adresse weiter rauszählen, als vorhanden



  • Ist sowas in Ordnung, und gängige Praxis, oder kann mir dass irgendwie den Code um die Ohrenhauen? Ich hab ja immer gedacht, so lang man die jeweilige Adresse nicht dereferenziert, passiert nix, man liest sie ja nur?!

    int a[4] = {0};
    
        for(int *p = a; p != a+4; ++p)
        {
            cout << *p << endl;
        }
    

    MfG
    Stromberg



  • Stromberg* schrieb:

    Ist sowas in Ordnung, und gängige Praxis, oder kann mir dass irgendwie den Code um die Ohrenhauen?

    Das ist okay. Gängig ist allerdings eher die Verwendung von operator[] .

    Stromberg* schrieb:

    Ich hab ja immer gedacht, so lang man die jeweilige Adresse nicht dereferenziert, passiert nix, man liest sie ja nur?!

    Du dereferenzierst sehr wohl, schliesslich hast du einen operator* drin. Passieren kann allerdings rein gar nichts. Das Array ist sogar initialisiert. Aber selbst wenn es nicht initialisiert wäre, könnte nichts passieren, du hättest dann einfach willkürliche Werte drin.

    Edit: Ah, ich hab den Post noch einmal gelesen. Wahrscheinlich machst du dir wegen des a+4 Sorgen. Auch diese sind unbegründet; wie du schon richtig sagst, liest du die Adresse nur.



  • Vll. verwechsel ich dass jetzt mit sowas:

    delete p;
    cout << *p << endl;
    

    Dass ist doch definitiv böse?
    Was für Situationen gibts den noch, in denen falsche Zeigerhandhabung, dass danze Programm crashen lästt?

    MfG
    Stromberg



  • Es ist allerdings nur für das Element hinter dem letzten OK.
    Wenn du den Zeiger weiter raufzählst ist es UB. (Auch wenn du den Zeiger nicht dereferenzierst! Und auch wenn es mit den allermeisten Compilern/Implementierungen überhaupt kein Problem macht.)



  • Ich hab den oberen Beitrag noch editiert.

    Grundsätzlich ist das Dereferenzieren von ungültigen Zeigern böse. Ungültig ist ein Zeiger, wenn er nicht initialisiert wurde, wenn das verwiesene Objekt nicht mehr existiert, wenn man es sonst irgendwie schafft, den Zeiger zu verbiegen (eher selten) oder wenn der Zeiger Null ist (da erhält man wenigstens einen richtigen Laufzeitfehler).

    Das Böse daran ist, dass es nicht einmal crashen muss, sondern undefiniertes Verhalten hervorruft. Das Programm kann normal weiterlaufen, in einer Fehlermeldung enden, es können irgendwelche Daten oder sogar (Rück-)Sprungadressen überschrieben werden, es kann auch gar nichts passieren...



  • @Nexus: selbst der Zugriff auf einen nicht-initialisierten Zeiger ist UB. Also nicht Dereferenzieren, sondern bloss "a = b", wenn b ein nicht-initialisierter Zeiger ist, ist UB. Das einzige was man mit einem nicht-initialisierten (oder sonstwie ungültigen) Zeiger machen darf, ist ihn mit einem gültigen Wert zu überschreiben. Also nochmal ganz klar:

    char* a; // OK soweit
    char* b; // ""
    b = a; // <-- UB, weil a nicht initialisiert ist. und zwar ganz egal was wir mit a oder b später noch anstellen (oder nicht anstellen)
    

    Die Ausnahme für das "one past last" Element von z.B. Arrays gibt es im Prinzip nur, damit man bei den in der Std. Library üblichen halboffenen Intervallen ("[begin, end)") nicht dauernd Probleme bekommt. Sonst könnte man z.B. ja nichtmal ein Array mit std::fill füllen oder mit std::copy kopieren.

    p.S.: die Einschränkung, dass man ungültige Zeiger nichtmal rumreichen darf, sondern eben nur überschreiben, gibt es, um z.B. Implementierungen zu ermöglichen, auf Systemen, wo ungültige Zeiger nichtmal in ein Register geladen werden können. Wenn z.B. ein Zeiger aus einem Segment-Descriptor + Offset besteht, und die CPU beim Laden des Zeigers in ein Register prüft, ob der Segment-Descriptor gültig ist, dann würde man Probleme bekommen.



  • Also "a+4" ist erlaubt, aber mehr als ein Feld drüber gehen darf man nicht? Und man darf es vorallem nicht dereferenzieren!?
    Kann ich mir dass einfach so vorstellen wie den "end-Iterator"?

    MfG
    Stromberg



  • hustbaer, vielen Dank für die Erklärung, das war mir bisher nicht bewusst. Ich dachte, Zeiger würden sich diesbezüglich wie andere Objekte verhalten, und sofern man für sie genügend Platz bereitgestellt hätte, dürfe man sie auch lesen (aber nicht dereferenzieren).

    Gut, in der Praxis kommt es eigentlich eh nie vor, dass man uninitialisierte Zeiger herumreicht, deswegen ist das Undefined Behaviour nicht sehr schlimm. Aber dennoch gut zu wissen... 😉

    Stromberg*, hustbaer hat ja bereits alles dazu gesagt...



  • Gerade noch eine Frage dazu: Gilt die erwähnte Ausnahme auch für das Element vor dem Array (analog zu rend() bei Iteratoren?)



  • Nexus schrieb:

    Gerade noch eine Frage dazu: Gilt die erwähnte Ausnahme auch für das Element vor dem Array (analog zu rend() bei Iteratoren?)

    Weiss ich nicht, glauber aber eher nicht.
    Lies im Standard nach 😉


Anmelden zum Antworten