Hallo, ich hab mal wieder ein Problem. Wie einige hier schon mitbekommen
haben hab ich mit C erst angefangen, deswegen fehlt mir hier und da noch
der Durchblick. Ich möchte Daten, die ich von einem Browser bekomme über
einen Webserver einlesen. Der TCP/IP stack ist der aus EasyWeb von
Andreas Dannenberg, falls das jemandem was sagt. Er ist bei den
Beispielen der Keil uvision dabei. Es funktioniert auch alles soweit
ganz gut, ich kann Daten senden und empfangen. Da der Puffer zum
Einlesen der Daten vom Ethernetcontroller begrenzt ist, erhalte ich bei
längeren Daten fragmentierte Pakete. Der Webserver liest diese zyklisch
aus. Ich will nun eigentlich nur die Daten, die die TCP/IP Schicht zur
Verfügung stellt, aneinander hängen, da ich sie später parsen oder
anderweitig verwenden will. Genau hier liegt auch mein Problem, wie
gesagt auslesen klappt, aneinander hängen allerdings nicht.
Mein Code sieht so aus:
1
if(HTTPRxBuffer!=NULL&&sizeof(HTTPRxBuffer>0)){//does HTTPRxBuffer exist? Is there some data in it? -->first cycle is through
2
sendString("cycle größer 1");
3
buf0=(unsignedchar*)malloc(sizeof(HTTPRxBuffer));/*get memory to save old buffer*/
4
if(buf0!=NULL){
5
memcpy(buf0,HTTPRxBuffer,sizeof(HTTPRxBuffer));//save content of old HTTPRxBuffer
6
sendString("http nach buf0 schieben");
7
sendString("Größe von buf0");
8
sprintf(num1,"%d",sizeof(buf0));
9
sendString(num1);
10
}
11
free(HTTPRxBuffer);//discard old HTTPRxBuffer
12
HTTPRxBuffer=NULL;
13
HTTPRxBuffer=(unsignedchar*)malloc(sizeof(buf0)+TCPRxDataCount);//get memory for new HTTPRxBuffer
14
if(HTTPRxBuffer!=NULL&&buf0!=NULL){
15
sendString("http buffer neu zusammensetzen");
16
sprintf(num,"%d",sizeof(buf0));
17
sendString(num);
18
memcpy(HTTPRxBuffer,buf0,sizeof(buf0));//copy old content to new HTTPRxBuffer
19
memcpy(HTTPRxBuffer+sizeof(buf0),RxTCPBuffer,TCPRxDataCount);//save new incomig traffic to HTTPRxBuffer
20
free(buf0);//discard buf0
21
buf0=NULL;
22
if(TCPRxDataCount<MAX_TCP_RX_DATA_SIZE)
23
HTTPReadComplete=true;
24
}
25
if(HTTPRxBuffer==NULL)
26
sendString("httprxbuffer ist NULL!");
27
HTTPRxBufferSize=sizeof(HTTPRxBuffer);
28
sendString("httprxbuffersize");
29
sprintf(num,"%d",HTTPRxBufferSize);
30
}
31
else{
32
sendString("1. cycle");
33
HTTPRxBuffer=(unsignedchar*)malloc(TCPRxDataCount);//first cycle, fetch memory for HTTPRxBuffer
34
if(HTTPRxBuffer!=NULL)
35
memcpy(HTTPRxBuffer,RxTCPBuffer,TCPRxDataCount);//put available data in the buffer
36
if(TCPRxDataCount<MAX_TCP_RX_DATA_SIZE)
37
HTTPReadComplete=true;
38
HTTPRxBufferSize=sizeof(HTTPRxBuffer);
39
}
Meine Frage wäre nun, ob ich malloc falsch benutze? Kann man kurz
hintereinander den Speicher von einem Pointer nehmen und dann gleich
neuen zuweisen?
Zur Erklärung: HTTPRxBuffer-Puffer den ich füllen möchte, buf0-zur
Zwischenspeicherung, TCPRxDataCount-Daten, die eingelesen werden
könen(TCP/IP), sendString() sendet Daten an die serielle Schnittstelle,
mit der ich die Daten überwache. HTTPRxBuffer lasse ich mir später im
Programm damit ausgeben, aber es steht immer nur das drin, was im
aktuellen RxTCPBuffer steht, den ich jeweils auslese.
So, nun hoffe ich, das sich das trotz der Länge jemand durchliest, danke
schon mal dafür.
Bis dann
> Meine Frage wäre nun, ob ich malloc falsch benutze? Kann man kurz> hintereinander den Speicher von einem Pointer nehmen und dann gleich> neuen zuweisen?
Ja, kann man.
Aber dein Code enthält viele Fehler und durch die Art und Weise
der Formatierung ist er nicht besonders leicht zu lesen. Mach
doch ein paar Leerzeichen rein um die Dinge optisch etwas
zu gruppieren. Sowas
1
if(HTTPRxBuffer!=NULL&&buf0!=NULL){
muss doch nicht wirklich sein. Da muss ich erst mal eine Viertelstunde
parsen bis ich raus habe, dass du haben willst
1
if(HTTPRxBuffer!=NULL&&
2
buf0!=NULL){
Zu den Fehlern.
Das hier
1
if(HTTPRxBuffer!=NULL&&sizeof(HTTPRxBuffer>0)){
macht mit Sicherheit nicht das was du denkst. Mach mal ein paar
Leerzeichen rein und sieh nach wie denn der > wirkt und in welchem
Zusammenhang er zum sizeof steht :-) (Mit etwas mehr Leerzeichen
wäre das wahrscheinlich nicht passiert)
Du erwartest zu viel von sizeof. sizeof kannst du
nur bei Dingen verwenden die zur Compilezeit schon ihre Größe haben.
zb
1
memcpy(HTTPRxBuffer,buf0,sizeof(buf0));//copy old content to new HTTPRxBuffer
das kopiert nicht den ganzen Bufferinhalt um, sondern nur
sizeof(buf[0]) Bytes. buf[0] ist vom Datentyp her ein unsigned
char, hat also die Länge 1. Daher wird auch nur 1 byte umkopiert.
Selbiges hier
1
HTTPRxBuffer=(unsignedchar*)malloc(sizeof(buf0)+TCPRxDataCount);//get memory for new HTTPRxBuffer
buf0 ist ein Pointer. Abhängig davon wieviele Bytes ein Pointer
auf deinem System belegt ist das eine Zahl zwischen 2 und 4 aber
sicher nicht die Buffergröße die du hier
1
buf0=(unsignedchar*)malloc(sizeof(HTTPRxBuffer));/*get memory to save old buffer*/
allokiert hast.
Wenn ich dir einen Rat geben darf: Fang mal damit an dir eine
Append Funktion zu schreiben, die die Aufgabe hat an einen
bestehenden Buffer neue Daten anzuhängen und bei Bedarf auch
neuen Speicher allokiert. Die Funktion sollte so verwendet
werden:
Buffer = Append( Buffer, &BufferLen, Data, DataLen );
mit der Bedeutung: An Buffer (momentane Größe der Daten im Buffer
steht in BufferLen) die neuen Daten aus Data (deren Größe ist in
DataLen angegeben) anhängen. Der Returnwert der Funktion ist ein
Zeiger auf einen Speicherbereich in dem diese Aneinanderfügung
stattgefunden hat und BufferLen wurde in der Größe korrigiert
(daher auch die Übergabe als Zeiger). Append gibt auch einen
eventuell von Buffer bereits allokierten Speicher frei (Buffer
könnte ja auch NULL sein), lässt allerdings die Speicherallokierung
von Data in Ruhe.
Die Funktion hat also die Signatur
Hallo Karl Heinz, wow, so schnell hätte ich nich mit einer Antwort
gerechnet, aber umso besser :-). Ich werd mich morgen früh mal ran
setzen deine Ratschläge zu befolgen. Das mit der Formatierung tut mir
leid, ich bin mir da selbst noch nich ganz so einig, aber wie das
Beispiel mal wieder zeigt muss ich da noch nachsteuern.
Ich dachte, das die Größe bei malloc() die Größe der Daten anlegt, die
man speichern will, also z.b. 256B angelegt und nen Zeiger auf die erste
Adresse zurückbekommen. So war mein Plan, naja, werd mir mal die
Appendfunktion vornehmen, das is ne gute Idee.
So, nu aber gute Nacht
Karl heinz Buchegger wrote:
> das kopiert nicht den ganzen Bufferinhalt um, sondern nur> sizeof(buf[0]) Bytes. buf[0] ist vom Datentyp her ein unsigned char,> hat also die Länge 1. Daher wird auch nur 1 byte umkopiert.
Naja, da stand nicht buf[0] sondern buf0. Ändert aber nur minimal
was: buf0 ist (wie du einen Absatz tiefer festgestellt hast) ein
Pointer, damit ist sizeof(buf0) gleich 2, es werden also zwei Bytes
kopiert.
@Jörg
Da hatte ich wohl Tomaten auf den Augen :-) (War schon spät)
@Icke
Wie ... schon sagte, den malloc hast du schon richtig verstanden.
Was du aber noch intus kriegen musst: Wieviele Bytes du in einem
malloc allokoiert hast, musst du dir selbstin einer Variablen
merken. Es gibt keine C-Funktion mit der du rauskriegen könntest
wieviele Bytes sich hinter einem Pointer den du von malloc() bekommen
hast verbergen.
Die oben vorgeschlagene Funktion soll dir helfen diese Abstraktion
zu machen und zu orgaisieren. Denn der Funktion selbst ist es
völlig egal, ob sich in diesem Speicherbereich nun TCP/IP Daten
oder Telefonbücher tummeln. Alles was sie will ist: Pointer
auf die Daten + Länge der Daten. Diese beiden Informationen gehören
zusammen und sind untrennbar miteinander verknüpft (weswegen es
sich auch anbieten würde dafür einen eigenen Datentyp, eine struct,
zu erfinden).
Also ich hab das jetzt mal ausprobiert, das append() hab ich implizit in
der Funktion gemacht, wo ich auch vorher schon rumkopiert hab.
Angehangen wird jetzt, ich hatte die Fkt. sizeof() einfach völlig falsch
verstanden, ich merk mir jetzt wie du gesagt hast die Größe des Puffers
beim letzten Durchlauf. Das klappt soweit, er bleibt bis jetzt aber noch
beim dritten Durchlauf hängen, aber das liegt vermutich an irgendnem
Fehler, den ich selbst heraus finde, wenn nicht werd ich euch noch mal
fragen :-). Ansonsten danke für die schnelle Hilfe!!!
So Jungs, das war echt ne schwere Geburt, aber jetzt klapts so wie ich
mir das vorgestellt hab. Nach langem hin und her und ewiger Googelei kam
die Erleuchtung. malloc() arbeitet auf dem Heap, gut zu wissen, der war
bei mir nämlich gleich mal 0 bytes groß :( sehr dumme Sache. Google
verwies mich dabei auch noch genau auf diese Seite... Damit war klar,
dass er sich irgendwann mal aufhängt, da es vermutlich zum Overflow kam,
oder sehe ich das falsch? Was ich nicht ganz verstehe ist, dass ich so
um die 500Bytes in einem Rutsch locker lesen konnte, mit Rumkopiererei
allerdings nicht. Hängt das mit der Fragmentierung zusammen? Aber warum
hat malloc() dann nicht NULL zurückgegeben, oder war das nur Glück?
Woher hat malloc() den Speicher genommen, als der Heap noch 0B war? Oder
gibts da vielleicht noch irgendwo nen default Wert? (LPC2378)
Wie auch immer hab ihn jetzt auf 256B eingestellt, damit funktionierts
erst mal ganz gut. Wenns dazu kritische Betrachtungen gibt immer her
damit. Wenn ich den Speicher regelmäßig, nach meiner Bearbeitung lösche
sollte es doch keine Fragmentierungsprobleme in Zukunft geben, oder?
So hier noch der Code, vielleicht bringts ja jemand anderem mal was,
oder euch fällt noch was auf. Hab auch versucht Augenkrebs zu vermeiden.
1
if(TCPRxDataCount>0){//data to work with?
2
3
if(HTTPRxBuffer!=NULL){//does HTTPRxBuffer exist? -->first cycle is through
4
5
buf0=(unsignedchar*)malloc(oldBufferSize);//get memory to save old buffer
6
7
if(buf0!=NULL){
8
memcpy(buf0,HTTPRxBuffer,oldBufferSize);//save content of old HTTPRxBuffer
9
}
10
11
free(HTTPRxBuffer);//discard old HTTPRxBuffer
12
HTTPRxBuffer=NULL;
13
HTTPRxBuffer=(unsignedchar*)malloc(oldBufferSize+TCPRxDataCount);//get memory for new HTTPRxBuffer
14
15
if(HTTPRxBuffer!=NULL&&
16
buf0!=NULL){
17
18
memcpy(HTTPRxBuffer,buf0,oldBufferSize);//copy old content to new HTTPRxBuffer
19
memcpy((HTTPRxBuffer+oldBufferSize),RxTCPBuffer,TCPRxDataCount);//save new incomig traffic to HTTPRxBuffer
20
free(buf0);//discard buf0
21
buf0=NULL;
22
23
}
24
if(HTTPRxBuffer==NULL)//debugging
25
sendString("httprxbuffer ist NULL!");
26
27
oldBufferSize+=TCPRxDataCount;//size of the new HTTPRxBuffer
28
29
if(TCPRxDataCount<MAX_TCP_RX_DATA_SIZE){
30
HTTPReadComplete=true;
31
sendString("komplett ausgelesen");
32
HTTPRxBufferSize=oldBufferSize;
33
}
34
}
35
else{
36
HTTPRxBuffer=(unsignedchar*)malloc(TCPRxDataCount);//first cycle, fetch memory for HTTPRxBuffer
37
38
if(HTTPRxBuffer!=NULL){
39
memcpy(HTTPRxBuffer,RxTCPBuffer,TCPRxDataCount);//put available data in the buffer
40
sendString("httprxbuffer ist nicht null");
41
}
42
43
oldBufferSize=TCPRxDataCount;
44
cycle++;
45
if(TCPRxDataCount<MAX_TCP_RX_DATA_SIZE){
46
HTTPReadComplete=true;
47
HTTPRxBufferSize=oldBufferSize;//tell the user how many bytes are available
48
sendString("Komplett gelesen");
49
}
50
}
51
}
52
else{
53
if(cycle>0){
54
HTTPReadComplete=1;//scenario, if last bufferread read exactly all left bytes out of the RxTCPBuffer
55
HTTPRxBufferSize=oldBufferSize;
56
}
57
}
58
59
if(HTTPRxBuffer!=NULL){
60
sendString("Daten auslesen");
61
sendString((char*)HTTPRxBuffer);
62
63
if(HTTPReadComplete)
64
clearHTTPRxBuffer();
65
}
66
67
68
}
69
voidclearHTTPRxBuffer(){//has to be called to get new HTTP data
70
71
free(HTTPRxBuffer);
72
HTTPRxBuffer=NULL;
73
HTTPReadComplete=false;
74
HTTPRxBufferSize=0;
75
oldBufferSize=0;
76
cycle=0;
77
}
Wie gesagt, wenns was zu nörgeln gibt, meine Ohren sind offen, ansonsten
sag ich mal bis zum nächsten Problem ;-) und nochmals danke!!!
Ich hatte mal irgendwo gelesen, dass es den Code lesbarer und besser
nachvollziehbar macht, die Meinung teile ich, man weiß halt immer was
drin ist und der Compiler meckert und man braucht nicht ewig suchen wo
der Fehler ist, wenn was verhauen ist. Da ich wie gesagt am Anfang bin
behalte ich das erst mal bei, bis mein Überblick besser ist.
Icke Muster wrote:
> Ich hatte mal irgendwo gelesen, dass es den Code lesbarer und besser> nachvollziehbar macht,
was ist daran lesbarer bzw. besser nachvollziehbar?
Wenndu duokumentieren willst, dass hier unsigned char allokiert
werden, dann schreib das so:
1
buf0=malloc(oldBufferSize*sizeof(unsignedchar));
aber lass den cast weg.
> die Meinung teile ich, man weiß halt immer was> drin ist
das weist du auch so. An dieser Stelle interessiert das
(zb. bei der Allokierung einer struct) aber auch niemanden.
Du willst an dieser Stelle genug Speicher für zb. eine struct Test
allokieren. Also schreibt man
1
structTest*pPtr;
2
3
pPtr=malloc(sizeof(structTest));
und hat damit dokumentiert, dass man Speicher für eine struct
allokiern will. Der cast bringt an dieser Stelle gar nichts,
weder an Sicherheit noch an dokumentarischem Wert.
Aber eigentlich ist mir an dieser Stelle auch relativ egal, dass
pPtr ausgerechnet auf eine struct Test zeigen soll. Das ist ein
Detail das ich an dieser Stelle gar nicht haben will. Eigentlich
will ich ja ausdrücken: Allokiere genug Speicher, so dass pPtr
entsprechend seinem Datentyp auf genau ein derartiges Objekt
zeigt, egal was das jetzt sein mag. In C schreibt sich das dann
1
structTest*pPtr;
2
3
pPtr=malloc(sizeof(*pPtr));
das hat dann auch den Vorteil, dass sich diese Anweisung ganz
von alleine an veränderte Umstände anpasst, zb. wenn pPtr plötzlich
nicht mehr auf eine struct Test sondern auf eine struct FileHeader
zeigen soll:
1
structFileHeader*pPtr;
2
3
pPtr=malloc(sizeof(*pPtr));
Siehst du? Keine Änderung beim malloc notwendig! Damit habe ich
hier eine mögliche Fehlerquelle ausgeschlossen. Und jeder Fehler
der per Design vermieden werden kann ist ein guter Fehler.
> und der Compiler meckert und man braucht nicht ewig suchen wo> der Fehler ist, wenn was verhauen ist.
Ganz im Gegenteil.
Mit einem cast stellst du das Typ Prüfsystem des Compilers
ab und übernimmst selbst die 100% Verantwortung dafür, dass
alles richtig ist.
Das Problem:
Wenn du den Header nicht inkludierst, in dem malloc() definiert
ist, dann gelten Standardannahmen für den Funktionsaufruf. Zb.
die Annahme, dass die Funktion einen int zurückliefert (was sie
nicht tut. Sie liefert einen void*).
Wenn jetzt auf deinem System (zugegeben, auf einem AVR ist das nicht
so) ein int eine andere Größe hat als ein void *, dann hast du dir
mit dem vergessenen Header File einen Fehler eingebaut, der mit
ziemlicher Sicherheit in Folge zu einem kräftigen Programmabsturz
führen wird und der extrem schwer zu finden ist.
Dumm ist nur, dass der Cast den Compiler ruhigstellt -> Keine
Fehlermeldung
Lässt du aber den Cast weg, dann teilt dir der Compiler in diesem
Fall brav mit, dass er einen int nicht an einen Pointer zuweisen
kann und du merkst zumindest beim Compilieren, dass da was nicht
stimmt.
Cast sind Waffen! Sie sollten nur eingesetzt werden, wenn es
nicht anders geht. Hier in diesem speziellen Fall ist der Cast
völlig unnötig und in speziellen Fällen sogar kontraproduktiv,
weil er einen Fehler verstecken kann, der böse ins Auge gehen
kann.
Vor allem erschweren Casts die Wiederverwendung von Code ungemein - man
muß ihn Zeile für Zeile nachvollziehen und überprüfen, ob die neue
Maschine, auf der er laufen soll, dafür paßt.
Bei einigermaßen komplizierten Algorithmen kommt das u.U. einer
Neuimplementierung gleich.
Zudem sind Fehler, die durch falsches casting entstehen, ziemlich gemein
und oft sehr schwer zu finden.