Forum: Mikrocontroller und Digitale Elektronik Schnittstellenprogrammierung in C


von flip (Gast)


Lesenswert?

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

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Was bitte hat das mit Schnittstellenprogrammierung zu tun?

Das ist eine einfach verkette Liste.

Literaturhinweis:

Kernighan & Ritchie: Programmieren in C, 2. Auflage, Hanser-Verlag.

von Michael W. (miwitt001)


Lesenswert?

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
    return pHead;
8
}
9
10
Node *end(void) //hier das selbe
11
{
12
    return pTail;
13
}
14
15
Node *next(T *pItem)
16
{
17
    //die ganzen lokalen Variablen sind unnötig, würde aber auch gehen
18
    return pItem->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.

von tuppes (Gast)


Lesenswert?

> 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.

von Ein Gast (Gast)


Lesenswert?

> 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...

von Oh je (Gast)


Lesenswert?

>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?

von flip (Gast)


Lesenswert?

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

von tuppes (Gast)


Lesenswert?

>>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.

von ... (Gast)


Lesenswert?

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
typedef int T;
2
3
typedef struct Node {
4
  T data;
5
  struct Node* pNext;
6
} Node;
7
8
Node* pHead;
9
Node* pTail;

Das zeigt wieder mal anschaulich, wie sehr man C vergewaltigen kann.

von flip (Gast)


Lesenswert?

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

von ... (Gast)


Lesenswert?

> 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 "..." :)

von ... (Gast)


Lesenswert?

> 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:
1
typedef int T;
2
3
typedef struct Node {
4
  T data;
5
  struct Node* pNext;
6
} Node;
7
8
Node* pHead;
9
Node* pTail;
10
Node* pElem;
11
12
Node* begin(void)
13
{
14
    return pHead;
15
}
16
17
Node* end(void)
18
{
19
    return pTail;
20
}
21
22
Node* next(Node* pItem)
23
{
24
    return pItem->pNext;
25
}
26
27
for(pElem = begin(); pElem != end(); pElem = next(pElem)) {
28
    printT(pElem->data);
29
}

von ... (Gast)


Lesenswert?

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

von ... (Gast)


Lesenswert?

Huch, da fällt mir glatt doch noch ne Lösung für das Problem mit dem 
letzten Listenelement ein:
1
typedef int T;
2
3
typedef struct Node {
4
  T data;
5
  struct Node* pNext;
6
} Node;
7
8
Node* pHead;
9
Node* pTail;
10
Node* pElem;
11
12
Node* begin(void)
13
{
14
    return pHead;
15
}
16
17
Node* end(void)
18
{
19
    return NULL;
20
}
21
22
Node* next(Node* pItem)
23
{
24
    return (pItem != pTail) ? pItem->pNext : NULL;
25
}
26
27
for(pElem = begin(); pElem != end(); pElem = next(pElem)) {
28
    printT(pElem->data);
29
}

CU

von flip (Gast)


Lesenswert?

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

von ... (Gast)


Lesenswert?

> 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
typedef int T;
2
3
typedef struct Node {
4
  T data;
5
  struct Node* 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

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.