Allignement etc. tests

Hallo C-Profis,

ich bin auf der Suche nach einem Quelltext, mit dem man auf einer Plattform schnell feststellen kann,

welches Allignment in strukturen verwendet wird, welche Größen int long etc haben, ob ints „normal“ überlaufen (also bei (32767) + 1 ergibt -> - 32768 bei 16-bit values)

Ich weiss, ich weiss, lieber platformunabhängig programmieren, aber dann sind structs und viele integer-arithmetiken von vornherein ausgeschlossen.

Vielen Dank,

und viele Grüße

achim

ich bin auf der Suche nach einem Quelltext, mit dem man auf
einer Plattform schnell feststellen kann,

welches Allignment in strukturen verwendet wird,

Das ist schwierig festzustellen. Solange du keine sehr seltsamen low-level-Routinen schreibst, solltest du davon sowieso die Finger lassen. Der Compiler kennt sich damit recht gut aus.

welche Größen int long etc haben,

sizeof(int), sizeof(long), … ergibt die Größe des Datentyps (oder auch einer Variable) in Bytes. Natürlich kann ein Byte auch mal 7 oder 9 Bit haben.

ob ints „normal“ überlaufen (also bei (32767) + 1 ergibt -> - 32768
bei 16-bit values)

int16_t var = 32767;
++var;
/* var prüfen */

Ich weiss, ich weiss, lieber platformunabhängig programmieren,
aber dann sind structs und viele integer-arithmetiken von
vornherein ausgeschlossen.

Wo soll das Problem mit structs sein? Die sind in C standard und der Compiler addressiert die Einzelteile richtig. Was Integer-Arithmetik angeht fährt man mit den normalen Operatoren eigentlich immer gut. Ich kenne mal keine Architektur, die in ihrem normalen Befehlssatz mit Sättigung oder anderen nicht ganz trivialen Herangehensweisen arbeitet.

Hallo Fragewurm,

Wo soll das Problem mit structs sein? Die sind in C standard
und der Compiler addressiert die Einzelteile richtig.

Hier wird schon recht lustig, wenn man direkt auf komplexe I/Os zugreiffen will. Bei MicoControllern ist hal kein BS dafür zuständig …

Oder wenn man Binär-Daten aus einer Datei lesen muss, wobei da einer die Structs mit fwrite() geschrieben hat … Hier kann es selbst beim selben Compiler zu problemen kommen, wenn man unterschiedliche Werte für das Alignment einstellt.

Und binärprotokolle machen auch immer Spass …

Gerade bei Protokollen kommt dann oft noch hinzu, dass man den Code auf zwei unterschiedlichen CPUs verwenden möchte und das ganze nicht zu viel Overhead produzieren sollte.

MfG Peter(TOO)

Hallo achim,

ich bin auf der Suche nach einem Quelltext, mit dem man auf
einer Plattform schnell feststellen kann,

welches Allignment in strukturen verwendet wird, welche Größen

Geht eigentlich nur über sizeof()

struct
{
char c;
long l;
} test;
#if (sizeof(test)-sizeof(char)-sizeof(long))
#error „aligment wird verwendet“
#endif
// Diese Abfrage kannst du natürlich beliebig ausbauen

int long etc haben, ob ints „normal“ überlaufen (also bei
(32767) + 1 ergibt -> - 32768 bei 16-bit values)

Eigentlich geht es hier um die Frage ob int signed oder unsigned ist.

Einige dieser Antworten findes du normalerweise in limits.h

Ich weiss, ich weiss, lieber platformunabhängig programmieren,
aber dann sind structs und viele integer-arithmetiken von
vornherein ausgeschlossen.

Du meinst wohl eher Tricks :wink:)

MfG Peter(TOO)

Oder wenn man Binär-Daten aus einer Datei lesen muss, wobei da
einer die Structs mit fwrite() geschrieben hat … Hier kann
es selbst beim selben Compiler zu problemen kommen, wenn man
unterschiedliche Werte für das Alignment einstellt.

Dafür muss man sich dann schon zumindest an die eigenen Konventionen halten. Ansonsten müsste man ja Alignment und Typengröße auch übermitteln, und wie willst du die dann bitteschön auslesen? :wink: Genau für sowas gibt es ja festgenagelte Datentypen (stdint.h) oder network byteorder (siehe auch RFC 1014), womit man dann den Kram Stück für Stück verschickt.

Und binärprotokolle machen auch immer Spass …

Die machen in Hardware bald mehr Spass als in Software :smile:

Gerade bei Protokollen kommt dann oft noch hinzu, dass man den
Code auf zwei unterschiedlichen CPUs verwenden möchte und das
ganze nicht zu viel Overhead produzieren sollte.

First rule of optimization: Don’t do it.
Second rule of optimization (experts only): Don’t do it yet.

Wie willst du Datentransfers ohne feste Konvention nur unter Angabe einiger Parameter wie Alignment effizienter implementieren als ein paar hton[sl] beim Sender und noth[sl] beim Empfänger plus ein paar Casts auf beiden Seiten? Alleine die Zeigerarithmetik für das Alignment würde so verknorzt aussehen, dass man am nächsten Morgen nicht mehr weiss, was man da gehackt hat.

Wenn der Empfänger zu schwach ist um das selber zu erledigen, dann muss der Sender das halt in seiner Serialisierung schon in das endgültige Format packen, also quasi asymmetrisch serialisieren und wieder deserialisieren.

Hallo Fragewurm,

Oder wenn man Binär-Daten aus einer Datei lesen muss, wobei da
einer die Structs mit fwrite() geschrieben hat … Hier kann
es selbst beim selben Compiler zu problemen kommen, wenn man
unterschiedliche Werte für das Alignment einstellt.

Dafür muss man sich dann schon zumindest an die eigenen
Konventionen halten. Ansonsten müsste man ja Alignment und
Typengröße auch übermitteln, und wie willst du die dann
bitteschön auslesen? :wink: Genau für sowas gibt es ja
festgenagelte Datentypen (stdint.h) oder network byteorder
(siehe auch RFC 1014), womit man dann den Kram Stück für Stück
verschickt.

Tja, es gibt da so tolle Programmierer … die schreiben das einfach so raus, weil die keine Ahnung haben, es funktioniert ja, ein Struct mit fwrite() rausschreiben und mit fread() wieder auslesen… Und dann muss man mit dieser Datei leben !!! Meist sind diese Programmierer dann auch nicht auffindbar … und der Sourcecode ist auch nicht erhältlich.

Und binärprotokolle machen auch immer Spass …

Die machen in Hardware bald mehr Spass als in Software :smile:

Gerade bei Protokollen kommt dann oft noch hinzu, dass man den
Code auf zwei unterschiedlichen CPUs verwenden möchte und das
ganze nicht zu viel Overhead produzieren sollte.

First rule of optimization: Don’t do it.
Second rule of optimization (experts only): Don’t do it yet.

Wie willst du Datentransfers ohne feste Konvention nur unter
Angabe einiger Parameter wie Alignment effizienter
implementieren als ein paar hton[sl] beim Sender und noth[sl]
beim Empfänger plus ein paar Casts auf beiden Seiten? Alleine
die Zeigerarithmetik für das Alignment würde so verknorzt
aussehen, dass man am nächsten Morgen nicht mehr weiss, was
man da gehackt hat.

Das Problem ist meist das ich software bekommen, welche zufälligerweise geht, weil der Compiler kein Alignment macht …

Wenn der Empfänger zu schwach ist um das selber zu erledigen,
dann muss der Sender das halt in seiner Serialisierung schon
in das endgültige Format packen, also quasi asymmetrisch
serialisieren und wieder deserialisieren.

Ich habe keine Probleme mit der Implementierung von Protokollen, auch nicht auf unterschiedlicher Hardware.

MfG Peter(TOO)

Hallo Peter,

vielen Dank Euch beiden für die Hilfe. Zu Deiner letzten Aussage …

Ich habe keine Probleme mit der Implementierung von
Protokollen, auch nicht auf unterschiedlicher Hardware.

gehe ich dann richtig in der Annahme, dass dabei dann Strukturen und Datentypen > 1 Byte Tabu sind?

Das wäre natürlich schade, da mir z.B. für die Beschreibung eines Protokolls eine sinnvolle C-Programmiermöglichkeit fehlt. Prinzipiell kann ich ja nichtmal einen Pointer auf ein Int32 in einen Datenstrom legen, da Int32-Pointer je nach Prozessor 2 oder 4-Byte alligned sein müssen.

Wir verwenden daher für jedes Kopieren in und aus einem Datenstrom quasi memcpy, aber ich hatte gehofft, es geht irgendwie einfacher, „lesbarer“, ähnlich komfortabel wie mit Strukturen.

Gruß
achim

Hallo achim,

gehe ich dann richtig in der Annahme, dass dabei dann
Strukturen und Datentypen > 1 Byte Tabu sind?

JAIN :wink:)

Das wäre natürlich schade, da mir z.B. für die Beschreibung
eines Protokolls eine sinnvolle C-Programmiermöglichkeit
fehlt. Prinzipiell kann ich ja nichtmal einen Pointer auf ein
Int32 in einen Datenstrom legen, da Int32-Pointer je nach
Prozessor 2 oder 4-Byte alligned sein müssen.

Das ist noch das Harmlose, viel schlimmer sind schon Little- oder Big-Endian bei 16Bit Daten …

Wir verwenden daher für jedes Kopieren in und aus einem
Datenstrom quasi memcpy, aber ich hatte gehofft, es geht
irgendwie einfacher, „lesbarer“, ähnlich komfortabel wie mit
Strukturen.

Gerade memcpy() ist TABU !!!

Es wäre schön wenn man einfach eine struct üder die Leitung kopieren könnte.
Gerade bei Compilern für Intel-CPUs kann man das Allignement einstellen. Dadurch kann ich den gleichen Source mit einem Compiler zweimal übersetzen und diese beiden, scheinbar identischen, Programme können nicht mehr miteinander kommunizieren.

Man kann soetwas mit „#if sizeof()“ konstrukten absichern, zumindest soweit, dass der Compiler Fehlermeldungen macht, wenn die Einstellungen nicht stimmen.

Weiterhin kann man die structs so erstellen, dass man selber char z.B. auf Modulo 2 oder 4 auffüllt. Aber das geht auch nur innerhalb bestimmter Grenzen. Allerdings musst du dazu verstanden haben was der Compiler und der Linker eigentlich genau wie machen …

Das Einzige was fast immer funktioniert ist alles byteweise zu zerlegen und auf der Gegenseite wieder zusammen zusetzen.

Im weiteren Text steht „Compiler“ einerseits für unterschiedliche Versionen von unterschiedlichen Herstellern, sowie für Compiler für unterschiedliche CPUs.

Also nehmen wir den Wert 0x1234 welche in einem int abgelegt wird.

Je nach Compiler ist ein int 16, 32, 64 oder sonstwas Bits lang !
Nehmen wird mal 16Bit an.

union 
 {
 char c[2];
 int i;
 } x\_int

x\_int.i = 0x1234;

Bei Little-Endian ist nun
x_int.c[0] == 0x12 und
x_int.c[1] == 0x34

Bei Big-Endian ist es aber
x_int.c[0] == 0x34 und
x_int.c[1] == 0x12

Du musst also zuerst einmal festlegen wie die Daten byteweise übertragen werden sollen!
Ich lege jetzt mal fest, dass unser Wert 0x1234 als
0x12 gefolgt von 0x34
über die Leitung gehen soll.

int i = 0x1234;

SENDEN:

send\_byte( (i \>\> 16) & 0xFF );
send\_byte( i & 0xFF );


EMPFANGEN:

i = (get\_byte() 

Die Maskierung mit 0xFF, 0xFF00 und 0x00FF scheint auf den ersten Blick überflüssig zu sein.
Da aber int nicht 16Bit gross sein muss und zudem auch nicht garantiert ist, dass es signed oder unsigned ist, kann die die Signextension einen grossen Strich durch die Rechnung machen.

MfG Peter(TOO)

Hallo Peter,

vielen Dank für die Ausführungen

Gerade memcpy() ist TABU !!!

Tabu? Bei „bekannter Endian auch ?“

Man kann soetwas mit „#if sizeof()“ konstrukten absichern,

#if sizeof() ist leider nicht ANSI-C (vor 99) soweit ich weis, da es noch nicht zur Laufzeit des Präprozessors ausgewertet, oder?

Weiterhin kann man die structs so erstellen, dass man selber
char z.B. auf Modulo 2 oder 4 auffüllt.

Ein Standard-Problem dabei: Header ist nicht Modulo 4 groß, aber trotzdem bilde ich Strukturen über die Gesamtmessage und über die Daten.

Das Einzige was fast immer funktioniert ist alles byteweise zu
zerlegen und auf der Gegenseite wieder zusammen zusetzen.

tja, genau das was ich befürchtet habe, aber da muß es doch auch was eleganteres geben, oder? Gut, bei üblichen Protokollen z.B. für TCP-IP ist meist alles an 4-Byte Grenzen angelehnt, da stellen sich die Probleme (noch) nicht. Probleme bekommen wir hier nur mit „Ich-kann-auch-ein-schönes-Protokoll-selber-machen,-und-optimiere-sogar-ein ein-paar-Bits“-Protokollen. Und eben die möchte ich gerne ebenso elegant handhaben können, als hätte ich struct zur Verfügung.

Ich habe mal mit einer C-Beschreibungsstruktur angefangen, in der ein Telegramm-Inhalt auf eine Struktur gemapped wird, und ein Handler für den Datenaustausch per Byte-Operationen sorgt. Ist aber recht aufwendig, daher wäre es schön wenn es sowas als Standard gäbe. Die schon erwähnten htons & Co helfen dabei ja leider nicht.

int i = 0x1234;

SENDEN:

send_byte( (i >> 8) & 0xFF );
send_byte( i & 0xFF );

EMPFANGEN:

i = (get_byte()

Die Maskierung mit 0xFF, 0xFF00 und 0x00FF scheint auf den
ersten Blick überflüssig zu sein.
Da aber int nicht 16Bit gross sein muss und zudem auch nicht
garantiert ist, dass es signed oder unsigned ist, kann die die
Signextension einen grossen Strich durch die Rechnung machen.

ist nicht selbst das auf der (nicht Ansi-C) Annahme begründet, dass auf beiden Rechner die gleiche Zahlendarstellung (2-er Komplement) vorliegt? Gut, das möchte ich nicht auch noch berücksichtigen, und ich kenne auch keine anderen Maschinen, aber ich kenne welche, die andere Maschinen kennen.

Gruß und Danke nochmal

Achim

Hallo Achim,

Gerade memcpy() ist TABU !!!

Tabu? Bei „bekannter Endian auch ?“

Innerhalb des Programms kannst du memcpy() verwenden, aber schon bei der Ausgabe in eine Datei sollte man es nicht verwenden, wenn diese Daten auch von einem anderen Programm gelesen werden sollen. memcpy() hat keine Ahnung von deiner Datenstruktur und kopiert auch die Füllbytes für das Allignement einfach mit.

Man kann soetwas mit „#if sizeof()“ konstrukten absichern,

#if sizeof() ist leider nicht ANSI-C (vor 99) soweit ich weis,
da es noch nicht zur Laufzeit des Präprozessors ausgewertet,
oder?

Da bin ich jetzt gerade überfragt, aber es hat auf den von mir verwendeten Compiler schon vor 99 funktioniert.

Weiterhin kann man die structs so erstellen, dass man selber
char z.B. auf Modulo 2 oder 4 auffüllt.

Ein Standard-Problem dabei: Header ist nicht Modulo 4 groß,
aber trotzdem bilde ich Strukturen über die Gesamtmessage und
über die Daten.

Tja, wenn der Compiler da das Allignement regelt, weiss keinerwie gross dir Struktur wirklich ist. z.B.

struct
 {
 char c;
 int i;
 } s;

Für die H8/300x CPUs ist da zwischen char und int immer ein Füllbyte. Diese CPU „verträgt“ keine Ints auf ungeraden Adressen. Achja, int ist 16 oder 32 Bit, je nach Compiler-Einstellung …

Für eine Pentium CPU sind da 0, 1, 3, oder … Füllbytes, je nach Compilereinstellung…

Das Einzige was fast immer funktioniert ist alles byteweise zu
zerlegen und auf der Gegenseite wieder zusammen zusetzen.

tja, genau das was ich befürchtet habe, aber da muß es doch
auch was eleganteres geben, oder?

Nö, nur in speziellen Fällen !!

Gut, bei üblichen
Protokollen z.B. für TCP-IP ist meist alles an 4-Byte Grenzen
angelehnt, da stellen sich die Probleme (noch) nicht. Probleme
bekommen wir hier nur mit
„Ich-kann-auch-ein-schönes-Protokoll-selber-machen,-und-optimiere-sogar-ein
ein-paar-Bits“-Protokollen. Und eben die möchte ich gerne
ebenso elegant handhaben können, als hätte ich struct zur
Verfügung.

Für viele Dinge kommt man halt um ein eigenes Protokoll nicht drum rum.

Ich habe mal mit einer C-Beschreibungsstruktur angefangen, in
der ein Telegramm-Inhalt auf eine Struktur gemapped wird, und
ein Handler für den Datenaustausch per Byte-Operationen sorgt.
Ist aber recht aufwendig, daher wäre es schön wenn es sowas
als Standard gäbe. Die schon erwähnten htons & Co helfen dabei
ja leider nicht.

Der Programmierer sollte halt wissen was er tut.

ist nicht selbst das auf der (nicht Ansi-C) Annahme begründet,
dass auf beiden Rechner die gleiche Zahlendarstellung (2-er
Komplement) vorliegt?

JA.

Gut, das möchte ich nicht auch noch
berücksichtigen, und ich kenne auch keine anderen Maschinen,
aber ich kenne welche, die andere Maschinen kennen.

BCD-Maschinen sind recht selten.
Allerdings wirst du dabei noch ganz andere Probleme haben, deinen Source-Code kompatiber zu bekommen.

MfG Peter(TOO)

1 Like