Hallo, ich wollte ienen fifo also eine Queue in c schreiben, damit ich
eintreffende Events in der eingetroffenen reihenfolge bearbeiten kann,
wenn ich dazu zeit habe. hierbei bekomme ich die queue einfach nicht
hin. irgendwas stimmt da nicht mit den Pointern. meine getEvent und
meine initialisierung funktionieren nicht richtig. der compiler meldet
folgende fehler:
> keine Variable vereinbart wird.
hm warum nicht? steht doch mainEventQueue dahinter. Iwe macht man das
sonst?
Naja schrieb:
> Welcher Typname wird hiermit>>
definierst du:
1. ein Token: struct eventQueue und
2. einen Typen: mainEventQueue.
Du kannst damit dann Variablen deklarieren, etwa so:
1. struct eventQueue var1, var2, bla, blubb;
2. mainEventQueue varA, varB, x, y, z;
warum? ich verknüpfe es doch in meine liste??
das free gibt den speicherplatz wieder frei. alloziert wird der, wenn er
der Funktion übergeben wird. aber du hast recht ich übergebe keinen
pointer, nur eine kopie, deshalb geht das verlohren.
hier die neue Version:
1
/**
2
*
3
* Project: Automatic WIFI Network
4
* Package: EventQueue
5
*
6
*
7
* File: Test.c
8
* Author: Soenke Paschko
9
* Maintainer: Soenke Paschko
10
*
11
* Created on: 08. August 2009, 11:35
12
*
13
* Content:
14
*
15
*
16
* Revision history:
17
* 08.08.09 1. revision Paschko - First version
18
*/
19
20
#ifndef EVENTQUEUE_H_
21
#define EVENTQUEUE_H_
22
23
/**
24
* Event structs
25
*/
26
typedefstruct{
27
chareventClass;
28
chareventSource;
29
}event;
30
31
typedefstructqueue{
32
eventdata;
33
structqueue*next;
34
}eventQueue;
35
36
37
voidinitEventQueue();
38
voidaddEvent(event*evt);
39
eventgetEvent();
40
41
#endif /* EVENTQUEUE_H_ */
und der code:
1
/**
2
*
3
* Project: Automatic WIFI Network
4
* Package: EventQueue
5
*
6
*
7
* File: Test.c
8
* Author: Soenke Paschko
9
* Maintainer: Soenke Paschko
10
*
11
* Created on: 08. August 2009, 11:35
12
*
13
* Content:
14
*
15
*
16
* Revision history:
17
* 08.08.09 1. revision Paschko - First version
18
*/
19
20
21
#include"../main.h"
22
#include"events.h"
23
#include"eventQueue.h"
24
25
26
27
28
intlength;
29
30
eventQueue*endOfQueue;// Pointer to the end of the queue
31
eventQueue*mainEventQueue;// Pointer to the first Element of the queue
32
33
34
/**
35
* This function initializes the event queue
36
*/
37
voidinitEventQueue(){
38
length=0;
39
endOfQueue=&mainEventQueue;
40
}
41
42
/**
43
* This function adds an event to the event queue
44
*/
45
voidaddEvent(event*evt){
46
length=length+1;
47
eventQueuenewElement={evt,NULL};
48
endOfQueue->next=&newElement;
49
}
50
51
/**
52
* This function gets the event from the event queue
Das Problem mit der ungültigen Variable besteht immer noch. In 'void
addEvent' wird eine lokale Varible 'newElement' angelegt. Sobald die
Funktion verlassen wird, existiert 'newElement' nicht mehr und der
gespeicherte Pointer zeigt auf einen Speicherbereich mit zufälligem
Inhalt. Damit die Variable auch dann gültig ist, wenn die Funktion
verlassen wurde, muss sie auf dem Heap abgelegt werden. Und das macht
macht man eben mit malloc. Siehe
http://home.fhtw-berlin.de/~junghans/cref/FUNCTIONS/malloc.html
Und noch was: 'endOfQueue = &mainEventQueue;' in 'void initEventQueue'
ist sowohl semantisch als auch syntaktisch totaler Unsinn. 'endOfQueue'
ist ebenso wie 'mainEventQueue' vom Typ 'eventQueue*' und deshalb kann
man ihr auch keine Adresse von mainEventQueue zuweisen. Was Du machen
willst, ist beide Zeiger auf NULL zu setzen, damit sie nicht auf einen
zufälligen Bereich zeigen.
MfG Mark
Nachtrag: 'addEvent' hat noch einen kleinen Fehler, nämlich dass
'endOfQueue' am Ende der Funktion nicht auf die Adresse des neuen
Elements gesetzt wird. Zudem darf 'endOfQueue->next = &newElement;' so
nicht stehen bleiben, sondern es muss zuerst geprüft werden, ob
überhaupt ein letztes Element exisitiert (mit if(endOfQueue)). Erst dann
kann man endOfQueue dereferenzieren.
Sönke Paschko schrieb:
> also muss ich das so machen?>>
1
>/**
2
> * This function adds an event to the event queue
3
> */
4
>voidaddEvent(event*evt){
5
>length=length+1;
6
>eventQueue*newElement=(*char)malloc(3*8);
7
>if(newElement!=NULL){
8
>newElement={evt,NULL};
9
>endOfQueue->next=&newElement;
10
>}
11
>}
12
>
>> nur da werden mir syntaxfehler gemeldet.
Welche?
Syntaxfehler haben einen Text. Manchmal ist der Text wenig hilfreich
aber meistens führt er einen auf die Spur.
* benutze KEINEN cast in C um das Ergebnis von malloc zurechtzucasten!
Wenn der C-Compiler einen Datentypfehler ankreidet, dann ist hier ein
cast ausnahmsweise nicht das Mittel der Wahl.
* (*char) ist Kein Cast, sondern ein banaler Syntaxfehler. Es müsste
(char*) heissen. Aber der Cast ist wie gesagt in C nicht notwendig
und auch nicht gut.
* malloc(3*8)
Benutze kein Wissen darüber, wie gross Datentypen sind, wenn du es
nicht unbedingt musst. Lass den Compiler sich um die Details der
Datentypgrößen kümmern! Auf anderen Maschinen als auf einem AVR
wirst du nämlich dein blaues Wunder erleben, wenn du eine derartige
struct so allokierst. Da kann es dann durchaus sein, dass ein
Strukturelement 4 Bytes gross ist, obowhl du rechnerisch nur auf
3 kommst -> der Compiler hat padding Bytes eingefügt.
Lass den Compiler rechnen, der kann das besser und kennt die Details.
* newElement = {evt, NULL};
in welcher Sprache du hier auch immer programmierst, C ist es auf
keinen Fall.
> was ist da falsch?
Grob gesagt: Falsch ist, dass du kein C-Buch benutzt um die Sprache zu
lernen (und da spreche ich noch gar nicht über das halbe Dutzend
logischer Fehler, die du in den 5 Zeilen Code eingebaut hast)
Sönke Paschko schrieb:
> also laut einem onlinebuch kann man ein struct so initialisieren>> struckttyp hallo = {1.eintrag,2.eintrag};>
Eine Initialisierung ist keine Zuweisung (auch wenn sie so aussieht)
Initialisierung: Ein neues Objekt kommt zur Welt und bekommt Werte.
Aber die Zuweisung von Werten an ein bereits bestehendes Objekt ist
keine Initialisierung!
Für Initialisierungen kann man in C Syntaxelemente benutzen, die für
Zuweisungen nicht zur Verfügung stehen.
WIe ich schon sagte: Du brauchst ein Buch. Und zwar ein vernünftiges
Buch (zb den K&R) und nicht irgendein windiges Online-Tutorial, in dem
die Hälfte nicht drinnen steht.
>> gehts nämlich auch nicht.
Logo geht das nicht (logisch gesehen). mainEventQueue ist ein Pointer!
Worauf zeigt denn dieser Pointer ganz am Anfang?
Antwort: Irgendwo ins Nichts!
1
/**
2
* This function initializes the event queue
3
*/
4
voidinitEventQueue(){
5
length=0;
6
mainEventQueue=NULL;
7
endOfQueue=NULL;
8
}
Zusammen mit der add Funktion, die ich dir oben geschrieben habe,
funktioniert das dann perfekt. Bei ersten add wird das erste eventQueue
Objekt erzeugt und mainEventQueue und endOfQueue darauf gesetzt.
Poste mal den Quelltext wie er jetzt aussieht.
"geht nicht" ist leider als Information nicht ausreichend. Woran siehst
Du das es nicht geht?
Wenn man davon ausgeht das der Quellcode sich nicht wesentlich verändert
hat, also immer noch
1
eventQueue*mainEventQueue;// Pointer to the first Element of the queue
da steht, dann "geht" der Code deswegen nicht, weil noch kein Speicher
für das worauf mainEventQueue zeigt, alloziiert ist. Ergo gibt es die
Elemente data und next nicht.
Und noch ein Tip:
starte deinen ersten Ausflug in die Welt der dynamischen Datenstrukturen
NICHT auf einem AVR sondern auf einem PC. Du hast dort wesentlich
bessere Debug-Möglichkeiten. Alleine die Tatsache, dass du dir deine
Queue mittels
eine simple View-Funktion machen kannst, die ziemlich zuverlässig Amok
läuft, sobald auch nur der kleinste Hund in deiner Datenstruktur
enthalten ist, ist die Vorarbeit auf einem PC wert. Nach jedem add
oder delete die print Funktion aufrufen und nachsehen, ob das erwartete
Ergebnis vorliegt.
Du wirst nämlich schnell merken, dass bei dynamischen Datenstrukturen
jede Anweisung 100% korrekt sein muss, ansonsten geht etwas schief. Das
gilt auch für die Reihenfolge von Anweisungen.
Das sieht zwar alles auf den ersten Blick korrekt aus, aber Fehler
finden sich in dynamischen Strukturen immer erst >20 Schritte später.
Das alte Prinzip, wonach ein Fehler im Debugger dort zu suchen ist, wo
auch der Absturz passiert, gilt hier nicht mehr.
Wenn du deine Eventqueue auf dem PC korrekt laufen hast, lässt sich das
alles 100% 1:1 auf den AVR übernehmen.
Und leg dir Papier und BLeistift bereit und zeichne die Operationen mit!
Für jeden Pointer malst du ein kleines Rechteck und wenn es sich dabei
um eine benannte Variable handelt, dann schreibst du den Variablennamen
darüber. Machst du einen malloc, dann malst du ein größeres Rechteck
aufs Papier. Bei der Zuweisung des Pointers von malloc in eine Variable,
malst du einen Pfeil von der 'Variablen' zum Rechteck. Genauso bei jeder
anderen Zuweisung von Adressen an Pointer: Ein Pfeil wird eingezeichnet
(Ausnahme: NULL wird einfach hineingeschrieben)
Das ist die Ausgangssituation:
Du hast 2 Variablen, beides Pointer
1
mainEventQueue
2
+------------------+
3
| |
4
+------------------+
5
6
endOfQueue
7
+------------------+
8
| |
9
+------------------+
Jetzt gehen wir mal die add Funktion durch und zeichnen bei jedem
Statement mit. Und zwar nur das, was auch tatsächlich im Quelltext
steht!
Wir fangen bei der initFunktion an, die einfach nur die beiden Pointer
Variablen auf NULL setzt
1
mainEventQueue
2
+------------------+
3
| NULL |
4
+------------------+
5
6
endOfQueue
7
+------------------+
8
| NULL |
9
+------------------+
Die add-Funktion beginnt und bekommt einen Pointer auf einen Event in
evt
1
mainEventQueue
2
+------------------+
3
| NULL |
4
+------------------+
5
6
endOfQueue
7
+------------------+
8
| NULL |
9
+------------------+
10
11
12
evt
13
+-------------+ +-------------+
14
| o---------------->| |
15
+-------------+ | Event |
16
| |
17
+-------------+
Die Funktion beginnt
1
eventQueue*newElement=malloc(sizeof(eventQueue));
auf dem Papier bedeutet das, das ein neues Rechteck geboren wird, und es
eine weitere Pointer Variable namens newElement gibt, die auf dieses
Rechteck zeigt
1
mainEventQueue
2
+------------------+
3
| NULL |
4
+------------------+
5
6
endOfQueue
7
+------------------+
8
| NULL |
9
+------------------+
10
11
newElement
12
+---------------+ +------------+
13
| o-------------------------->| data: |
14
+---------------+ | next: |
15
+------------+
16
17
evt
18
+-------------+ +-------------+
19
| o---------------->| |
20
+-------------+ | Event |
21
| |
22
+-------------+
Beachte: Bei data bzw. next habe ich nichts eingetragen. Keinen Pfeil
und auch nicht NULL. Denn genau das macht auch malloc, nämlich nichts.
Was auch immer dort steht ist zufällig. Und zur Erinnerung: Du darfst
auf dem Papier nur das machen, was auch dein Programm macht!
Weiter im Text
1
if(newElement!=NULL){
Such dir die Variable newElement in deiner Zeichnung. Steht da NULL
drinnen oder nicht? Da geht ein Pfeil raus, newElement ist also nicht
NULL. Also wird der then Teil vom if genommen
1
newElement->data=evt;
rechte Seite der Zuweisung: evt. Von evt soll also der Wert genommen
werden. Das läuft in der Zeichnung darauf hinaus, dass ein 2-ter Pfeil
konstruiert wird, der dorthin zeigt, wo auch evt hinzeigt. Und wo soll
dieser Pfeil beginnen? In newElement->data. Also: newElement aufsuchen.
Dort muss ein Pfeil beginnen (daher im Quelltext auch ->). Dieser Pfeil
endet in einem Rechteck und in diesem Rechteck muss es ein data Feld
geben. Dort soll der neue Pfeil beginnen.
1
mainEventQueue
2
+------------------+
3
| NULL |
4
+------------------+
5
6
endOfQueue
7
+------------------+
8
| NULL |
9
+------------------+
10
11
newElement
12
+---------------+ +------------+
13
| o-------------------------->| data: o--------+
14
+---------------+ | next: | |
15
+------------+ |
16
|
17
+-----------------------------+
18
evt v
19
+-------------+ +-------------+
20
| o---------------->| |
21
+-------------+ | Event |
22
| |
23
+-------------+
Überzeuge dich davon, dass der neu eingezeichnete Pfeil exakt dem
entspricht, was durch
1
newElement->data=evt;
gefordert war!
Weiter im Text
1
newElement->next=NULL;
Das ist wieder einfach. newElement aufsuchen, dem Pfeil einmal folgen
und im Rechteck bei next einen NULL Pointer eintragen
1
mainEventQueue
2
+------------------+
3
| NULL |
4
+------------------+
5
6
endOfQueue
7
+------------------+
8
| NULL |
9
+------------------+
10
11
newElement
12
+---------------+ +------------+
13
| o-------------------------->| data: o--------+
14
+---------------+ | next: NULL | |
15
+------------+ |
16
|
17
+-----------------------------+
18
evt v
19
+-------------+ +-------------+
20
| o---------------->| |
21
+-------------+ | Event |
22
| |
23
+-------------+
weiter
1
if(mainEventQueue==NULL)
sieh dir meinEventQueue an. Steht da NULL drinnen?
Ja das tut es, also wird der then-Zweig genommen
1
mainEventQueue=newElement;
Aha. Kennen wir schon. In mainEventQueue einen Pfeil installieren, der
dorthin zeigt, wo auch newElement hinzeigt
1
mainEventQueue
2
+------------------+
3
| o--------------------------------+
4
+------------------+ |
5
|
6
endOfQueue |
7
+------------------+ |
8
| NULL | |
9
+------------------+ |
10
|
11
newElement v
12
+---------------+ +------------+
13
| o-------------------------->| data: o--------+
14
+---------------+ | next: NULL | |
15
+------------+ |
16
|
17
+-----------------------------+
18
evt v
19
+-------------+ +-------------+
20
| o---------------->| |
21
+-------------+ | Event |
22
| |
23
+-------------+
1
endOfQueue=newElement;
Auch das ist wieder einfach
1
mainEventQueue
2
+------------------+
3
| o--------------------------------+
4
+------------------+ |
5
|
6
endOfQueue |
7
+------------------+ |
8
| o-------------------------------+|
9
+------------------+ ||
10
||
11
newElement vv
12
+---------------+ +------------+
13
| o-------------------------->| data: o--------+
14
+---------------+ | next: NULL | |
15
+------------+ |
16
|
17
+-----------------------------+
18
evt v
19
+-------------+ +-------------+
20
| o---------------->| |
21
+-------------+ | Event |
22
| |
23
+-------------+
1
length=length+1;
OK. length habe ich bis jetzt ignoriert. Die Variable würde ich sowieso
rauswerfen.
Damit ist die Funktion zu Ende und die Variablen evt und newElement als
lokale Varibablen werden aufgelöst:
1
mainEventQueue
2
+------------------+
3
| o--------------------------------+
4
+------------------+ |
5
|
6
endOfQueue |
7
+------------------+ |
8
| o-------------------------------+|
9
+------------------+ ||
10
||
11
vv
12
+------------+
13
| data: o--------+
14
| next: NULL | |
15
+------------+ |
16
|
17
+-----------------------------+
18
v
19
+-------------+
20
| |
21
| Event |
22
| |
23
+-------------+
Und? Das Ergebnis sieht richtig aus. Alle Pointer Variablen zeigen
irgendwo hin oder sind NULL. Kein Pointer zeigt ins Nichts (Pfeil der an
keinem Rechteck endet)
(Fortsetzung folgt)
Sieh dir die Variable mainEventQueue an. Ist sie NULL? Nein, da kommt
ein Pfeil heraus. Also wird der else Zweig genommen
1
endOfQueue->next=newElement;
Die Variable endOfQueue aufsuchen, dem Pfeil folgen (wegen dem -> im
Quelltext) und im Rechteck an dem du landest bei next einen Pfeil
eintragen, der dort endet, wo auch newElement endet:
Zurücklehnen und die Datenstruktur studieren.
Sieht gut aus. Beginnend mit mainEventQueue startet eine Kette von
EventQueue Objekten, die über den next-Pointer sauber miteinander
verknüpft sind. Das letzte EventQueue Objekt hat ein NULL im next
Pointer. Jedes EventQueue Objekt zeigt auch auf einen Event in seinem
data Feld und endOfQueue zeigt auch tatsächlich auf das letzte
EventQueue Objekt in der Kette.
Bleibt nur noch darüber zu sprechen, was bei der Umkehrung (free) am
Papier passieren soll.
Wenn dein Programm einen free ausführt, dann radierst du das Rechteck
aus auf das der angegebene Pfeil zeigt, inklusive aller Pfeile die aus
dem Rechteck herausführen. Und das wars dann schon. Insbesondere
radierst du keinen Pfeil weg, der zu diesem Rechteck geführt hat! Nur
weil man eine struct mittels free löscht, wird deswegen noch lange kein
Pointer der dorthin zeigt umgesetzt. Der Pointer zeigt nach wie vor auf
die gleiche Stelle im Speicher und die Tatsache, dass du dann am Papier
einen Pfeil hast, der im Nichts endet, soll dich darauf aufmerksam
machen, dass dann eine Operation wie Pointer->next einfach nicht mehr
machbar ist. Der Pfeil zeigt ins Leere, dort gibt es kein next Feld!
Gehen wir als Beispiel deine etwas umgewandelte get Funktion durch
Der Aufrufer kriegt also einen Pointer zurück. ret zeigt tatsächlich
noch auf etwas und dieses Etwas ist sogar sinnvoll, nämlich die
Event-Beschreibung des ersten Events.
Damit ist die Funktion wieder zu Ende und die lokalen Variablen werden
aufgelöst
Sieht nach wie vor gut aus. Der main Pointer zeigt auf den Anfang der
verbleibenden EventQueue Objekte, auch endOfQueue ist noch gültig. Das
einsame Event Objekt da mitten drinn, ist auch kein Problem, ein Pointer
darauf wurde ja dem Aufrufer zurückgegeben, er ist auch dafür zuständig,
dass es zerstört wird. Genauso wie er dafür zuständig war, dass es beim
Aufruf von add schon erzeugt war.
Was meinst du damit?
ich habe kein Emulator (VIEL ZU VIEL GELD)
Mit nem Simulator lassen sich nur sehr schwehr interrupts erzeugen, das
ist zu kompliziert und warum?
Debugger (JTAG ICE) hab ich auch nicht.
Wozu auch, man kann doch über LEDs debuggen. oder man nimmt halt rs232
und hat nen printfdebugging