(lesender) Zugriff auf dafault-initialized Array Members UB?


  • Mod

    hustbaer schrieb:

    camper schrieb:

    int foo()
    {
        char x[10];
        char y[10];
        mymemcpy(y,x,10); // definiert oder UB?
    }
    

    das soll wahrscheinlich wohldefiniert sein, allerdings wird es jedenfalls nicht ausdrücklich im Standard erlaubt.

    Das ist natürlich nicht erlaubt. Hast du vielleicht ein = {} bei char x[10] vergessen?

    Nichts vergessen. Und wir haben

    n3337 3.9/2 schrieb:

    For any object (other than a base-class subobject) of trivially copyable type T, whether or not the object
    holds a valid value of type T, the underlying bytes (1.7) making up the object can be copied into an array of char or unsigned char.40[/quote

    Dies Passage ist m.A.n. die Lizenz, den Zustand jedes beliebigen trivial kopierbaren Objektes untersuchen zu dürfen. In diesem Fall (char/unsigned char) ist nun Objekt- und Wertrepräsentation identisch, so dass beim Kopieren

    *dst_++ = *src_++;
    

    einerseits der Wert des Objektes als auch die zugrundeliegenden Bytes kopiert werden.



  • Uff. Hab ich wiedermanl Mist geschrieben.
    Danke für die Erklärung!

    Also so wie ich das jetzt verstehe:

    void foo()
    {
        int a, b, c, d;
    
        memcpy(&b, &a, sizeof(a));   // OK
        mymemcpy(&c, &a, sizeof(a)); // OK
        d = a;                       // UB
    }
    

  • Mod

    hustbaer schrieb:

    Uff. Hab ich wiedermanl Mist geschrieben.
    Danke für die Erklärung!

    Also so wie ich das jetzt verstehe:

    void foo()
    {
        int a, b, c, d;
    
        memcpy(&b, &a, sizeof(a));   // OK
        mymemcpy(&c, &a, sizeof(a)); // OK
        d = a;                       // UB
    }
    

    Ist jedenfalls meine Ansicht. Ausserdem

    struct foo { int x; };
    void bar()
    {
        foo bar;
        foo bar = baz; // ok
        cout << bar.x; // UB
        cout << baz.x; // ??? wahrscheinlich ok
    }
    

    Noch spannender wird das, wenn man den Copyctor selbst definiert.



  • Hm...
    Ich hätte gesagt

    void bar()
    {
        foo bar;
        foo baz = bar; // keine Ahnung
        cout << bar.x; // UB
        cout << baz.x; // UB
    }
    


  • Von Konstruktion zu sprechen, ist hier also schon mal fehl am Platz.

    Da meinte ich eher, dass das Objekt, hier ein Skalar, in einem gültigen (=benutzbaren) Zustand ist.
    Ich traue aber meinen Augen nicht, wenn ich lese,

    int a, b = a;
    

    Soll UB sein. Nur da der Wert nicht spezifiziert ist, heißt das doch nicht, dass man ihn nicht benutzen darf...

    Dies Passage ist m.A.n. die Lizenz, den Zustand jedes beliebigen trivial kopierbaren Objektes untersuchen zu dürfen.

    Aber ein Array ist doch kein Objekt.



  • foo baz = bar; // keine Ahnung
    

    Wohldefiniertes Verhalten, da ein Kopierkonstruktor implizit definiert wurde. Und bar ist zu dem Zeitpunkt ein lvalue das auf ein vollständig konstruiertes Objekt verweist. Wo könnte da auch nur annähernd ein Problem sein?


  • Mod

    Sone schrieb:

    Nur da der Wert nicht spezifiziert ist,

    ist er nicht.
    Der Wert ist unbestimmt (indeterminate), was etwas völlig anderes als unspezifiziert darstellt, an anderer Stelle ist im Standard von singulären Iteratoren und ungültigen Zeigern die Rede, was im Grunde das gleiche Konzept ist. Die dürfen auch nicht angefasst werden.



  • camper schrieb:

    Sone schrieb:

    Nur da der Wert nicht spezifiziert ist,

    ist er nicht.
    Der Wert ist unbestimmt (indeterminate), was etwas völlig anderes als unspezifiziert darstellt, an anderer Stelle ist im Standard von singulären Iteratoren und ungültigen Zeigern die Rede, was im Grunde das gleiche Konzept ist. Die dürfen auch nicht angefasst werden.

    Ich sehe unspezifiziert als

    Certain other aspects and operations of the abstract machine are described in this International Standard as unspecified (for example, order of evaluation of arguments to a function). Where possible, this International Standard defines a set of allowable behaviors. These define the nondeterministic aspects of the abstract machine. An instance of the abstract machine can thus have more than one possible execution for a given program and a given input

    was hier auch zutrifft, ausgehend von dem Intervall möglicher Werte kann diese Variable nur X verschiedene Zustände annehmen.

    Wobei du natürlich Recht hast, ich darf die Begriffe die ja explizit im Standard definiert und verwendet werden nicht mischen.

    Trotzdem ist es doch für die Natur der Sprache unerheblich, ob bei einer Zuweisung a = b der rechte Operand einen unbestimmten Wert hat oder bspw. einen vom Programmierer zufällig ausgedachten. Er hat einen, also wird a auch einen haben.


  • Mod

    Sone schrieb:

    ausgehend von dem Intervall möglicher Werte kann diese Variable nur X verschiedene Zustände annehmen.

    Das mag so sein. Nur ist es eben nicht möglich, diesen Zustand (direkt - d.h. auslesen über l-zu-rvalue-Umwandlung) zu ermitteln.
    Anders gesagt: Der Wert den ein Compiler in diesem Fall liefert, muss nichts mit dem tatsächlich am Ort der Variablen gespeicherten Wert (wie er sich aus den dort befindlichen Bytes ergeben würde) zu tun haben und er muss nat. auch nicht gleich bleiben, wenn man ihn ein Zweites mal liest. Klassisch undefiniertes Verhalten.



  • Der Wert den ein Compiler in diesem Fall liefert, muss nichts mit dem tatsächlich am Ort der Variablen gespeicherten Wert (wie er sich aus den dort befindlichen Bytes ergeben würde) zu tun haben und er muss nat. auch nicht gleich bleiben, wenn man ihn ein Zweites mal liest. Klassisch undefiniertes Verhalten.

    Ahh... das heißt, bei jeder lvalue-to-rvalue Umwandlung wird ein undeterministischer Wert zurückgegeben? Ich habe es so verstanden, das die Variable einen unbestimmten aber festen Wert hat.



  • Sone schrieb:

    Ahh... das heißt, bei jeder lvalue-to-rvalue Umwandlung wird ein undeterministischer Wert zurückgegeben? Ich habe es so verstanden, das die Variable einen unbestimmten aber festen Wert hat.

    Wozu die ganze Diskussion? Alles wurde schon mit dem dritten Post geklärt. Fettgedruckter Text: "Lvalue-to-rvalue conversion" "if the object is uninitialized" "undefined behavior". Schwer zu glauben, wie man das missverstehen kann. Ausser natürlich, du verstehst "undefined behaviour" nicht, dann frag aber bitte direkt nach.



  • Erkläre mir mal folgendes:

    foo bar = baz; // ok
    

    Wieso ist das Ok, obwohl beim unterliegenden kopieren der Member auch das uninitialisierte x einem anderen zugewiesen wird (was doch eigentlich eine lvalue-to-rvalue conversion beinhaltet), und laut unserer Diskussion UB ist?

    Ausser natürlich, du verstehst "undefined behaviour" nicht, dann frag aber bitte direkt nach.

    Ich kenne das sehr viel genauer als du, denke ich. Sag mir zum Beispiel, ob der Standard irgendwelche Anforderungen an den Programmablauf richtet, welcher vor dem UB im Programm liegt?



  • Sone schrieb:

    Erkläre mir mal folgendes:

    foo bar = baz; // ok
    

    Wieso ist das Ok, obwohl beim unterliegenden kopieren der Member auch das uninitialisierte x einem anderen zugewiesen wird (was doch eigentlich eine lvalue-to-rvalue conversion beinhaltet), und laut unserer Diskussion UB ist?

    Das musst du camper fragen nicht mich.

    [lol]

    Wo könnte da auch nur annähernd ein Problem sein?

    [/lol]

    Nicht das lvalue-to-rvalue von baz ist das Problem, sondern das lvalue-to-rvalue von baz.x (was uninitialized ist), was aber automatisch im impliziten Kopierkonstruktor vorkommt.

    Was jedenfalls eindeutig UB ist, ist

    Sone schrieb:

    Ich traue aber meinen Augen nicht, wenn ich lese,

    int a, b = a;
    

    Soll UB sein.

    Nach meinem Verständnis:

    foo a, b;
    &a; // ok
    memcpy(&a, &b, sizeof(a)); // ok
    assert(memcmp(&a, &b, sizeof(a)) == 0); // ok
    cout << a.x; // UB, a.x invalid (uninitialized)
    

    Der Standard unterscheided zwischen validen Werten und invaliden. Ein Objekt über unsigned char-Pointer zuzugreifen ist immer erlaubt, es auszulesen nur, wenn der Wert valide ist.



  • Nun, es scheint so, dass auch ein etwas anderes Verstaendnis von UB hatte. Folgt man http://en.wikipedia.org/wiki/Undefined_behavior und anderen Artikeln folgt, so muss

    int a, b;
    a = b;
    

    UB sein, weil das Resultat a der Operation = undefiniert ist, eben weil b undefiniert ist.



  • Bei Stackoverflow.com gibt es eine ähnliche Diskussion darüber: Does initialization entail lvalue-to-rvalue conversion? Is `int x = x;` UB?
    Wie soll man eigentlich korrekte C++ Programme schreiben, wenn man sich noch nicht einmal darüber einig ist, was UB ist und was nicht?



  • Was jedenfalls eindeutig UB ist

    Ach, du weißt es? Woher weißt du das?

    Nicht das lvalue-to-rvalue von baz ist das Problem, sondern das lvalue-to-rvalue von baz.x (was uninitialized ist), was aber automatisch im impliziten Kopierkonstruktor vorkommt.

    Siehe oben. Woher weißt du, dass eine lvalue-to-rvalue Umwandlung nötig ist?

    Th69 schrieb:

    Wie soll man eigentlich korrekte C++ Programme schreiben, wenn man sich noch nicht einmal darüber einig ist, was UB ist und was nicht?

    Der Fall "initialisiere eine Variable mit einer uninitialisierten anderen Variable" ist eine Ausnahme. Es gibt kaum Fälle in C++ wo es völlig unklar ist ob etwas erlaubt ist oder nicht. 🙂

    Und: was heißt hier korrekt? Dieser hier diskutierte Fall führt schon zu sowieso nicht korrektem Code - wer schreibt sowas? 😕



  • Sone schrieb:

    Siehe oben. Woher weißt du, dass eine lvalue-to-rvalue Umwandlung nötig ist?

    Das ist common sense. So common sense, dass es im Standard nur als Note festgehalten wird

    For example, the built-in assignment operators expect that the left operand is an lvalue and that the right operand is a prvalue and yield an lvalue as the result.

    Ansonsten siehe den Post von Johannes Schaub - litb im verlinkten SO für eine exakte Herleitung.



  • facepalm schrieb:

    Sone schrieb:

    Siehe oben. Woher weißt du, dass eine lvalue-to-rvalue Umwandlung nötig ist?

    Das ist common sense. So common sense, dass es im Standard nur als Note festgehalten wird

    For example, the built-in assignment operators expect that the left operand is an lvalue and that the right operand is a prvalue and yield an lvalue as the result.

    Ansonsten siehe den Post von Johannes Schaub - litb im verlinkten SO für eine exakte Herleitung.

    Dein Name passt wunderbar zu deinen Posts.
    Er* erwähnt doch noch explizit, dass assignment und initialization nicht dasselbe sind! Und das seine Schlussfolgerung nur Spekulation ist! Lern lesen!



  • Sone schrieb:

    Er erwähnt doch noch explizit, dass assignment und initialization nicht dasselbe sind! Und das seine Schlussfolgerung nur Spekulation ist! Lern lesen!

    Lern lesen! Ich schrieb Johannes Schaub - litb und nicht Andy Prowl.



  • facepalm schrieb:

    Sone schrieb:

    Er erwähnt doch noch explizit, dass assignment und initialization nicht dasselbe sind! Und das seine Schlussfolgerung nur Spekulation ist! Lern lesen!

    Lern lesen! Ich schrieb Johannes Schaub - litb und nicht Andy Prowl.

    Ja, aber du beziehst dich auf ein von Andy Prowl verlinktes Zitat - worauf denkst du habe ich geantwortet? Nur auf den ersten Teil deines Posts.


Anmelden zum Antworten