Forum: Compiler & IDEs Struktur Variablen hintereinander im Speicher ablegen?


von Jens S. (jenss)


Lesenswert?

Hallo zusammen,

irgendwo hier habe ich das schon einmal gelesen aber leider vergessen :(

Und zwar wird anscheinend eine Struktur nicht wirklich Byte für Byte bei 
mir in den Speicher abgelegt. Es scheint so als wenn das erste unsigned 
char 4 bytes belegt.

gab es da nicht ein zusatz den man bei einer Struct dran schreiben 
konnte, damit die Werte direkt hintereinander in den Speicher gelegt 
werden???

Ich benutze den SAM7XC256 und als Entwicklungsumgebung µVision von Keil.

Ich hoffe es kann mir wer helfen.

Meine Struktur ist so aufgebaut:
unsigned char
unsigned long int
unsigned char

leider ist zwischen dem ersten unsigned char und dem unsigned long int 
eine "lücke" von 3 Bytes. Kann man im Memorymonitor sehen :/

von Kai G. (runtimeterror)


Lesenswert?

Sortier mal um, wenn möglich:

unsigned long int
unsigned char
unsigned char

Der versucht den "unsigned long int" auf eine glatte (durch 4 teilbare) 
Adresse zulegen.

Ob man das auch ohne Umstellen forcieren kann, muss jemand mit mehr 
Ahnung beantworten ;)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Dafür hat jeder Compiler was eigenes.  Beim GCC (in dessen Forum du
dich ja befindest) heißt das __attribute__((packed)).

Ist aber generell keine gute Idee, da es erstens unportabel ist und
zweitens das Alignment ja aus gutem Grund existiert.  Nicht jeder
Prozessor toleriert es, wenn man Zugriffe auf 32-Bit-Daten nicht an
einer 32-Bit-Grenze im Speicher ausrichtet.  Manche (wie IA32) werden
davon einfach nur langsamer, da sie zwei Buszyklen statt nur eines
benötigen, andere wiederum verweigern dir die Arbeit und schmeißen
dir einfach einen Trap, da sie gar nicht daran denken, so 'nen Quatsch
erst in der Hardware ausführen zu wollen.

Wenn du byteweisen Zugriff brauchst, dann bau die Struktur auch
byteweise auf und schiebe dir hinterher die Multibyte-Zahlen zusammen.
Was anderes könnte dir Hardware ja ohnehin nicht machen, aber damit
bist du dann portabel.

von Jens S. (jenss)


Lesenswert?

Danke für die Antworten :)

In der Zeit wo ihr mir geantwortet hattet, hatte ich auch das zauberwort 
__packed gefunden.

Damit läuft es nun...nur ist leider nicht sauber da ja so ein alignment 
irgend nen grund hat :(


"Wenn du byteweisen Zugriff brauchst, dann bau die Struktur auch
byteweise auf und schiebe dir hinterher die Multibyte-Zahlen zusammen.
Was anderes könnte dir Hardware ja ohnehin nicht machen, aber damit
bist du dann portabel."

Wie meinste das nun?

Ich soll einfach nen Dickes Array speichern anstatt die Struktur?

Also z.b. nen Array von 6 Bytes und zerstückel es dann selber in die 
einzelnen Variablen?

Bei dem Zugriff läuft das bei mir so ab:
Ich kriege ein Wert, einen Subindex, entweder 0,1 oder 2 und der sagt 
mir welche der Variablen ich lesen oder schreiben will und dann setze 
ich ein Pointer auf die Adresse von der Struktur und wenn ich Wert 0 
will gebe ich ihn zurück, wenn ich Wert 1 will rechne ich den Pointer++ 
und gebe den Wert zurück und bei Wert 2 rechne ich den Pointer+5 und 
gebe den Wert zurück.

Ansich würde das auch gehen wenn ich das ganze nicht in Strukturen 
speicehre sondern in Arrays...aber das sieht dann total unsauber aus :(

An anderen Stellen des Programmes greife ich direkt auf den Wert in der 
Mitte der Struktur zu. Darum wäre die Möglichkeit alles in nem Array zu 
speichern eigentlich nicht so schön.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Woher kommt denn deine Anforderung nach dem Auslassen der Füllbytes?
Wenn das irgendwo durch Hardware bedingt ist bzw. externe Kommunikation,
dann ist das Array sicher die sinnvollste Lösung.

von Jens S. (jenss)


Lesenswert?

"...nach dem auslassen der füllbytes?" hm???

Also ich will die Füllbytes auslassen weil ich den Pointer nur um eins 
erhöhen will. Theoretisch könnte ich auch den Pointer, wenn ich Wert2 
lesen will, um 5 erhöhen anstatt nur um 1, aber das wäre ja auch 
getrickst.

Verändert sich das alginment wenn ich ne andere Optimierung wähle? Da 
das ausserdem ein Array von 128 dieser Strukturen betrifft, ist das doch 
ne riesen speicherverschwendung wenn ich das nicht packe.

Das ganze ist beim CANopen Protokoll, und zwar kriege ich per CAN 
Telegramm eine Objektnummer, die ich beschreiben oder lesen will. dann 
gucke ich in dem ersten Array, welche Variable zu der Nummergehoert und 
habe die Pointeradresse dadrauf. Wenn man nun SUbindex 1 lesen/schreiben 
will dann heißt das man möchte den zweiten Wert der Struktur(die von 
oben) lesen/schreiben. Der Pointer ist vom Type unsigned chjar und kann 
auch nicht ein pointer vom typ der struktur sein, weil er noch andere 
Werte lesen muss.

An anderen Stellen im Programm schreibe ich aber auch direkt bzw. lese 
sehr oft die mittlere Variable der Strukur aus. Darum wäre es sehr 
umständlich wenn ich dort immer erst den WErt aus einem Array zusammen 
setzen müsste.


Gibt es eine möglichkeit zu erkennen, das dort ein alginment vorgenommen 
wurde? Was mir auch sagt wieviele Bytes hinzugefügt wurden? Bestimmt 
nicht oder?

Die Strukturelemente zu tauschen würde nun ein riesen aufwand bedeuten 
aber falls das dann klappen würde wäre es ne Möglichkeit.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Warum greifst Du mit einem char-Pointer auf Strukturelemente anderen 
Typs zu?
Wenn Du das tust, kannst Du auch gleich mit char-Arrays arbeiten und Dir 
den Aufwand der Strukturdefinition sparen.

von Oliver (Gast)


Lesenswert?

>Damit läuft es nun...nur ist leider nicht sauber da ja so ein alignment
>irgend nen grund hat :(

Überlass das doch einfach dem Compiler. Solange das Programm macht, was 
du willst, und das auch schnell genug ist, brauchst du dir darüber nicht 
den Kopf zu zerbrechen.

Oliver

von Jens S. (jenss)


Lesenswert?

Warum ich ein Char Pointer nehme? Weil ich Byteweise die Daten sende und 
Byte für Byte von verschiedenen Variablentypen lese. Mal ist es eine 
Variable aus der Struktur, mal ein Integer und manchmal nur ein 
Charakter oder irgendwas anderes. Warum es kein Array sein kann? Weil 
ich in einem anderen Interrupt direkt nur auf den zweiten Wert der 
Struktur zugreife. Dort die Byteausleserei aus einem Array wäre zuviel 
Aufwand.

Das Problem mit dem Pack war nun die Performance :(
Mein Interrupt brauchte nun 20µs länger als vorher also anstatt 46µs nun 
67µs. Das war nun zu lange. Also habe ich es anders gelöst. Etwas 
umständlich aber es klappt. Ich habe nun noch ein zweiten Pointer vom 
Typ der Struktur der dann auf das jeweilige Element der Struktur zeigt 
und diese Adresse gebe ich dem anderen Charakter Pointer.


Danke für eure Hilfreichen Kommentare.

Gruß
Jens

von Rolf Magnus (Gast)


Lesenswert?

> "...nach dem auslassen der füllbytes?" hm???
>
> Also ich will die Füllbytes auslassen weil ich den Pointer nur um eins
> erhöhen will. Theoretisch könnte ich auch den Pointer, wenn ich Wert2
> lesen will, um 5 erhöhen anstatt nur um 1, aber das wäre ja auch
> getrickst.

Die Frage nochmal anders formuliert: Warum dürfen keine Füllbytes 
vorkommen?

> Verändert sich das alginment wenn ich ne andere Optimierung wähle?

Beim normalen -O* würde ich das nicht erwarten, da man in der Regel Code 
zusammenlinken kann, wo diese Einstellung verschieden ist, also muß das 
ABI kompatibel sein. Aber es gibt ja noch andere Optimierungs-Optionen, 
bei denen das evtl. anders ist. Es ist halt auch prozessorabhängig.

> Da das ausserdem ein Array von 128 dieser Strukturen betrifft, ist das
> doch ne riesen speicherverschwendung wenn ich das nicht packe.

Naja, kommt auf die Speicherkapazität an. 128*6 = 768 Bytes wäre der 
Overhead. Du mußt aber auch rechnen, daß bei einer Architektur, die 
Zugriffe mit falschem Alignment nicht unterstützt, zusätzlicher Code 
geschrieben werden muß, der ja auch Platz braucht und dazu noch bei 
jedem Zugriff Performance schluckt.
Wenn du deine Struktur so umsortieren kannst, daß die unsigned char alle 
am Ende stehen, wäre der Overhead nur 2 statt 6 Bytes.

> dann gucke ich in dem ersten Array, welche Variable zu der
> Nummergehoert und habe die Pointeradresse dadrauf. Wenn man nun
> SUbindex 1 lesen/schreiben will dann heißt das man möchte den zweiten
> Wert der Struktur(die von oben) lesen/schreiben.

Verstehe ich noch nicht ganz. Ist der Subindex nun ein Byte-Offset 
innerhalb der Struktur oder die Nummer des Elements?

> An anderen Stellen im Programm schreibe ich aber auch direkt bzw. lese
> sehr oft die mittlere Variable der Strukur aus. Darum wäre es sehr
> umständlich wenn ich dort immer erst den WErt aus einem Array zusammen
> setzen müsste.

Wenn dein Prozessor ein 32-Bit-Alignment dafür benötigt, wäre es noch 
viel umständlicher, wenn du jedesmal immer erst das Struct-Element in 
ein Array umkopieren und dann daraus deinen Wert zusammensetzen müßtest.

> Gibt es eine möglichkeit zu erkennen, das dort ein alginment
> vorgenommen wurde? Was mir auch sagt wieviele Bytes hinzugefügt wurden?
> Bestimmt nicht oder?

Du kannst offsetof verwenden, um die Position eines Elements innerhalb 
einer Struct zu ermitteln. Daraus kannst du dir die Füllbytes 
ausrechnen. Aber generisch geht das natürlich nicht. Du mußt dem 
offsetof immer den Namen der Struktur und des Elements übergeben.

> Die Strukturelemente zu tauschen würde nun ein riesen aufwand bedeuten

Warum?

von Jens S. (jenss)


Lesenswert?

Der Subindex ist die Nummer des Strukturelements:
Subindex 0: erste Strukturelement unsigned char
Subindex 1: zweite Strukturelement unsigned long int
Subindex 2: dritte Strukturelement unsigned char


Die Struktur zu tauschen wäre nen riesen aufwand weil ich mein riesen 
Array umschreiben muss. Das ist ja schon mit Defaultwerten gefüllt.


Also wenn ich das dingen Packe geht mir einfach zuviel Performance 
verloren da ich ja sehr schnell zugreifen muss. Die möglichkeit nun mit 
dem Zweiten Pointer erscheint mir die beste.

Ich werde nun aber doch noch einmal Testen ob ich die Struktur 
umschreibe so das die unsigend char am ende sind und ich damit weniger 
Overhead kriege.

von Rolf Magnus (Gast)


Lesenswert?

> Der Subindex ist die Nummer des Strukturelements:

Gut, das heißt aber dann, daß du in der Software noch irgendwo eine 
Tabelle haben mußt, wo die Zuordnung zwischen Nummer und Offset steht. 
Wenn die genannte Struktur gepackt wäre, wären die Adressen ja so 
verteilt:

> Subindex 0: erste Strukturelement unsigned char
 @ Struct-Anfang
> Subindex 1: zweite Strukturelement unsigned long int
 @ Struct-Anfang + 1
> Subindex 2: dritte Strukturelement unsigned char
 @ Struct-Anfang + 5

Wenn du so eine Tabelle hast, wäre es doch völlig problemlos möglich, 
die Elemente der Struktur zu tauschen und dann einfach die Offsets in 
der Tabelle anzupassen.

> Die Struktur zu tauschen wäre nen riesen aufwand weil ich mein riesen
> Array umschreiben muss. Das ist ja schon mit Defaultwerten gefüllt.

Ach so. In C gibt's eine Syntax, mit der das kein Problem wäre. 
Vielleicht eine Überlegung für nächstes Mal:
1
struct Blah
2
{
3
    int a;
4
    int b;
5
    int c;
6
};
7
8
struct Blah foo = { .a =  3,
9
                    .b = 5,
10
                    .c = 2 };

Die Reihenfolge der Elemente spielt hier keine Rolle mehr, sondern nur 
noch deren Namen.

von Jens S. (jenss)


Lesenswert?

Hey Danke für den Tip mit der andere deklarationssyntax. Die kannte ich 
noch nicht. Das macht die Sache natürlich echt praktisch.

Durch meine jetzige Lösung ist es ja auch egal an welcher Stelle die 
Variable steht so das ich garkeine Offsets mehr berechnen muss.

Verkürzt sieht das nun so aus:
1
pucStrukturPointer = (Structur1 *)pucCharakterPointer;
2
switch(Subindex)  
3
{
4
   case 0:
5
         pucCharakterPointer = (unsigned char *) &pucStrukturPointer->Wert1;
6
   break;
7
8
usw...

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.