Hallo,
ich habe eine Frage zu einer Funktion zur Endianess Umwandlung:
1
unsignedshortendians(unsignedshortvar){
2
unsignedchar*ptr=(unsignedchar*)&var;
3
unsignedshortb0=(unsignedshort)*(ptr++);
4
unsignedshortb1=(unsignedshort)*(ptr++);
5
returnb1|(b0<<8);
6
}
Diese funktion soll die beiden Bytes tauschen, es kommt aber nicht das
richtige Ergebnis raus. Kompiliert wurde mit GCC als cross-compiler für
arm926ej-s.
Ich habe es dann anders gelöst:
1
unsignedshortendians(unsignedshortvar){
2
unsignedshortb0=var&0xff00;
3
unsignedshortb1=var&0x00ff;
4
return((b0>>8)|(b1<<8));
5
}
Aber ich muss trotzdem wissen warum die erste Variante nicht
funktionierte, da ich weitere Probleme mit meinem Programm habe. Ich
hoffe jemand hat einen Tipp für mich.
Viele Grüße
Daniel
Falls ein short aus zwei Bytes besteht, kann das entweder so
| HI | LO |
oder so
| LO | HI |
im Speicher abgelegt sein.
Das Konstrukt
unsigned char *ptr = (unsigned char*) &var;
zeigt dann auf das Byte mit der niedrigeren Adresse.
Je nach Endianess ist das dann entweder das HI oder das LO Byte. Wenn es
das HI Byte ist, dann stellt dein Code genau das Original wieder her,
vertauscht also nix.
Also der uC arbeitet in little Endian, also:
1. var = | LO | HI |
2. ptr zeigt auf LO
3. LO wird in b0 gespeichert
4. HI wird in b1 gespeichert
5. b0 erfährt linksshift um 8
6. b0 enthält LO im höherwertigen byte, b1 enthält HI im niederwertige
byte
nach addition im Speicher also: | b1 | b0 |
und damit: | HI | LO |
das ist doch richtig, oder nicht?!
Ich habe die Funktion so getestet:
unsigned int x = 0xabcd;
if (endians(x) == x)
return error;
if (endians(endians(X)) != x)
return error;
es wird weder die gleiche zahl noch die korrekt umgestellte
zurückgegeben. ich versteh das nicht... hab ich C etwa nicht verstanden
oder macht der compiler merkwürdige sachen?
Testen müsste sich das so lassen:
unsigned short endians (unsigned short var) {
unsigned short tmp = var;
unsigned char *ptr = (unsigned char*) &tmp;
unsigned short b0 = (unsigned short) *(ptr++);
...
nein, keine warnings trotz -Wall. So weit ich weiss wird var im register
übergeben so wie generell bis zu 4 variablen. ich habe auch versucht var
vorher in einer lokalen variablen zu speichern aber kein erfolg.
außerdem müsste der compiler, wenn es mit dem register nicht geht, doch
von selbst auf eine lösung kommen, oder?
meinst du meine testroutine ist nicht korrekt? mit der neuen variante
wird kein fehler gemeldet.
Ich habe dieses und andere Probleme hinsichtlich fehlerhaftem Verhalten
des Programms gelöst: Mein Fehler war es, Pointer-Casts zu benutzen und
diese gecasteten Pointer dann auch noch zu dereferenzieren.
Laut ISO-C dürfen Pointer auf nicht-kompatible Datentypen nicht auf die
gleiche Adresse zeigen (aliasing).
1
inti=5;
2
charwert=*((char*)&i);
...geht also nicht. Zumindest nicht bei den heutigen gcc versionen, bei
denen aliasing durch optimierungsroutinen nicht möglich ist.
Die Lösung ist, wenn man beispielsweise ein struct aus verschiedenen
Datentypen auch durch ein char-array lesen oder beschreiben will, ein
union:
1
union{
2
struct{
3
inti;
4
inta;
5
shortc;
6
}__attribute__((packed))fields;
7
charbytes[10];
8
}__attribute__((packed))header;
Was außerdem Probleme machen kann: in gcc müssen alle Datentypen größer
1 Byte aligned sein. Liegt ein Integer also irgendwo unaligned im
speicher wird es nicht lesbar sein. dies kommt z.B. bei Netzwerkpaketen
vor, die durch den den MAC im Speicher abgelegt werden. Da muss man eben
char nutzen und den header in ein struct in einer union kopieren.
Quelle: http://mail-index.netbsd.org/tech-kern/2003/08/11/0001.html
und: http://gcc.gnu.org/bugs.html#known
Daniel G. wrote:
> Liegt ein Integer also irgendwo unaligned im speicher wird es nicht> lesbar sein. dies kommt z.B. bei Netzwerkpaketen vor, die durch den> den MAC im Speicher abgelegt werden. Da muss man eben char nutzen> und den header in ein struct in einer union kopieren.
Das ist ein immer wieder gern hervorgeholtes Gerücht. Das
1
__attribute__((packed))
hat genau den Zweck, dem Compiler zu sagen, dass die Daten
möglicherweise nicht an Wort/Halbwort-Grenzen liegen und er daher
geeigneten Code erzeugen muss, um die Daten stückweise
zusammenzuklauben. Also im Prinzip genau das, was Du mit dem
Byte-Array zu Fuß machst.
Wir haben in diesem Forum bereits festgestellt, dass einige GCC
Versionen dabei Fehler machen. Erfahrungsgemäß ist man mit dem GCC von
CodeSourcery ganz gut bedient.
Gruß
Marcus
http://www.doulos.com/arm/
Manch ein ARM schreibt gern mal auf die alignte Adresse, auch wenn du
ihm eine nicht alignte gibst. Und das ohne jegliche Fehlermeldung ...
D.h. z.B. nach abc0 statt nach abc1.
Juergen wrote:
> Manch ein ARM schreibt gern mal auf die alignte Adresse, auch wenn du> ihm eine nicht alignte gibst. Und das ohne jegliche Fehlermeldung ...>> D.h. z.B. nach abc0 statt nach abc1.
Das Verhalten ist im Architecture Reference Manual für Prozessoren ohne
System Control Coprocessor genau so spezifiziert.
Daher muss der Compiler, der prinzipiell die Aufgabe hat, korrekten Code
für die Zielplattform zu erzeugen, derartige Speicherzugriffe vermeiden
und ggf durch Zugriff auf einzelne Bytes nachbilden.
Gruß
Marcus
http://www.doulos.com/arm/
Hallo Marcus,
wenn ein Datentyp kleiner einem Wort gelesen werden soll, so
beispielsweise in meinem struct für Netzwerk-Header die durch
das((packed)) eben nicht alle aligned im speicher liegen, wird der
kompiler natürlich darauf zugreifen können.
Ich meinte aber folgendes: Wenn Pakete empfangen werden und in den
Speicher gelegt werden und daraus Datentypen größer einem Byte gelesen
werden sollen (die je nicht alle aligned sind), kann kein direkter
zugriff erfolgen. So schreibt es jedenfalls die Autorin des verlinkten
Textes oder zumindest habe ich es so verstanden.
Wobei genau machen manchen versionen von gcc Fehler? bei dem beispiel
was sich mit der union gegeben habe oder wobei sonst?
Gruß
Daniel
Achso, _attribute_ ((packed)) ist übrigens dazu da um die Datentypen
ohne zwischenraum im Speicher anzuordnen. Der Speicherraum käme
normalerweise zustande, weil der Compiler die Variablen so anordnet
damit er schnell drauf zugreifen kann, halt aligned.
Durch das Packed ergibt sich zwangsweise, dass der compiler den Zugriff
auf kleinere Datentypen als 32Bit (bei ARM9) emulieren muss.
Daniel G. wrote:
> Ich meinte aber folgendes: Wenn Pakete empfangen werden und in den> Speicher gelegt werden und daraus Datentypen größer einem Byte gelesen> werden sollen (die je nicht alle aligned sind), kann kein direkter> zugriff erfolgen.
Doch, wenn Du dem Compiler sagst, dass genau das der Fall sein
kann. Der Compiler wird dadurch angehalten, anstelle eines
Wortzugriffs (LDR) eine Reihe von Einzelzugriffen (LDRH, LDRB)
durchzuführen. Nicht sonderlich effizient, funktioniert aber.
> Wobei genau machen manchen versionen von gcc Fehler? bei dem beispiel> was sich mit der union gegeben habe oder wobei sonst?
Der betroffene GCC greift mit einem Wortzugriff auf ein Wort zu, das
nicht zwingend an einer durch vier teilbaren Adresse liegt -- obwohl
wir den Compiler mit dem Attribut "packed" darauf hingewiesen haben.
Das hier ist ein Beitrag in einem recht langen Thread
Beitrag "Re: Kann ich garantieren, dass ein C Struct genau 3 Byte im Speicher belegt?"
Gruß
Marcus
http://www.doulos.com/arm/