Hallo zusammen,
folgendes kleines MVCE sei gegeben:
1
#include<stdint.h>
2
3
template<typenameT>
4
structC{
5
union{
6
volatileTa;
7
volatileTb;
8
};
9
staticconstexpruint8_taddress=0x28;
10
};
11
12
template<typenameC>
13
constexprinlineC*getBaseAddr(){
14
returnreinterpret_cast<C*>(C::address);
15
}
16
17
volatileuint8_tg=0;
18
19
intmain(){
20
constexprautoc1=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 ...
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.
tictactoe schrieb:> Funktioniert das wirklich?
Ja.
> Was ist c1()?
Steht doch da ...
> Sagen wir mal, da wäre nur c1 statt c1(). Dann handelt es sich> tatsächlich um Undefined Behavior bei g = c1->b.
Nein, dann wäre es syntaktisch falsch.
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.
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
structComponent{
4
typedefuint8_tvalue_type;
5
union{
6
volatilevalue_typea;
7
volatilevalue_typeb;
8
};
9
};
10
11
volatileComponentc1;
12
volatileuint8_tg=0;
13
14
intmain(){
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.
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.
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.
tictactoe schrieb:> Soweit ich sehe, ist es ein Pointer auf struct C. Seit wann kann man> einen Pointer wie eine Funktion aufrufen?
Nö, das ist ein Pointer auf eine parameterlose Funktion die einen
Pointer auf struct C zurückliefert.
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
constexprautoc1=getBaseAddr<C<uint8_t>>;
Die Funktion wird nicht aufgerufen, also ist c1 ein Funktionspointer auf
die Funktion.
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 :-).
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 ]
Daniel A. schrieb:> Nein, Betrachte den code nochmal:> constexpr auto c1 = getBaseAddr<C<uint8_t>>;
Jetzt seh ich's. Keine Klammern nach dem Ausdruck auf der rechten Seite.
Danke.
Raoul D. schrieb:> Das lese ich etwas anders (C++, N4297, Kapitel 9.5 Unions):
Ja, stimmt. Das habe ich vergessen. Dein Use-case entspricht dem
Szenario mit der "common initial sequence".
Johann L. schrieb:> Da die Komponenten volatile sind, hat der Compiler eh keine andere Wahl> als Code zu generieren, die die Komponente zurückliest.
Das "denke" ich auch, allerdings finde ich dafür auch keine Bestätigung
im Standard oder sonstwo.