[C++] Datentyp für große Bitfields

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
Benutzeravatar
Schrompf
Moderator
Beiträge: 4959
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas
Wohnort: Dresden
Kontaktdaten:

[C++] Datentyp für große Bitfields

Beitrag von Schrompf »

Moin!

Ich brauch ein großes Bitfield. Da will ich mal als uint16_t reingreifen, evtl. mal mit uint64_t, und sicher auch mal mit der ganzen Macht von memset() oder AVX _m256. Welchen Datentyp nehme ich da als Basis, damit der Compiler die Fresse hält und ich nicht irgendwann in ein UndefinedBehaviour latsche?

Soweit ich weiß, ist Cast von und zu CharPointer immer ok. Aber gibt's noch was Anderes? Ein Char-Pointer aliased doch auch mit ALLEM, und es wär doch echt doof, wenn dann jeder beliebige Ref-Funktionsparameter für den Compiler nach Aliasing aussieht. Oder mach ich mir da zuviele Gedanken?
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
antisteo
Establishment
Beiträge: 911
Registriert: 15.10.2010, 09:26
Wohnort: Dresdem

Re: [C++] Datentyp für große Bitfields

Beitrag von antisteo »

ich bin auf uint64. Für FastPFOR und Arithmetische Kodierung beides ideal, weil man da die wenigsten Overlaps zu Nachbarfeldern hat (halb so viele wie bei uint32)
http://fedoraproject.org/ <-- freies Betriebssystem
http://launix.de <-- kompetente Firma
In allen Posts ist das imo und das afaik inbegriffen.
Benutzeravatar
starcow
Establishment
Beiträge: 550
Registriert: 23.04.2003, 17:42
Echter Name: Mischa Schaub
Kontaktdaten:

Re: [C++] Datentyp für große Bitfields

Beitrag von starcow »

Schrompf hat geschrieben: 14.10.2024, 22:25 Welchen Datentyp nehme ich da als Basis, damit der Compiler die Fresse hält und ich nicht irgendwann in ein UndefinedBehaviour latsche?

Soweit ich weiß, ist Cast von und zu CharPointer immer ok. Aber gibt's noch was Anderes? Ein Char-Pointer aliased doch auch mit ALLEM, und es wär doch echt doof, wenn dann jeder beliebige Ref-Funktionsparameter für den Compiler nach Aliasing aussieht. Oder mach ich mir da zuviele Gedanken?
Interessante Frage, die mich in ganz ähnlicher Weise gerade auch beschäftigt.
Die Kernfrage scheint mir zu sein, wann man guten Gewissens von einem Pointer-Typ in einen anderen Pointer-Typ casten darf, ohne dabei laut Standard UB auszulösen.
Bislang dachte ich, dass dies alleine vom Alignment abhängig sei - und solange dies den Anforderungen des Typs entspräche, sei alles "gut".
Deshalb hatte ich auch jeweils meine Buffer einfach mittels _Alignas entsprechend den Typ-Anforderungen ausgerichtet.

Von der ARM Architektur weiss ich, dass ein "Alignment-missmatch" zu einer nicht unerheblichen Performance-Strafe führt.
Abgesehen davon ist es aber der Hardware "egal" - was aber ja leider nicht bedeuten würde, dass es dann laut C(++) Standard auch tatsächlich legal ist.
Der Standard sagt hier leider klar: UB.
Übrigens scheint das Brechen des Alignment der x86/x64 Architektur weitestgehend egal zu sein (hörensagen - ohne Gewär).
https://hackaday.com/2022/05/10/data-al ... -the-ugly/


Seit gestern bin ich dabei mich in den C11 ISO Standard einzulesen (mit dem Ziel ihn systematisch durchzuarbeiten).
Und seit gestern kann ich mit Sicherheit sagen: Ein richtiges Alignment alleine reicht leider nicht aus, um aus der "UB-Zone" zu kommen.
Das Ganze ist (nach meinen Erfahrung gestern) auch nicht ganz trivial zu durchschauen und damit die Frage konkret zu beantworten.
Es gibt nicht die eine Stelle im Standard, die hier Klarheit bringen würde (oder ich habe sie noch nicht gefunden).
Es müssen an verschiedenen Stellen spezifische Definitionen miteinbezogen werden, die sich in verschiedenen Kapiteln befinden.

Ich kann dir aber soweit versichern:
Schrompf hat geschrieben: 14.10.2024, 22:25 Soweit ich weiß, ist Cast von und zu CharPointer immer ok.
Ja, das trifft genau zu: C11 Standard 6.5 Expressions §7 (s. 77)

Du könntest dir überlegen, ob dir ein union behilflich sein könnte?
Damit kann man in bestimmten Situationen die "Pointer-Cast-Problematik" umgehen.
Von der Idee her so, oder so ähnlich:

Code: Alles auswählen

	union
	{
		unsigned char u8[8];
		unsigned short u16[4];
		unsigned int u32[2];
		unsigned long long u64[1];
	} u = { 0 };

	unsigned char * p1 = u.u8;       // Ok!
	unsigned short * p2 = u.u16;     // Ok!
	unsigned int * p3 = u.u32;       // Ok!
	unsigned long long * p4 = u.u64; // Ok!
P. S.: Den C11 Standard hatte ich gewählt, weil er für rund 80.- zu haben war. Der aktuelle C18 Standard hätte rund 240.- gekostet.
C11 und C18 unterscheiden sich jedoch nur marginal - und diese Abweichungen betreffen (nach meinen Recherchen) in erster Linie die stdlib.
Meine Vermutung: Wenn in einigen Monaten der neue C23 Standard erscheint, sollte der C18 Standard wiederum entsprechend günstiger zu haben sein.
Freelancer 3D- und 2D-Grafik
mischaschaub.com
Benutzeravatar
Schrompf
Moderator
Beiträge: 4959
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas
Wohnort: Dresden
Kontaktdaten:

Re: [C++] Datentyp für große Bitfields

Beitrag von Schrompf »

Dafür überhaupt Geld zu verlangen ist eine Sauerei. Ich finde es bemerkenswert, dass Du das trotzdem tust.

Ne union ist aber auch keine Lösung: wenn ich mich recht erinnere, darf man in ner Union immer nur auf den Teil zugreifen, denn man zuletzt geschrieben hat. Alles andere ist ebenfalls UB
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
starcow
Establishment
Beiträge: 550
Registriert: 23.04.2003, 17:42
Echter Name: Mischa Schaub
Kontaktdaten:

Re: [C++] Datentyp für große Bitfields

Beitrag von starcow »

Schrompf hat geschrieben: Gestern, 14:37 Ne union ist aber auch keine Lösung: wenn ich mich recht erinnere, darf man in ner Union immer nur auf den Teil zugreifen, denn man zuletzt geschrieben hat. Alles andere ist ebenfalls UB
Das scheint mir ein verbreitetes Missverständnis zu sein (und auch einer der Gründe, wieso ich den Standard durcharbeiten will).

K&R (Second Edition ANSI) sagt bei 6.8 auf S. 148 dazu
It is the programmer's responsibility to keep track of which type is currently stored in a union; the results are implementation-dependent if smothing is sored as one type and extractd as another.
K. N. King hat in seinem Buch "C Programming - A Moddern Approach (Second Edition) ein Kapitel S. 519
Using Unions to Provide Multiple Views of Data
Indem er genau solche Beispiele auch zeigt. Er schreibt lediglich:
Be careful when using unions to provide mutliple views of data. Data that is valid in its original formats may be invalid when viewed as a different type, causing unexpected problems.
Wäre ich bereits auf die entsprechende Stelle im Standard gestossen, hätte ich sie natürlich jetzt aufgeführt! :-)
Laut C11 grundsätzlich erlaubt: C++ ISO Standard 6.5.2.3 Structure and union members (S. 83), Abschnitt 3 - Fussnote 95)
(Ich kann mir übrigens nicht vorstellen, dass sich C und C++ in diesem Aspekt unterscheiden) Danke Krishty :-(

Klar muss jedoch sein, dass das Ganze Implementationsabhängig sein "muss" - alleine aufgrund der LE/BE Thematik.

Aber vielleicht weiss es ja jemand aus dem Stehgreif?
Zuletzt geändert von starcow am 17.10.2024, 18:02, insgesamt 10-mal geändert.
Freelancer 3D- und 2D-Grafik
mischaschaub.com
Benutzeravatar
Krishty
Establishment
Beiträge: 8301
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] Datentyp für große Bitfields

Beitrag von Krishty »

In C++ ist es definitiv UB, es sei denn man greift auf das erste Member der Union zu und es hat in allen Varianten den selben Typ. Also bei

union U {
 struct A {
   int type;
   ...
 } a;
 struct B {
   int type;
   ...
 } b;
} u;


ist es erlaubt, u.b.type zu lesen obwohl u.a zuletzt geschrieben wurde weil beide am Anfang sind und den selben Typ haben. Das musste zugelassen werden, weil sonst die Berkeley Sockets kaputtgehen (sockaddr_in & Co.). Alles andere, ab dem zweiten Member, ist UB (nicht IB).

Ob C sich dort unterscheidet weiß ich aus dem Stehgreif nicht; ich meine mich zu erinnern dass schon. Clang und GCC tolerieren das üblicherweise weil es in der Linux-Welt so stark verbreitet ist.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
starcow
Establishment
Beiträge: 550
Registriert: 23.04.2003, 17:42
Echter Name: Mischa Schaub
Kontaktdaten:

Re: [C++] Datentyp für große Bitfields

Beitrag von starcow »

Ach Herr Jeeh! ^^
Danke Krishty! :-)

Edit:
Ich habs gefunden:
§6.5.2.3 Structure and union members (S. 83), Abschnitt 3 - Fussnote 95)

Es ist in C grundsätzlich erlaubt und wird (manchmal) unter dem Begriff "type punning" referenziert.
... a process sometimes called ‘‘type punning’’)
Es können aber sogenannte trap representations enstehen, die dann UB sind (Kapitel 6.2.6).
Beispiel: Du speicherst dir präzise eine u32 Zahl, die dann als float Repräsentation ungültig ist.
(Ist das in IEEE 754 überhaupt denkbar? Vielleicht ein NaN mittels 0xFFFFFFFF? Ich komm jetzt irgendwie auf kein Beispiel...)
Freelancer 3D- und 2D-Grafik
mischaschaub.com
Antworten