C++, Unions und UB

OP #4876944
Hallo zusammen,

folgendes kleines MVCE sei gegeben:
1
#include <stdint.h>
2

3
template<typename T>
4
struct C {
5
    union {
6
        volatile T a;
7
        volatile T b;
8
    };
9
    static constexpr uint8_t address = 0x28;
10
};
11

12
template<typename C>
13
constexpr inline C* getBaseAddr() {
14
    return reinterpret_cast<C*>(C::address);
15
}
16

17
volatile uint8_t g = 0;
18

19
int main() {
20
    constexpr auto c1 = getBaseAddr<C<uint8_t>>;
21
    
22
    c1()->a = 1;
23
    g = c1()->b;    
24
    
25
    while(true) {}
26
}

Mit z.B. avr-g++ (7.0.0) funktioniert es wie es soll.

Allerdings ist Datenelement b nicht das aktive Datenelement der union im 
Moment des Auswertens. Nach (oberflächlichem) Lesen der betreffenden 
Passagen des C++-Standards könnte das ein Grund für UB sein.
Bin mir aber unsicher ...
Gast #4876958
Funktioniert das wirklich? Was ist c1()?

Sagen wir mal, da wäre nur c1 statt c1(). Dann handelt es sich 
tatsächlich um Undefined Behavior bei g = c1->b.
#4877030
Ist das absichtlich so konfus geschrieben: "C" als Bezeichner einer 
struct, sowie als Template Argument. "c1" als Bezeichner für eine 
Funktion die immer die Adresse 0x28 zurückliefert.

Was steht denn an Adresse 0x28 wo du deine struct hincastest? Bei meinem 
"normalen" System läuft es auf eine Seg fault.
OP #4877048
Mikro 7. schrieb:

> Was steht denn an Adresse 0x28 wo du deine struct hincastest? Bei meinem
> "normalen" System läuft es auf eine Seg fault.

Ja, das ist klar. Aber darum geht es ja auch nicht.

Habe das Beispiel etwas "entschärft":
1
#include <stdint.h>
2

3
struct Component {
4
    typedef uint8_t value_type;
5
    union {
6
        volatile value_type a;
7
        volatile value_type b;
8
    };
9
};
10

11
volatile Component c1;
12
volatile uint8_t g = 0;
13

14
int main() {
15
    c1.a = 1;
16
    g = c1.b;    
17
    
18
    while(true) {}
19
}

Das erste Beispiel hätte ich vllt im µC / Elektronik Forum posten 
sollen, weil die Adresse natürlich etwas mit einer Resgisteradresse zu 
tun hat.

Bei der obigen Version ist es aber egal. Die Frage ist dieselbe.
OP #4877134
Mikro 7. schrieb:
> Ich nehme an, du erwartest g==1? Mit gcc scheint das auch zu klappen.

In C11 ist ein sog. "typ-punning" explizit erlaubt.

In C++(98/11/14/17) explizit nicht. Allerdings gibt es eben Ausnahmen.

Eine Ausnahme besteht für Standard-Layout-Type mit einer gleichen 
Abfolge der Datenelement. In diesem Fall soll das Lesen über das 
nicht-aktive Datenelement (hier b) ok sein.

Der nächste Schritt wäre dann auch die Modifikation über das 
nicht-aktive Datenelement b (was damit zum aktiven wird, weil zulesetzt 
geschrieben) und dann Lesen über a.

Auch dies scheint in allen Fällen beim g++ zu funktionieren, stellt aber 
i.A. UB dar, jedenfalls so, wie ich das im Standard lese.
Gast #4877205
Raoul D. schrieb:
>> Was ist c1()?
>
> Steht doch da ...

Soweit ich sehe, ist es ein Pointer auf struct C. Seit wann kann man 
einen Pointer wie eine Funktion aufrufen?

Aber egal...

Raoul D. schrieb:
> Eine Ausnahme besteht für Standard-Layout-Type mit einer gleichen
> Abfolge der Datenelement. In diesem Fall soll das Lesen über das
> nicht-aktive Datenelement (hier b) ok sein.

Zumindest in C++ gibt es keine Ausnahme: Man darf nur den Union-Member 
auslesen, der zuletzt beschrieben worden ist. Alles andere ist UB. Es 
ist egal, ob die Members Standard-Layout-Typen sind oder nicht.
#4877281
tictactoe schrieb:
> Raoul D. schrieb:
>>> Was ist c1()?
>>
>> Steht doch da ...
>
> Soweit ich sehe, ist es ein Pointer auf struct C. Seit wann kann man
> einen Pointer wie eine Funktion aufrufen?

Nein, Betrachte den code nochmal:
1
constexpr auto c1 = getBaseAddr<C<uint8_t>>;
Die Funktion wird nicht aufgerufen, also ist c1 ein Funktionspointer auf 
die Funktion.
#4877311
Raoul D. schrieb:
> In C11 ist ein sog. "typ-punning" explizit erlaubt.
>
> In C++(98/11/14/17) explizit nicht. Allerdings gibt es eben Ausnahmen.

Ich würde die Frage auf Stackoverflow einstellen. Am besten mit 'nem 
(bereinigten) Pointer Cast Beispiel (aus dem ersten Post -- das ist wohl 
etwas komplexer), falls sich Experten wie KHB oder Yalu etc. hier nicht 
noch melden. -- Wenn du's dort einstellst wäre ein Link nett :-).
OP #4877369
tictactoe schrieb:
> Raoul D. schrieb:
>>> Was ist c1()?
>>
>> Steht doch da ...
>
> Soweit ich sehe, ist es ein Pointer auf struct C. Seit wann kann man
> einen Pointer wie eine Funktion aufrufen?
>
> Aber egal...
>
> Raoul D. schrieb:
>> Eine Ausnahme besteht für Standard-Layout-Type mit einer gleichen
>> Abfolge der Datenelement. In diesem Fall soll das Lesen über das
>> nicht-aktive Datenelement (hier b) ok sein.
>
> Zumindest in C++ gibt es keine Ausnahme: Man darf nur den Union-Member
> auslesen, der zuletzt beschrieben worden ist. Alles andere ist UB. Es
> ist egal, ob die Members Standard-Layout-Typen sind oder nicht.

Das lese ich etwas anders (C++, N4297, Kapitel 9.5 Unions):
1
In a union, at most one of the non-static data members can be active at any time, that is, the value of at
2
most one of the non-static data members can be stored in a union at any time. [ Note: One special guarantee
3
is made in order to simplify the use of unions: If a standard-layout union contains several standard-layout
4
structs that share a common initial sequence (9.2), and if an object of this standard-layout union type
5
contains one of the standard-layout structs, it is permitted to inspect the common initial sequence of any of
6
standard-layout struct members; see 9.2. — end note ]

Antwort schreiben

Eine eigene Frage? Dieser Thread ist über 9 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.