Hypercell ein ] Hypercell aus ] Zeige Navigation ] Verstecke Navigation ]
c++.net  
   

Die mobilen Seiten von c++.net:
https://m.c-plusplus.net

  
C++ Forum :: C++ (alle ISO-Standards) ::  union -> struct inside  
Gehen Sie zu Seite Zurück  1, 2
  Zeige alle Beiträge auf einer Seite
Auf Beitrag antworten
Autor Nachricht
dot
Mitglied

Benutzerprofil
Anmeldungsdatum: 20.05.2004
Beiträge: 6751
Beitrag dot Mitglied 13:14:34 11.01.2017   Titel:              Zitieren

Meiner Interpretation des Standard nach handelt es sich bei obigem Code um UB. Es wird da aus einem inaktiven Member einer union gelesen, was laut Standard UB ist und fertig. Die Ausnahme von wegen common initial sequence greift hier nicht...

_________________
one point of view will never reveal the entire scene.


Zuletzt bearbeitet von dot am 13:16:38 11.01.2017, insgesamt 2-mal bearbeitet
SeppJ
Global Moderator

Benutzerprofil
Anmeldungsdatum: 10.06.2008
Beiträge: 27418
Beitrag SeppJ Global Moderator 13:16:33 11.01.2017   Titel:              Zitieren

dot schrieb:
Meiner Interpretation des Standard nach handelt es sich bei obigem Code streng genommen um UB; aus dem einfachen Grund, dass da aus einem inaktiven Member einer union gelesen wird, was laut Standard UB ist. Die Ausnahme von wegen common initial sequence greift hier nicht...
Und wo steht das im Standard? So einfach ist das wie gesagt nicht...

_________________
Korrekte Rechtschreibung und Grammatik sind das sprachliche Äquivalent zu einer Dusche und gepflegter Kleidung.
Finnegan
Mitglied

Benutzerprofil
Anmeldungsdatum: 09.12.2014
Beiträge: 462
Beitrag Finnegan Mitglied 17:48:40 11.01.2017   Titel:              Zitieren

Kein Standardreiterei, aber Hinweis in eine Richtung, in die man recherchieren kann, wenn mans genau wissen will:

Ich erinnere mich dass die Problematik in einem Vortrag zur Sprache kam (ich glaube es war einer der CppCon-Vorträge von Chandler Carruth über die Jahre).
Dort wurde erwähnt, dass derlesende Zugriff auf einen nicht aktiven Member wie in diesem Fall tatsächlich UB ist. Allerdings sei dieses Pattern derart weit
verbreitet - auch als Methode um vermeintlich strict-aliasing-konformen Code zu schreiben - dass hier wohl duch die Bank alle Compiler das "Richtige" machten.

Dennoch, für wirklich standardkonformen Code bleibt einem nur entweder die Werte über Bit-Schubserei oder via memcpy herauszufischen. Letzteres sieht
übrigens meist schlimmer aus als es ist: Die Compiler optimieren heutzutage gerade auch solche winzigen memcpy-Aufrufe recht gut - auch zu einem simplen
MOV zwischen Registern wenn möglich (trotz des "mem" im Namen).

P.S.: Ich glaube auch dass bezüglich Aliasing das von hustbaer vorgeschlagene reinterpret_cast in Ordnung wäre, allerdings nur aus dem Grund, dass ein
8-Bit-Farbkanal "zufällig" den richtigen Datentyp hat. Ich würde das eher vermeiden wollen, da zumindest bei den Sachen die ich selbst so mache nicht nur
8-Bit-Kanäle verwendet werden (und auch nicht immer für jeden Kanal gleich viele Bits).
hustbaer
Mitglied

Benutzerprofil
Anmeldungsdatum: 27.10.2006
Beiträge: 23335
Beitrag hustbaer Mitglied 02:20:47 12.01.2017   Titel:              Zitieren

Vergesst das aktive Element. Oder auch nicht, denn wenn es sich wie gesagt um etwas anderes als (unsigned) char handeln würde, dann wären diese Regeln natürlich wichtig -- und auch dieser Fall interessiert natürlich zu Recht viele Leute.

Hier sind es aber eben unsigned char, und damit wird der Fall IMO viel etwas einfacher. Denn man darf *jeden* (initialisierten) Speicher als char oder unsigned char auslesen, ganz egal was sich dort für ein Objekt befindet. Ebenso darf man jeden Speicher (initialisiert oder nicht) mit char bzw. unsigned char überschreiben. Das Überschreiben eines Objekts mit char bzw. unsigned char zerstört dieses -- natürlich ohne dass dabei der Destruktor aufgerufen würde. (Was natürlich auch bedeutet: man darf das nur machen wenn es für das korrekte Funktionieren des Programms nicht nötig ist dass der Destruktor aufgerufen wird.)

Weiters gibt es dann noch Sonderregeln, die es erlauben nach diesem "Überschreiben mit char" den Speicher wieder als ein Objekt eines anderen Typs zu verwenden -- so lange dieser andere Typ bestimmte Voraussetzungen erfüllt. Klassische C structs erfüllen z.B. diese Voraussetzungen. (Weiters sind speziell in C++11 noch etliche Dinge zusätzlich erlaubt, z.B. darf so ein struct IIRC nicht-virtuelle public Memberfunktionen haben etc. - aber die genauen Regeln dafür hab ich mir nie gemerkt.)

Diese Regeln führen u.A. dazu dass man eine memcpy-artige Funktion selbst implementieren kann, und verwenden um damit beliebige simple "Daten-structs" zu kopieren, ohne dabei in UB (oder auch nur IB) hineinzulaufen.

D.h. folgendes ist vollkommen definiert und vollkommen OK:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>
#include <stdlib.h>
 
void MyMemCpy(void* dest, void const* src, size_t size)
{
    unsigned char* d = static_cast<unsigned char*>(dest);
    unsigned char const* s = static_cast<unsigned char const*>(src);
    while (size--)
        *d++ = *s++;
}
 
struct Foo
{
    int a;
    int b;
};
 
int main()
{
    Foo const f1 = { 1, 2 };
    Foo* fp = static_cast<Foo*>(malloc(sizeof(Foo)));
    if (!fp)
        exit(1);
    MyMemCpy(fp, &f1, sizeof(Foo));
    printf("a = %d, b = %d\n", fp->a, fp->b);
    free(fp);
}


Wenn jetzt jemand argumentieren möchte, dass - unter der Voraussetzung dass sizeof(int) >= 4 * sizeof(char), der Zugriff auf color.a UB sein soll, dann würde mich interessieren was der Unterschied zu meinem Beispiel sein soll. Also warum ist color.a Lesen verboten, wohingegen *s Lesen in meinem Beispiel OK ist.
In beiden Fällen wird ein char gelesen "wo kein char ist". Wie man die Adresse ermittelt hat sollte dabei keine Rolle spielen. So lange sichergestellt ist dass die Adresse "passt" (also auf ein Byte zeigt welches initialisiert wurde), ist es erlaubt.

_________________
Until every person can enjoy all their human rights, we will not stop. I support Amnesty International. Will you?
https://www.amnesty.org / https://www.amnesty.de / https://www.amnesty.at
hustbaer
Mitglied

Benutzerprofil
Anmeldungsdatum: 27.10.2006
Beiträge: 23335
Beitrag hustbaer Mitglied 02:25:10 12.01.2017   Titel:              Zitieren

Was den "was wenn es kein (unsigned) char wäre?" Fall angeht: Hier glaube ich dass es tatsächlich UB wäre. Zumindest hab' ich das öfters gelesen, u.A. auch von Leuten wo ich den Eindruck hatte dass sie wissen wovon sie reden (schreiben).

Und es macht mMn. auch Sinn. Denn wenn man sich die klassische "strict Aliasing" Problemfunktion anguckt...
C++:
int Foo(int* a, short* b)
{
    *a = 0;
    *b = 1;
    return *a;
}

Die meisten Compiler optimieren das return *a hier (berechtigterweise) zu einem return 0. Weil strict aliasing ja besagt dass a und b unmöglich "aliasen" können (dürfen).
Und was soll nun passieren wenn man folgendes macht
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
 
int Foo(int* a, short* b)
{
    *a = 0;
    *b = 1;
    return *a;
}
 
union U
{
    int a;
    short b;
};
 
int main()
{
    U u;
    printf("Foo: %d\n", Foo(&u.a, &u.b));
}

Hier kommt mit optimiertem Code üblicherweise "Foo: 0" raus. Wenn es für enums ne Ausnahme (ohne weitere Einschränkungen) gäbe, dann müsste aber "Foo: 1" rauskommen. D.h. der Compiler dürfte in Foo nicht mehr das return *a zu return 0 optimieren. Was doof wäre, denn damit wäre die ganze strict aliasing Regel für die Katz. Was jetzt einige sicher freuen würde, aber das ist wieder ein anderes Thema -- ist halt einfach nicht so :)

D.h. entweder es gibt ne Ausnahme die wieder super krass eingeschränkt ist, und das Beispiel hier eben irgendwie ausschliesst, oder es ist schlichtweg einfach grundsätzlich verboten. Ich gehe wie gesagt davon aus dass es grundsätzlich verboten ist -- bzw. mehr noch, ich hoffe dass es grundsätzlich verboten ist. Weil alles andere die so schon super komplizierten Regeln in diesem Bereich noch komplizierter machen würde.

_________________
Until every person can enjoy all their human rights, we will not stop. I support Amnesty International. Will you?
https://www.amnesty.org / https://www.amnesty.de / https://www.amnesty.at
C++ Forum :: C++ (alle ISO-Standards) ::  union -> struct inside  
Gehen Sie zu Seite Zurück  1, 2
Auf Beitrag antworten

Zeige alle Beiträge auf einer Seite




Nächstes Thema anzeigen
Vorheriges Thema anzeigen
Sie können Beiträge in dieses Forum schreiben.
Sie können auf Beiträge in diesem Forum antworten.
Sie können Ihre Beiträge in diesem Forum nicht bearbeiten.
Sie können Ihre Beiträge in diesem Forum nicht löschen.
Sie können an Umfragen in diesem Forum nicht mitmachen.

Powered by phpBB © 2001, 2002 phpBB Group :: FI Theme

c++.net ist Teilnehmer des Partnerprogramms von Amazon Europe S.à.r.l. und Partner des Werbeprogramms, das zur Bereitstellung eines Mediums für Websites konzipiert wurde, mittels dessen durch die Platzierung von Werbeanzeigen und Links zu amazon.de Werbekostenerstattung verdient werden kann.

Die Vervielfältigung der auf den Seiten www.c-plusplus.de, www.c-plusplus.info und www.c-plusplus.net enthaltenen Informationen ohne eine schriftliche Genehmigung des Seitenbetreibers ist untersagt (vgl. §4 Urheberrechtsgesetz). Die Nutzung und Änderung der vorgestellten Strukturen und Verfahren in privaten und kommerziellen Softwareanwendungen ist ausdrücklich erlaubt, soweit keine Rechte Dritter verletzt werden. Der Seitenbetreiber übernimmt keine Gewähr für die Funktion einzelner Beiträge oder Programmfragmente, insbesondere übernimmt er keine Haftung für eventuelle aus dem Gebrauch entstehenden Folgeschäden.