Hallöchen,
Ich bin momentan am Verzweifeln. Ich soll für ein Praktikum ein paar
Funktionen in C schreiben, die später auf C++ umgebaut werden sollen,
nur zur Einführung erst noch in C.
Folgendes soll geschehen:
Es gibt eine verkettete Liste mit pHead als erstem und pTail als letztem
Wert, Inhalt ist nur ein int (pHead->data).
Jetzt soll mit folgendem Aufruf die Liste ausgegeben werden:
1
for(pElem=begin();pElem!=end();pElem=next(pElem))
2
printT(pElem);
Das heißt, begin() soll den ersten, end() den letzten und next() immer
den nächsten wert liefern. Die Funktionen soll ich schreiben.. mein
Versuch sieht so aus:
1
T*begin(void)
2
{
3
Node*pHelp;
4
pHelp=pHead;
5
return(T*)pHelp->data;
6
}
7
8
T*end(void)
9
{
10
Node*pHelp;
11
pHelp=pTail;
12
return(T*)pHelp->data;
13
}
14
15
T*next(T*pItem)
16
{
17
Node*pList;
18
pList=(Node*)pItem;
19
pList=pList->pNext;
20
return(T*)pList->data;
21
}
Der Fehler ist ein vielsagendes Segmentation Fault .. :(
Der Typ T ist momentan einfach int, und Node ist die Struktur mit data
als erstem Eintrag und pNext als Strukturzeiger auf den nächsten
Listenwert.
Mir scheint, ich hab irgendwie Lücken im Verständnis für diese ganzen
Aufrufe, wann ich das * brauche und wann ich casten muss, und so weiter
.. im Moment dreh ich echt am Rad, wär wirklich schön wenn mir jemand
helfen könnte! Sitze seit Stunden an der Sache dran ...
Vielen Dank an alle Helfer!!
flip
Was bitte hat das mit Schnittstellenprogrammierung zu tun?
Das ist eine einfach verkette Liste.
Literaturhinweis:
Kernighan & Ritchie: Programmieren in C, 2. Auflage, Hanser-Verlag.
Der Segmentation Fault kommt daher, da du in jeder Funktion einen Wert
vom Typ (T*) zurückgibst, was in deinem Fall ja ein Zeiger auf einen
Integer ist. Du willst aber einen Zeiger auf ein Element deiner Liste.
1
for(pElem=begin();pElem!=end();pElem=next(pElem))
2
printT(pElem);
3
4
Node*begin(void)//Du brauchst eine Node als Rückgabewert
5
{
6
//einfach die erste Node zurückgeben
7
returnpHead;
8
}
9
10
Node*end(void)//hier das selbe
11
{
12
returnpTail;
13
}
14
15
Node*next(T*pItem)
16
{
17
//die ganzen lokalen Variablen sind unnötig, würde aber auch gehen
18
returnpItem->next;//das nächste Element des aktuellen zurückgeben
19
}
Jetzt musst du nur noch dafür sorgen, dass die Print Funktion auch
pItem->data ausgibt.
> für ein Praktikum ein paar Funktionen in C schreiben,> die später auf C++ umgebaut werden sollen> ... verkettete Liste ...
Was für eine nutzlose Aufgabe. Aber mit Praktikanten kann mans ja machen
:-$
Die C-Programmierung kannst du dir sparen, stattdessen ein STL-Buch
lesen. std::list ist genau das, was du suchst, das kannst du in dem
C++-Programm direkt verwenden.
> Was für eine nutzlose Aufgabe. Aber mit Praktikanten kann mans ja machen
Die Aufgabe ist anscheinend nicht nutzlos, er hat klar gezeigt, dass er
kein bisschen nachgedacht hat sondern wahrscheinlich nur Code angepasst
hat, den er irgendwo gefunden hat. Ein wenig ändern bis er compiliert
und dann wundern...
>Die C-Programmierung kannst du dir sparen, stattdessen ein STL-Buch>lesen. std::list ist genau das, was du suchst, das kannst du in dem>C++-Programm direkt verwenden.
Gute Idee. An einer trivialen Liste scheitern, also sich mit 'ner
Bibliothek aus der Affaere ziehen und dann an der naechsten Trivialitaet
scheitern?
Hey,
also erstmal muss ich das richtigstellen: Ich bin kein praktikant in
einer Firma, sondern es ist ein Praktikum (eine Übung) in der FH. Den
Code hab ich auch nicht irgendwoher herkopiert, sondern das Programm war
schon halb fertiggestellt, und wir sollen nur ergänzen.
@ Michael Wittmann: Deswegen kann ich leider auch nicht deinen Vorschlag
umsetzen, denn der Datentyp muss so bleiben wie er ist (T). pElem ist
nämlich auch vom Datentyp T, das hätte ich auch sagen sollen.
Die "Anschuldigungen", dass ich triviale sachen nicht verstanden hätte,
sind wohl richtig, sonst würd ich hier nicht fragen.
Vielleicht hat noch wer einen Tipp für mich? :)
Danke, flip
>>Die C-Programmierung kannst du dir sparen, stattdessen ein STL-Buch>>lesen. std::list ist genau das, was du suchst, das kannst du in dem>>C++-Programm direkt verwenden.>Gute Idee. An einer trivialen Liste scheitern, also sich mit 'ner>Bibliothek aus der Affaere ziehen und dann an der naechsten Trivialitaet>scheitern?
Genau. Immer noch besser, als jedes kleine Rädchen neu erfinden zu
wollen.
Ist pElem tatsächlich vom Type 'T', dann vergiss es.
Falls es aber 'T*', also 'Pointer auf T' ist, gibts eventuell unter
bestimmten Bedingungen eine Lösung. Wenn ich Lehrkraft an einer FH wäre,
würd ich Dir diese allerdings solange um die Ohren hauen, bis sie Dir
abfallen. Und einer Lehrkraft, die sowas akzeptiert gehört fristlos
gekündigt!
1
T*begin(void)
2
{
3
return(T*)pHead;
4
}
5
6
T*end(void)
7
{
8
return(T*)pTail;
9
}
10
11
T*next(T*pItem)
12
{
13
return(T*)((Node*)pItem)->pNext;
14
}
Das funktioniert aber nur unter den folgenden Bedingungen!
Und auch nur, wenn der Compiler auch noch mitspielt.
1
typedefintT;
2
3
typedefstructNode{
4
Tdata;
5
structNode*pNext;
6
}Node;
7
8
Node*pHead;
9
Node*pTail;
Das zeigt wieder mal anschaulich, wie sehr man C vergewaltigen kann.
Vielen lieben Dank, liebe/r "..." !! Es funktioniert!
Könnte mir noch kurz jemand erklären, warum ich so casten/klammern muss:
(T*)((Node*)pItem)->pNext? Und warum ich nicht den Zeiger auf ->data am
Ende zurückgeben muss? (weil ich lern ja nix dabei wenn ichs einfach
abschreib ..)
Meine Vermutung, warum ich nicht "(T*)((Node*)pItem)->pNext->data"
zurückgeben muss ist, weil data ja der erste Eintrag im struct ist, und
daher die gleiche Adresse hat. Aber dann dürfte sich ja nix ändern, wenn
ich das ->data mit dazuschreib. Allerdings krieg ich dann wieder einen
Segmentation fault. Warum?
Für die seltsame casterei hab ich allerdings keine Vermutung, wär schön
wenn mir das jemand erklären könnte ..
Zum Thema Prof vergewaltigt C: Ich mag ihn auch nicht, und wenn man
googlet findet man einige Foren wo er in der Richtung beschimpft wird
... Diese seltsame Struktur wurde genauso von ihm vorgegeben, also er
erzwingt es quasi so .. :s
Also danke nochmal, ich war echt am verzweifeln!
Liebe Grüße, flip
> weil data ja der erste Eintrag im struct ist
Genau. Und deshalb geht es auch nur wenn der Compiler das dann auch
genau so im Speicher anordnet! Und genau deshalb ist diese Lösung auch
Schrott!
>Aber dann dürfte sich ja nix ändern, wenn ich das ->data mit dazuschreib
Doch, weil Du dann den Inhalt von 'data' castest und nicht dessen
Adresse. Damit kommt beim zurück casten ('(Node*)pItem') beim nächsten
Aufruf nur Müll raus.
Wenn schon mit '->data', dann so:
1
T*begin(void)
2
{
3
return&pHead->data;
4
}
5
6
T*end(void)
7
{
8
return&pTail->data;
9
}
10
11
T*next(T*pItem)
12
{
13
return&((Node*)pItem)->pNext->data;
14
}
Beachte das '&'.
Das Ganze funktioniert aber nur, wenn 'data' tatsächlich vom Type 'T'
ist und nicht 'T*'.
Aber wie schon gesagt solltest Du (insbesondere als Anfänger) solche
Vergewaltigungen von Typecasts am besten ganz schnell wieder vergessen.
Solche Sachen führen in der Praxis zu absolut unwartbarem Code und zu
Fehlern, bei denen man sich dumm und dämlich sucht (wie Du ja grad
selber erlebst). Da braucht nur mal jemand kommen und der Struktur noch
einen weiteren Member hinzufügen oder die Reihenfolge der Member ändern
und Du hast verloren.
CU
PS: lieber "..." :)
> seltsame Struktur wurde genauso von ihm vorgegeben
Die Struktur ist nicht das Problem, das Problem ist der Datentype von
pElem und die Parameter- bzw. Rückgabetypen der Funktionen.
Eine bessere Lösung wäre:
Wobei mir grad noch auffällt, daß eigentlich keine der Varianten korrekt
ist. Das letzte Element der Liste wird nicht mit ausgegeben.
Falls die 'for'-Schleife tatsächlich so aussehen MUSS (genau so mit den
Funktionsaufrufen), dann gibt es dafür keine Lösung!
CU
Guten Morgen :)
Vielen Dank für deinen ganzen Einsatz, "..." ! Allerdings hättest du dir
keine großen Gedanken um das letzte Listenelement machen müssen, denn
der Prof hatte das eh so vorgesehen, dass das letzte Element gar nicht
ausgegeben wird, sondern dass das vorletzte das letzte wirkliche Element
ist. Er meinte, das wär eine total gängige Programmierpraxis...
Also, du hast mir sehr geholfen, auf das passende Casting bin ich
einfach nicht gekommen. Aber jetzt passt alles, die ganzen
I/O-Funktionen die noch dazugehören hab ich, nachdem das Problem geklärt
war, auch gut hingebracht.
cya flip
> total gängige Programmierpraxis
Auweia, der Prof gehört wirklich gefeuert.
Sowas wie Dein pTail benutzt man eigenlich nur, wenn man am Ende der
Liste mehrfach möglichst schnell und einfach weitere Elemente anhängen
will (z.B. bei einer FIFO-Implementierung). Ansonsten sorgt man einfach
(bzw. generell) dafür, daß im letzten Element der Liste der pNext
Pointer den Wert NULL hat und benutzt dieses Kriterium um die Schleife
abzubrechen. Normalerweise erfindet man für so simple Sachen auch nicht
extra eigene Funktionen (OK führ Lehr-/Lernzwecke gelegentlich doch
sinvoll, der obige Code wird dadurch aber eher unübersichtlicher als das
er durch die Strukturierung durch Funktionen gewinnt).
Unter der Vorraussetzung, daß bei einer leeren Liste pHead den Wert NULL
enthält, käme man dann zu folgender Kurzform:
1
typedefintT;
2
3
typedefstructNode{
4
Tdata;
5
structNode*pNext;
6
}Node;
7
8
Node*pHead=NULL;
9
Node*pElem;
10
11
/* ... */
12
13
for(pElem=pHead;pElem!=NULL;pElem=pElem->pNext){
14
printT(pElem->data);
15
}
Und die ist durchaus "total gängige Programmierpraxis".
CU