Hallo ,
ich habe hier ein Problem mit Zeigern.
Ich lese aus einer SD Karte eine Bitmapdatei aus. Die Daten werden in
einem Charakter Puffer gespeichert und sollen dann weiterverarbeitet
werden. Das ganze läuft auf einem Atmal ARM7 AT91SAM7X256. Kompiliert
wird mit Crossworks for ARM.
Nach dem lesen des ersten Blocks möchte ich den Bitmap Header auswerten.
da die Daten als Char eingelesen werden muß eine Umwandlung in die
entsprechenden Datenformate erfolgen. Das zuerst gezeigte Programm
funktioniert nur für die ersten beiden byte im Puffer BMPBuffer. Die
Byte 2-5 enthalten als unsigned long die Größe der Bitmapdatei. Ich
versuche einen void Zeiger auf das dritte Byte zu setzen und auf
unsigned long zu casten. Der Pointer vptr enthält auch die entsprechende
Adresse und zeigt auch den richtigen Wert an. Die Zuweisung an eine
Variable vom Typ unsigned long führt aber ins leere. Der Prozessor ruft
den abort handler auf.
1
// Dieses Programm funktioniert nicht!
2
3
#include"mmc.h"
4
#include"ff.h"
5
#include<stddef.h>
6
#include<stdlib.h>
7
#include"bmp.h"
8
#include<string.h>
9
10
#define buffersize 1024
11
12
voidRead_BMP(void){
13
FRESULTfres;
14
FATFSBMPfs;
15
FIL*BMPFile;
16
unsignedcharBMPBuffer[buffersize];
17
unsignedint*prdByte,rdByte;// Anzahl der gelesenen Byte
vptr=(unsignedlong*)&BMPBuffer[2];// funktioniert nicht
38
ul=*(unsignedlong*)vptr;// Absturz Aufruf des abort-handlers
39
tBMP.bfSIZE=ul;
40
41
}
42
f_close(BMPFile);
43
44
}
Hier die nun funktionierende Variante. Ich kopiere die Pufferdaten ab
dem dritten byte in einen neuen Puffer. Dessen Anfangsadresse kann ich
wieder direkt bestimmen "vptr = (unsigned long +)buffer".
Und siehe da, alles funktioniert wie gewünscht.
Meine frage, kann sich jemand von den C Experten das Verhalten erklären?
hier der funktionierende Code.
1
// Das folgende Programm funktioniert!
2
3
#include"mmc.h"
4
#include"ff.h"
5
#include<stddef.h>
6
#include<stdlib.h>
7
#include"bmp.h"
8
#include<string.h>
9
10
#define buffersize 1024
11
12
voidRead_BMP(void){
13
FRESULTfres;
14
FATFSBMPfs;
15
FIL*BMPFile;
16
unsignedcharBMPBuffer[buffersize];
17
unsignedcharbuffer[54];
18
unsignedint*prdByte,rdByte;// Anzahl der gelesenen Byte
Misalignment. Es ist nicht bei jedem Prozessor straflos möglich, auf
Adressen zuzugreifen, deren Wert nicht zur Grösse des Datentyps passt.
Bei einem 32-Bit Datentyp sollten also die beiden unteren Bits der
Adresse 0 sein. Sind sie hier nicht.
Ich weiss grad nicht wie dieser ARM7 definiert ist. Auf ganz alten ARMs
wäre Schrott geladen worden (die Bytes 0..3 um 16 Bits rotiert), manche
Prozessoren laufen in eine Exception und manche basteln sich den Kram in
mehreren Zugriffen zusammen (x86, Cortex-M3).
Warum die Konstruktion mit malloc? Eine "lokale variable" vom Typ FIL
und ein "&" bei den Fuktionsaufrufen zur FAT-library düfte ausreichen.
free() fehlt ohnehin. Betr. BMP-Header parsen ist mglw. etwas aus einer
meiner Basteileien hilfreich:
http://www.siwawi.arubi.uni-kl.de/avr_projects/arm_projects/index_cortex.html#stm32_dpf
(darin slideshow.c/LCD_DrawBMPFromFile()). Falls man nicht manual
"zerpflücken" will, könnte man noch ausprobieren, BMPHeader mit einem
packed-attribut zu versehen (siehe GNU GCC Dokumentation) - portabel
wird der Code dadurch aber noch weniger.
Danke erstmal für die Antworten.
@A.K.
Misalignment kam mir natürlich nicht in den Sinn. Werde das aber auch
überprüfen. Wenn dem so ist (Wahrscheinlichkeit sehr groß) werde ich
wohl bei meinem Konstrukt bleiben, obwohl es mir nicht gefällt, oder
irre ich mich da?
@Martin Thomas
Die Konstruktion mit malloc ist die einzige die bisher funktioniert.
Habe ich auf drei Prozessoren ausprobiert (H8SX1668, ATmega644 und eben
AT91SAM7X256).
Wenn ich nur die Adressenzuweisung einer FIL Variablen mache
funktioniert nichts. In f_read wird dann dem Puffer BMPBuffer eine
Adresse im ROM zugewiesen. Nicht mal unbedingt die Adresse 0. Auf dem
ATmega644 habe ich das ganze mal genau unter die Lupe genommen und
festgestellt, daß die übergebene Adresse irgendwann einfach mit 0
überschrieben wird. Warum ist mir nicht klar. Die Routinen sind übrigens
die ELM Chan Routinen.
Danke für das free (). Da ich noch nicht soweit bin, ist mir das Fehlen
noch garnicht aufgefallen.
Harald Hermenau schrieb:
> wohl bei meinem Konstrukt bleiben, obwohl es mir nicht gefällt, oder> irre ich mich da?
Im Prinzip ok, wenn man, wie Martin anführt, das malloc durch eine
simple Variable ersetzt. Notfalls als union.
Die "packed" Variante geht auch. D.h. eine packed struct exakt so bauen,
dass die richtigen Typen an der richtigen Stelle liegen
(kontrollieren!). Dann kann man entweder direkt mit der arbeiten, wobei
der Zugriff dabei etwas weniger effizient ist, oder man hat zwei
structs, eine gepackte und eine normale, und führt einmalig eine
Zuweisung der Elemente durch.
> Wenn ich nur die Adressenzuweisung einer FIL Variablen mache> funktioniert nichts.
Beispielcode ist besser als Prosa.
Eine weitere Variante liest nicht en bloc, sondern passend zum Typ. Also
bmp.var0 = f_read_u16(file);
bmp.var1 = f_read_u32(file);
...
mit get_u16(file) beispielsweise als
uint_least16_t r = f_read_u8(file);
return r | f_read_u8(file) << 8;
wenn das BMP als little-endian definiert ist. Das funktioniert dann
plattformunabhängig, d.h. auch wenn auf big-endian Hardware.
Lies das Byte für Byte und bau das mit Shifts zusammen (siehe A.K.),
das ist portabel und funktioniert immer.
Alles andere ist Murks und führt nur zu Problemen mit Endianness oder
Alignment (wie hier geschehen).
@A.K.
Danke.
Das mit dem Misalignment wars erstmal. Habe es gerade ausprobiert. Auf
durch 4 teilbaren Adressen funktionierts.
Das mit einer gepackten Struktur habe ich auch schon versucht. Ob
richtig, sei mal dahingestellt. Es gab jedenfalls kein positives
Ergebnis. Kann natürlich auch an mir gelegen haben, es nicht richtig
gemacht zu haben. Vielleicht kann ich es nochmal rekonstruieren.
Die andere Geschicht mit der Adresszuweisung muß ich aucht erstmal
wieder rekonstruieren, da ich den Code schon umgeschrieben habe und den
nicht funktionierenden nicht weiter dokumentiert habe.
Die Variante mit dem "nicht en bloc" lesen werde ich auch mal ins Auge
fassen, da ich mit dem H8SX1668 einen big-endian Prozessor vor mir habe.
Danke für die guten Tips.