Hallo,
wenn ich einen Array mit dem struct definiere, welcher 3 Byte
beinhaltet. Ist dann garantiert, dass N * 3 Byte für das Array im RAM
belegt werden?
Beispiel für den struct
Nein, es könnten auch N * 4 Bytes sein (Maschine mit 16-bit int und
Speicherausrichtung, eher selten) oder N * 8 Bytes sein (Maschine
mit 32-bit int und Speicherausrichtung, sehr häufig anzutreffen).
Auch muss ein Byte nicht zwingend 8 Bits haben, allerdings sind
Maschinen, auf denen das nicht der Fall ist, eher seltene Vögel
(irgendwelche DSPs, die als kleinste Einheit 32-bit-Zahlen
adressieren können).
Wäre es eine Lösung alles als "Byte-Stream" in den RAM zu schreiben und
dann per Lese-Routine, die Daten in char, int etc. Variablen zu laden,
wie sie gerade benötigt werden ?
quasi:
ggf. das ganze dann bei Bedarf in Struktur laden.
Das würde mir dazu spontan einfallen.
Also char Array, und dann bei Bedarf in lokale Struktur laden.
Oder seh ich da was falsch?
@Sebastian B.
In ANSI-C sind für die verschiedenen Typen nur Mindestbereiche
angegeben, die man damit darstellen können muss. Wieviele Bits/Bytes sie
im einzelnen belegen, garantiert einem da keiner. Ich sehe da nur die
Möglichkeit, an einer Stelle entsprechende Typen zu erstellen (so was
wie uint8_t, uint16_t), und Anweisungen an den jeweiligen Compiler,
diese Daten entsprechend gepackt werden sollen. Um manuelle Eingriffe
beim Portieren auf andere Systeme/Compiler kommt man wohl nicht ganz
herum.
Gast wrote:
> Wer sagt denn das byte_array[1] immer das High-Byte und> byte_array[2] immer das Low Byte ist.
Die Definition, was wo in byte_array[] steht.
Normalerweise macht man so einen Zirkus ja nur, wenn das Array über
bspw. ein externes Protokoll gefüllt wird, dessen Daten man intern
weiter benutzen möchte (Netzwerkprotokoll, SCSI-Protokoll, als zwei
Beispiele, bei denen ich das so schon gesehen habe).
Jörg Wunsch wrote:
> Normalerweise macht man so einen Zirkus ja nur, wenn das Array über> bspw. ein externes Protokoll gefüllt wird, dessen Daten man intern> weiter benutzen möchte (Netzwerkprotokoll, SCSI-Protokoll, als zwei> Beispiele, bei denen ich das so schon gesehen habe).
Bei mir dreht sicher der Zirkus um eine "Befehls-Sequenz", die ich aus
dem EEPROM lade. Diese Befehlssequent besteht jeweils aus einer 1 Byte
ID und einem 2 Byte Wert. Deshalb auch die 3 Byte ;-)
Mir ging es nur darum, dass ich nicht zu viel Platz im RAM
"verschwenden" möchte, in dem ich die Sequenz schön formatiert in ein
Array mit dem Typ struct lade.
Ich gehe nun so vor, dass ich den EEPROM Inhalt ins RAM "spiegle".. und
dann bei Bedarf die Daten mit einem u8* Zeiger in meine "lokale
Struktur" lade um damit an dieser Stelle damit weiter zumachen.
Hintergrund:
---------------
Die Abarbeitung, also auch das Laden der Sequenzschritte direkt aus dem
EEPROM funktioniert, aber wird natürlich durhc die langsame
Zugriffsgeschwindigkeit ausgebremst. Ich wollte die Abarbeitung jetzt
beschleunigen in dem ich den EEPROM Inhalt zu Beginn komplett in den RAM
lade.
Gruss
Christian R. wrote:
> Dafür gibts doch #pragma pack(), inwieweit das von allen Kompilern und> Zielplattformen unterstützt wird, weiß ich aber auch nicht.
Da wir hier im GCC-Forum sind: gar nicht. GCC nimmt keine Pragmas,
weil Pragmas ein miserabel durchdachtes Konzept sind. (Sie stehen
zu weit weg von den Dingen, die man deklariert, damit ist man nicht
sehr ,,zielgenau''.)
GCC nimmt _attribute_, und so sollte es nicht wundern, dass man
das mit __attribute__((pack)) macht.
Manuel Stahl wrote:
> Zumindest der avr-gcc versteht als Option -fpack-structs
Das bringt leider nichts, weil dann alle Structs/Unions gepackt werden
und nicht nur die gewünschte. Ausserdem müssten dann alle Module und
Bibliotheken neu übersetzt werden, um Interlinkfähigkeit zu garantieren.
was ist hier mit 4er grenze gemeint?
struct beispiel_struct{
unsigned char dummy; /* für 4er Grenze */
unsigned char byte_1; /* 1 Byte */
unsigned int byte_2_3; /* 2 Byte */
};
und wieso können das N*8 byte sein (jetzt ohne den dummy) bei ner 32 bit
maschine sind doch N*4 byte oder??
mfg. muhkuh
muhkuh wrote:
> und wieso können das N*8 byte sein (jetzt ohne den dummy) bei ner 32 bit> maschine sind doch N*4 byte oder??
Weil der enthaltene 32-bit-int am Schluss steht und normalerweise
auf einer 32-bit-Grenze ausgerichtet wird. Manche CPUs erzeugen
einen Trap, wenn sie `unaligned'-Zugriffe bekommen, andere CPUs
(wie IA32) arbeiten weniger effektiv, da sie sich die Zahl über
Byte-Extraktion aus dem Ergebnis zweier Buszyklen zusammennageln
müssen. Die Zeiten, da die Busse nur 8 bit breit waren, sind ja
schon ein Weilchen vorbei.
Daher ist es eine gute Idee, structs "vom Großen zum Kleinen"
aufzufüllen, d. h. zuerst alle 64-bit-Daten (so es welche gibt),
dann alle mit 32 bits, usw. Auf diese Weise wäre ggf. höchstens
am Ende ein Auffüllen mit ungenutzten Bytes notwendig (padding).
Wenn man sie ,,wild'' mischt, dann würde das Padding auch zwischen
den einzelnen Teilen erfolgen und daher mehr Platz verschwendet.
Wären dann aber nicht im schlimmsten Fall 3 Füllbytes zwischen dem Char
und dem Int? das würde dann insgesamt 8 Byte machen aber wie kommt man
dann auf N*8 also 16 Byte?
Sollte doch wie folgt im Speicher liegen:
char Byte, freies Byte, freies Byte, freies Byte, int (4 Byte) und dann
aus also in Summe 8 Byte.
Auf 32 Bit Maschinen ist das Alignment ja 4, nicht 8 oder?
Verstehe folgendes nicht ganz:
Nein, es könnten auch N * 4 Bytes sein (Maschine mit 16-bit int und
Speicherausrichtung, eher selten) oder N * 8 Bytes sein (Maschine
mit 32-bit int und Speicherausrichtung, sehr häufig anzutreffen).
muhkuh wrote:
> Wären dann aber nicht im schlimmsten Fall 3 Füllbytes zwischen dem Char> und dem Int? das würde dann insgesamt 8 Byte machen aber wie kommt man> dann auf N*8 also 16 Byte?
Die letzte Schlussfolgerung (die N = 2 impliziert) ist einfach
falsch.
N war die Größe des Arrays.
> Verstehe folgendes nicht ganz:> Nein, es könnten auch N * 4 Bytes sein (Maschine mit 16-bit int und> Speicherausrichtung, eher selten) oder N * 8 Bytes sein (Maschine> mit 32-bit int und Speicherausrichtung, sehr häufig anzutreffen).Was genau verstehst du daran nicht?
Wieso es bei 16 Bit systemen N * 4 <- versteh die 4 Byte nicht und bei
32 Bit Systemen N * 8 Byte sind.
Dachte es wären N*2 bei 16 Bit und N*4 bei 32 Bit Systemen.
muhkuh wrote:
> Wieso es bei 16 Bit systemen N * 4 <- versteh die 4 Byte nicht und bei> 32 Bit Systemen N * 8 Byte sind.>> Dachte es wären N*2 bei 16 Bit und N*4 bei 32 Bit Systemen.
Du hast immer noch nicht kapiert, dass "N" beim OP die Anzahl der
Elemente seines Arrays sein sollte, glaub ich. Folglich belegt das
Array N*<irgendwas> an Speicher.
Die 4 gibt es nur bei 16-bit-Systemen, die ein memory alignment
benötigen, weil dann der uint16_t auf einer 16-bit-Grenze ausgerichtet
werden muss. Dafür muss nach dem uint8_t-Element ein padding byte
eingefügt werden. Solche Systeme sind aber heutzutage eher selten
anzutreffen (16-bit-Systeme wohl insgesamt), eine PDP-11 fiele mir
auf Anhieb noch ein. Andere 16-bit-Systeme wie 8086 haben kein
Alignment benötigt (und hätten von einem solchen, im Gegensatz zu den
Nachfolgern ab 80386 auch nicht profitiert).
Bei 8-bit-Systemen wiederum braucht man kein Alignment, da ja der
Bus sowieso immer in Einheiten von 8 Bit zugegriffen wird.
Sebastian B. wrote:
> Hallo,>> wenn ich einen Array mit dem struct definiere, welcher 3 Byte> beinhaltet. Ist dann garantiert, dass N * 3 Byte für das Array im RAM> belegt werden?
Von sowas auszugehen ist ein Design-Fehler, das haengt von der
Implementierung im Compiler ab, die nicht nur von Compiler zu Compiler
sondern auch von Version zu Version verschieden sein kann.
Jörg Wunsch schrub:
> Da wir hier im GCC-Forum sind: gar nicht. GCC nimmt keine Pragmas,> weil Pragmas ein miserabel durchdachtes Konzept sind. (Sie stehen> zu weit weg von den Dingen, die man deklariert, damit ist man nicht> sehr ,,zielgenau''.)http://gcc.gnu.org/onlinedocs/gcc/Structure_002dPacking-Pragmas.html
Ist das beim AVR-GCC etwa anders?
Diese pragmas funktionieren beim GCC nicht in jedem Fall.
Der MinGW kann damit umgehen, wobei das push/pop anscheinend
nicht geht.
Der arm-gcc ignoriert dieses pragma kommentarlos.
Jeder ernstzunehmende C-Compiler kann Strukturen packen. Nur
leider ist die Syntax für die Deklaration nicht einheitlich.
> War mir komplett neu.
Mir auch.
> Möglicherweise funktioniert das damit auch beim AVR-GCC.
Nein:
warning: ignoring #pragma pack
Aber der AVR-GCC braucht so etwas ja auch nicht, weil er Strukturen
sowieso ins Byte-Raster packt (also wie pack(1)).
yalu wrote:
> Aber der AVR-GCC braucht so etwas ja auch nicht, weil er Strukturen> sowieso ins Byte-Raster packt (also wie pack(1)).
Immer, oder nur mit dem packed Attribut? Wenn der das immer macht, wäre
es ja auch ziemlich gefahrlich, da man dann zwische meinetwegen einem
AVR und einem Windows Programm nicht einfach eine Struktur 1:1
übertragen könnte, wenn man nicht den PC-Kompiler wiederum anweist, auf
1-Byte zu packen....
Christian R. wrote:
> Immer, oder nur mit dem packed Attribut? Wenn der das immer macht, wäre> es ja auch ziemlich gefahrlich, da man dann zwische meinetwegen einem> AVR und einem Windows Programm nicht einfach eine Struktur 1:1> übertragen könnte, wenn man nicht den PC-Kompiler wiederum anweist, auf> 1-Byte zu packen....
Das ist nicht gefährlich, sondern C. structs sind ja nicht in erster
Linie dafür erfunden worden, dass man sie bestmöglich zwischen einem
Abakus und einer Cray hin und her transferieren kann, sondern das
Programm auf dem Computer soll damit optimal arbeiten können.
Extern ist alles ein "octet stream", und um Dinge wie padding, byte
order etc. musst du dir beim Datenaustausch Gedanken machen. Dafür
gibt's im OSI-Modell eine eigene Schicht (presentation layer).
Christian R. wrote:
> Ich meinte eher: Gefährlich, wenn man es nicht weiß, und Daten über> Strukturen austauschen will.
Dann hat man die falsche Aufgabe, oder die fehlenden Vorkenntnisse
für die Aufgabe.
Es gibt halt auch Maschinen, bei denen man einfach mit
__attribute__((packed)) nicht einmal mehr lauffähigen Code bekäme
(praktisch alle 32- und 64-bit-RISC-Systeme fallen hier hinein),
andererseits wirst du nicht automatisch 64-bit-padding-Regeln auf
einem AVR anwenden wollen, nur damit die Speicherstrukturen
binärkompatibel zu einer UltraSPARC sind.
Wer also blindlings Speicherstrukturen 1:1 zwischen völlig verschiedenen
Architekturen austauscht, hat irgendwo einen prinzipiellen Fehler
gemacht.
Jörg Wunsch wrote:
> Es gibt halt auch Maschinen, bei denen man einfach mit> __attribute__((packed)) nicht einmal mehr lauffähigen Code bekäme> (praktisch alle 32- und 64-bit-RISC-Systeme fallen hier hinein),
Zum Beispiel?
Das Attribut sagt dem Compiler doch gerade, dass er aufpassen muss, da
dies Daten möglicherweise(!) unaligned im Speicher liegen. Es wird daher
Code erzeugt, der auf die Daten schlimmstenfalls byte-weise zugreift und
sie im Prozessor wieder zusammenbaut.
Einige RISC Architekturen (ARM seit v6) erlauben durchaus "echte"
unaligned Zugriffe.
Gruß
Marcus
PS: Irgendwie habe ich das Gefühl, das habe ich letztens in einem
anderen Thread auch schon geschrieben.
Marcus Harnisch wrote:
>> Es gibt halt auch Maschinen, bei denen man einfach mit>> __attribute__((packed)) nicht einmal mehr lauffähigen Code bekäme>> (praktisch alle 32- und 64-bit-RISC-Systeme fallen hier hinein),> Zum Beispiel?
sparc64 (aka. UltraSPARC)
> Das Attribut sagt dem Compiler doch gerade, dass er aufpassen muss, da> dies Daten möglicherweise(!) unaligned im Speicher liegen. Es wird daher> Code erzeugt, der auf die Daten schlimmstenfalls byte-weise zugreift und> sie im Prozessor wieder zusammenbaut.
Nö. Der packt die Strukturen einfach, wenn du dann drauf zugreifen
willst, knallt es (unaligned trap). Du hast ihn ja mit dem
__attribute__((packed)) explizit daran gehindert, die Struktur
so aufzubauen, dass man sie auch sinnvoll benutzen kann.
> Einige RISC Architekturen (ARM seit v6) erlauben durchaus "echte"> unaligned Zugriffe.
Ja, aber die sind dann teurer, weil sie mehrere Buszyklen brauchen.
So ist es ja letztlich auch bei IA32.
Sicher muss man wissen, ob die entsprechenden an der Übertragung
beteiligten Architekturen das unterstützen. Ich schreib ja auch keinen
Code, der ohne Änderung auf einem Win32 PC und einer Sparc 64 irgendwas
laufen soll.
Beim ARM7 (ARMv4) kann man unter Verwendung des arm-gcc oder RealView
auf gepackte Strukturen zugreifen. Die Compiler erzeugen entsprechenden
Code um die Daten passend zurecht zu schieben. Ohne dieses Feature
wären die Compiler für RISC Prozessoren wohl auch kaum zu gebrauchen.
Der TCP-Stack lwip würde dann auch auf keiner RISC Architektur
laufen, da der heftig Gebrauch von gepackten Strukturen macht.
Anders sieht es beim Casten aus. Nach einer Datenübertragung liegen die
Strukturen ja zunächst als Byte Array vor.
1
voidprocessData(uint8_t*au8Data)
2
{
3
structSomeStruct*ptr=(structSomeStruct*)au8Data;
4
if(ptr->element==0){
5
...
6
}
7
}
Wenn au8Data nicht auf einen ausgerichteten Block zeigt knallt es beim
Zugriff auf ptr->element. Mit oder ohne (packed). Dafür gibt es
aber beim gcc das __attribute__((aligned)).
Jörg Wunsch wrote:
> sparc64 (aka. UltraSPARC)Architekturen gibt es viele, die unaligned nicht unterstützen. Und es
ist Aufgabe des Compilers dafür zu sorgen, dass die Zugriffe auf
unaligned Daten geeignet ausgeführt werden.
> Nö. Der packt die Strukturen einfach, wenn du dann drauf zugreifen> willst, knallt es (unaligned trap).
Den Compiler würde ich als ungeeignet bezeichnen.
> Ja, aber die sind dann teurer, weil sie mehrere Buszyklen brauchen.> So ist es ja letztlich auch bei IA32.
Die Buszyklen hast du zwangsläufig. Egal ob vom Compiler generiert, oder
von der Hardware. Im letzteren Fall spart man Befehlszyklen und
(theoretisch) hat man die Möglichkeit das ganze noch atomar zu
gestalten. Außerdem kann die Hardware erkennen, ob der Zugriff
tatsächlich unaligned ist und den optimalen Zugriff für den gegebenen
Fall ausführen.
Gruß
Marcus
Bisher (AVR 8-bit und C167 16-bit) hab ich auch immer die packed
Variante für Parser verwendet, weil es im Code schön aussieht.
Vielleicht sollte man mal ein "best practise" Parser Tutorial schreiben.
Das kommt grade bei µC denke ich sehr häufig vor.
let wrote:
> Anders sieht es beim Casten aus. Nach einer Datenübertragung liegen die> Strukturen ja zunächst als Byte Array vor.
Kann natürlich sein, dass aus diesem Grunde alle XDR-artigen Codes,
die ich bislang gesehen habe, sich die internen Datenstrukturen aus
dem externen Octet-Stream ,,zu Fuß'' zusammen gebaut haben.
Ich müsste mal meine UltraSPARC wieder anwerfen um nachzusehen, ob
der GCC mir dort wirklich bei __attribute__((packed)) alls auf
Bytezugriffe umfummelt.
>> Wenn au8Data nicht auf einen ausgerichteten Block zeigt knallt es beim> Zugriff auf ptr->element. Mit oder ohne (packed).
Kann ich zumindest mit dem RealView Compiler (3.1) nicht nachvollziehen,
wenn die Struktur korrekterweise als packed deklariert wurde.
> Dafür gibt es aber beim gcc das __attribute__((aligned)).
Das geht aber nur, wenn entweder die Struktur eine geeignete Größe hat
(Vielfaches von 2,4,8 Bytes, je nach Prozessor). Sonst knallt es beim
Zugriff auf die zweite Struktur in der Byte Folge.
Gruß
Marcus
Ja, das (aligned) hilft nur bedingt. Wenn mehrere Strukturen
hintereinander liegen hat man verloren. Dann hilft nur noch
ein memcpy() in einen ausgerichteten Block (oder eine Variable).
Aber was macht denn der RealView bei einem solchen Konstrukt
das ich früher auf einem x86 hemmungslos so hingeschrieben
hätte:
1
switch(au8Data[0]){
2
case123:
3
processData(dptr+1);
4
break;
5
}
Hier kann au8Data noch so aligned sein, processData() bekommt
eine ungerade Adresse. Prüft der bei jedem Cast von einen
Datentyp < 4 Bytes auf einen größeren ob die Adresse durch vier
teilbar ist?
Das würde erklären weshalb die Jungs bei Keil in ihren Demos
mit casts nur so um sich werfen. Nur wie sieht es dann mit der
Effizienz aus? Nur für den Fall das der Programmierer ausnahmsweise
einmal weiß was er tut ;)
let wrote:
> Ja, das (aligned) hilft nur bedingt. Wenn mehrere Strukturen> hintereinander liegen hat man verloren. Dann hilft nur noch> ein memcpy() in einen ausgerichteten Block (oder eine Variable).
Und auch der wird unter Umständen Byte-weise ausgeführt, da man ja aus
einem Byte array liest. Damit hat man also nicht unbedingt an Effizienz
gewonnen. Kommt darauf an, wie oft man hinterher auf die Struktur
zugreift.
>
1
>switch(au8Data[0]){
2
>case123:
3
>processData(au8Data+1);
4
>break;
5
>}
6
>
Mit der aufrufenden Funktion macht der gar nichts. Egal was der
Parameter für einen Wert hat, es ist immer ein gültiger (uint8_t *).
Damit ist dem Prototypen genüge getan. Fall erledigt.
> Prüft der bei jedem Cast von einen Datentyp < 4 Bytes auf einen> größeren ob die Adresse durch vier teilbar ist?
Nein, warum auch? Wir haben die Struktur als packed deklariert, und
damit dem Compiler gesagt, dass er sie beliebig im Speicher anlegen
darf.
Anhand der statisch ermittelbaren Information wird der Compiler die
bestmögliche Methode verwenden, auf die Daten zuzugreifen. Damit hat der
Compiler seine Aufgabe erfüllt. Der Overhead einer Fallunterscheidung
zur Laufzeit würde den Vorteil des im Einzelfall schnelleren Zugriffs
wahrscheinlich zunichte machen.
> Nur wie sieht es dann mit der Effizienz aus? Nur für den Fall das der> Programmierer ausnahmsweise einmal weiß was er tut ;)
Der Compiler erzeugt vorrangig eine korrekte Instruktionssequenz
(abgesehen von Fehlern in der Implementierung). Die kann -- nachrangig
-- nur so effizient sein, wie es die darunter liegende Maschine zulässt.
Wenn wir (die Programmierer) es besser wissen, dann hätten wir die
Struktur ja intelligenter aufbauen können.
Gruß
Marcus
Marcus Harnisch wrote:
> Jörg Wunsch wrote:>> sparc64 (aka. UltraSPARC)>> Architekturen gibt es viele, die unaligned nicht unterstützen. Und es> ist Aufgabe des Compilers dafür zu sorgen, dass die Zugriffe auf> unaligned Daten geeignet ausgeführt werden.>>> Nö. Der packt die Strukturen einfach, wenn du dann drauf zugreifen>> willst, knallt es (unaligned trap).>> Den Compiler würde ich als ungeeignet bezeichnen.
Ich würd nicht sagen "ungeeignet", ich würd sagen "Bug".
Johann L. wrote:
> Ich würd nicht sagen "ungeeignet", ich würd sagen "Bug".
Ein Bug wäre es nur, wenn das eine zugesicherte Eigenschaft ist, dass
auch __attribute__((packed))-Strukturen unabhängig vom misalignment
trotzdem zugreifbar sind. Ich finde erst einmal keine derartige
Zusicherung in der GCC-Doku, ich finde aber auch keine Bemerkung, dass
es nicht funktionieren würde.
@mharnisch:
Da haben wir wohl aneinander vorbei geschrieben.
Also noch mal zusammenhängend:
1
uint8_tau8GlblRecBuffer[256];
2
structSomeStruct{
3
uint8_tdummy;
4
uint32_telement;
5
}__attribute__((packed));
6
7
voidprocessData(uint8_t*au8Data)
8
{
9
structSomeStruct*ptr=(structSomeStruct*)au8Data;
10
if(ptr->element==0){
11
...
12
}
13
}
14
15
...
16
17
switch(au8GlblRecBuffer[0]){
18
case123:
19
processData(au8RecBuffer+1);
20
break;
21
}
'processData()' bekommt eine ungerade Adresse und damit ist auch
'ptr' ungerade. Ich würde mich beim arm-gcc nicht darauf verlassen
das da etwas sinnvolles herauskommt. Im Prinzip sind wir hier
ja wieder bei den Arrays von Strukturen.
Nachtrag : Ich lese gerade das (packed) die Alignment-Anforderung
für die gesamte Struktur aufhebt, also nicht nur das Padding verhindert.
Was ich meinte (und eigentlich haben will) ist folgendes:
1
structSomeStruct{
2
uint8_ta;
3
uint32_tb__attribute__((packed));
4
}__attribute__((aligned));
Diese Struktur ist 8 Bytes groß, zwischen 'a' und 'b' befinden
sich aber keine Füllbytes. Der Zugriff auf die Elemente ist
wesentlich effizienter da der Compiler jetzt mit Wortzugriffen
dabeigeht und mit shift+or sich die Elemente zusammenbaut.
Das geht natürlich nur wenn die Strukturen auf ausgerichteten
Adressen liegen.
Also gehe ich nochmal durch die lwip-Sourcen...
Ohne __attribute__((aligned)) im letzten Beispiel scheint eine
5-Bytes Struktur herauszukommen die dennoch an einer ausgerichteten
Adresse liegen muß. Das muß ich an einem Trace-fähigen Aufbau mal
näher untersuchen.
let wrote:
> Also noch mal zusammenhängend:>> [...]>> Nachtrag : [...]> Ich lese gerade das (packed) die Alignment-Anforderung> für die gesamte Struktur aufhebt, also nicht nur das Padding verhindert.
Also diese Problematik ist klar, oder? Durch die Parameter Deklaration
wird dem Compiler ja explizit gesagt, dass er ungerade Adressen zu
erwarten hat.
> Was ich meinte (und eigentlich haben will) ist folgendes:>
1
>structSomeStruct{
2
>uint8_ta;
3
>uint32_tb__attribute__((packed));
4
>}__attribute__((aligned));
5
>
>> Diese Struktur ist 8 Bytes groß, zwischen 'a' und 'b' befinden> sich aber keine Füllbytes. Der Zugriff auf die Elemente ist> wesentlich effizienter da der Compiler jetzt mit Wortzugriffen> dabeigeht und mit shift+or sich die Elemente zusammenbaut.> Das geht natürlich nur wenn die Strukturen auf ausgerichteten> Adressen liegen.
Ich hab's nicht ausprobiert, aber ich möchte das "wesentlich"
bezweifeln.
Das Element b ist ja schließlich immer noch packed (alignment 1) und muß
nach wie vor bei jedem Zugriff durch Einzelzugriffe zusammengebastelt
werden. Ich fürchte, Du hast das Problem nur verschoben.
Nach dem ersten Zugriff wird der Compiler ohnehin versuchen, die
Elemente in Registern zu halten. Wenn Du nicht gerade -O0 (oder
volatile) verwendest ist das alles vermutlich nicht so tragisch, wie es
erscheint.
Wenn Du dem Compiler nicht traust, dann könntest Du lokale Variablen für
die Strukturelemente anlegen (oder eine lokale, ungepackte Version der
Struktur erzeugen).
Gruß
Marcus
BTW: Ich hätte da einen Kurs anzubieten, der sich mit dem RealView
Compiler beschäftigt :-)
Wenn es also nur darum geht die Löcher innerhalb einer Struktur
zu stopfen, der Anfang der Struktur aber an einer ausgerichteten
Adresse liegt (ist ja oft der Fall), ist es besser nur die Elemente
der Struktur zu packen.
Durch diese Seite bin ich darauf gekommen es mal anders zu probieren:
http://digitalvampire.org/blog/index.php/2006/07/31/why-you-shouldnt-use-__attribute__packed/
let wrote:
> Wenn es also nur darum geht die Löcher innerhalb einer Struktur> zu stopfen, der Anfang der Struktur aber an einer ausgerichteten> Adresse liegt (ist ja oft der Fall), ist es besser nur die Elemente> der Struktur zu packen.
Wenn man das sagen kann, klar. Aber es ging dem OP schließlich darum,
dass "ein C Struct genau 3 Byte im Speicher belegt". Wenn Du einzelne
Strukturelemente packst, hast Du möglicherweise immernoch ein Loch am
Ende.
Fällt unter den Punkt "Wenn wir (die Programmierer) es besser wissen,
dann hätten wir die Struktur ja intelligenter aufbauen können."
Nebenbei traue ich dem geposteten Code nicht. Erstmal ist er als
C-Programm dargestellt unleserlich und zweitens fehlen Informationen
(und komischerweise sogar Instruktionen, oder was soll "r3, r3, #117"
bedeuten?).
Probier's mal mit gcc -O2 -S -c <file> -o <file>.S
Dann möchte ich auf (hüstel) den Abschnitt "Wichtige Regeln - erst
lesen, dann posten!" hinweisen. Insbesondere auf die Stelle "(z.B. Code
in anderen Sprachen)".
Drittens hast Du in beiden Fällen eine Struktur generiert, deren
Elemente ein alignment von 1 haben. Damit wird das aligment der gesamten
Struktur in beiden Fällen ebenfalls auf eins gesetzt, so dass das
Speicherlayout gleich aussieht. Sowohl RealView 3.1, als auch
CodeSourcery GCC 2008q3-39
erzeugen für die Zugriffe auf beide Strukturvarianten wie erwartet
denselben Code.
Marcus Harnisch wrote:
> Sowohl RealView 3.1, als auch CodeSourcery GCC 2008q3-39> erzeugen für die Zugriffe auf beide Strukturvarianten wie erwartet> denselben Code.
Soll heißen:
RealView erzeugt für beide Fälle den selben Code, und GCC auch. RealView
und GCC erzeugen natürlich voneinander unterschiedlichen Code.
--
Marcus
Jörg Wunsch wrote:
> Johann L. wrote:>>> Ich würd nicht sagen "ungeeignet", ich würd sagen "Bug".>> Ein Bug wäre es nur, wenn das eine zugesicherte Eigenschaft ist, dass> auch __attribute__((packed))-Strukturen unabhängig vom misalignment> trotzdem zugreifbar sind. Ich finde erst einmal keine derartige> Zusicherung in der GCC-Doku, ich finde aber auch keine Bemerkung, dass> es nicht funktionieren würde.
Stimmt, spezifiziert ist es nicht.
Gleichwohl kann GCC das prinzipiell. Und es ist immer netter, Code zu
haben, der funktioniert, als solcher, der einem zur Laufzeit um die
Ohren fliegt wegen einer Trap weil das Alignment bei einem Zugriff nicht
stimmte.
GCC weiß auch, wie man Bitfelder auseinanderbröseln/zusammensetzen muss,
die über Bytegrenzen hinweg gehen. Die entsprechenden Pattern sind
immerhin Pflichtpattern in der Maschinenbeschreibung (insv, extv).
Weißt du zufällig auch, ob das Feature in der testsuite überprüft wird?
In dem Falle sollte es dann auch in der Doku erwähnt werden, ab dann
darf man sich drauf verlassen.
Johann L. wrote:
> Jörg Wunsch wrote:>> Ein Bug wäre es nur, wenn das eine zugesicherte Eigenschaft ist, dass>> auch __attribute__((packed))-Strukturen unabhängig vom misalignment>> trotzdem zugreifbar sind. Ich finde erst einmal keine derartige>> Zusicherung in der GCC-Doku, ich finde aber auch keine Bemerkung, dass>> es nicht funktionieren würde.>> Stimmt, spezifiziert ist es nicht.
Es wäre schon besser wenn sich alle Compiler an das ABI für die
jeweilige Architektur halten würden, und dafür funktionierenden Code
erzeugen. Zum Beispiel sind Strukturlayout und -alignment für ARM
Prozessoren im AAPCS "4.3 Composite Types" erklärt. Für andere
Prozessoren ist das sicher ähnlich.
Das selbe gilt für Dinge wie Bit-fields ("7.1.7 Bit-fields"), etc.
Gruß
Marcus
Jörg Wunsch wrote:
> Weißt du zufällig auch, ob das Feature in der testsuite überprüft wird?> In dem Falle sollte es dann auch in der Doku erwähnt werden, ab dann> darf man sich drauf verlassen.
__attribute__((packed)) wird in der Testsuite hier und da behandelt und
ist in einigen C-Dateien drinne, etwa in
./gcc/testsuite/gcc.c-torture/execute/20010518-2.c
http://gcc.gnu.org/viewcvs/trunk/gcc/testsuite/gcc.c-torture/execute/20010518-2.c?revision=138078&view=markup
Ich hab aber nicht den Überblick über alle Testprogramme, das hier war
einfach eines ausm grep.
Johann L. wrote:
> ./gcc/testsuite/gcc.c-torture/execute/20010518-2.c
Das sieht gut aus, da wird das wirklich explizit getestet. Wenn ich
dran denke, werde ich mal eine Patch für die Doku bauen und versuchen,
durch die GCC-Patch-Queue zu prügeln.
let wrote:
> O.k, o.k, ich habe das nur schnell eben hingeschmiert.> Nochmal etwas ordentlicher:
Vielen Dank :-)
Zunächst mal ist es wichtig, daran zu denken, dass Du immer nur auf das
erste Element zugreifst, und nicht auf beliebige Elemente des Arrays.
Externe Deklaration der Variablen kann das Bild verändern.
> Ganz ohne Attribut>
1
> ldr r3, .L3
2
> mov r2, #123
3
> mov r0, #0
4
> str r2, [r3, #4]
5
> bx lr
6
> .L4:
7
> .align 2
8
> .L3:
9
> .word data
10
> .size main, .-main
11
> .comm data,128,1
12
>
Das knallt erwartungsgemäß (bei ARMv4/5), wenn data ein alignment != 4
hat.
Die .comm Direktive gibt sogar explizit ein Alignment des ersten
Elements von 1 vor, oder? Kann ein GCC Kenner das beurteilen?
Ist aber ein klarer Programmierfehler. Wir haben den Compiler getäuscht.
> Mit (packed) für 'b':>
1
> ldr r3, .L3
2
> ldrb r2, [r3, #0] @ zero_extendqisi2
3
> mov r0, #0
4
> orr r2, r2, #31488
5
> str r2, [r3, #0]
6
> strb r0, [r3, #4]
7
> bx lr
8
> .L4:
9
> .align 2
10
> .L3:
11
> .word data
12
> .size main, .-main
13
> .comm data,128,1
14
>
Das knallt auch. Hier "optimiert" der Compiler, indem er ebenfalls
annimmt, dass data ein alignment von vier Bytes hat. Allerdings wird
data hier wieder mit einem alignment von 1 angelegt.
> Mit beiden (packed) Attributen:>
1
> mov r0, #0
2
> ldr r3, .L3
3
> orr r2, r0, #123
4
> strb r2, [r3, #1]
5
> strb r0, [r3, #4]
6
> strb r0, [r3, #2]
7
> strb r0, [r3, #3]
8
> bx lr
9
> .L4:
10
> .align 2
11
> .L3:
12
> .word data
13
> .size main, .-main
14
> .comm data,128,1
15
>
Die einzig sichere Methode für diesen Fall. Etwas weniger effizient
zwar, dafür in jedem Fall korrekt.
Es ist mir völlig unklar, warum Deine GCC Version in den letzten beiden
Fällen unterschiedlichen Code erzeugt. Meiner Meinung nach, müsste in
beiden Fällen der selbe Code rauskommen, was durch zwei verschiedene
Compiler (einer davon ein neuerer GCC) bestätigt wird.
Gruß
Marcus
Jetzt habe ich den Test nochmal mit Codesourcery wiederholt.
Da kommt auch bei mir für die beiden letzten Fälle der
gleiche Code raus.
Der andere GCC stammt aus dem Yagarto Paket (v4.3.2).
Und da ich jetzt neugierig geworden bin habe ich noch
den WinARM-4.3.0 probiert. Der verhält sich wie der Codesourcery.
Aber Danke für deine Erklärungen. Da habe ich (wieder mal)
Erkenntisse gewonnen von denen ich dachte das ich sie
schon hätte ;)