Forum: PC-Programmierung [c] Eine Funktion für verschiedene Listen


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Rogerger92 (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Hallo Community,

ich habe mich im Moment mit Listenprogrammierung in C beschäftigt. Dabei 
habe ich mich bis jetzt immer auf eine Liste beschränkt.

Jetzt, da ich gerne etwas komplexere Übungen machen möchte, steh ich vor 
folgendem Problem: Wenn ich mit verschiedenen structs arbeite, müsste 
ich mit meinem aktuellen Wissensstand für jede Liste eine extra Funktion 
schreiben, um ihre Elemente zu bearbeiten(Add, delete, Show, usw...), da 
die verschiedenen Listen i.d.R. aus verschiedenen Datentypen bestehen.

Gibt es eine Möglichkeit, für verschiedene Listen jeweils eine Funktion 
für die bereits erwähnten Bearbeitungen, zu schreiben? Also jeweils eine 
Funktion für das Hinzufügen von Listenelementen, eine zum Löschen, usw.

Meine Idee wäre, einer Funktion einen Typunabhängigen Pointer auf den 
Anfang einer Liste zu übergeben. Wenn nun ein definierter Zusammenhang 
der Adressen zwischen struct-Typ und Listen-TypEN besteht, kann ich mir 
doch ohne Probleme über nen Algorithmus alle Datentypen nacheinander 
abfragen, oder?!

Ob diese Idee so umsetzbar ist und ob es keine bzw. eine einfachere 
Lösung gibt, weiß ich nicht. Deswegen an euch nun die Frage, ob es 
möglich ist und wie es i.d.R. umgesetzt wird, bzw. wie ihr es umsetzen 
würdet.

Vielen Dank schon mal!

von Dr. Sommer (Gast)


Bewertung
0 lesenswert
nicht lesenswert
U.a. deswegen gibt es C++, dort kann man das nämlich mit templates 
sauber und sicher (ohne gefährliche casts) machen. Die Standard 
Bibliothek enthält sogar bereits Listen und andere solche Daten 
Strukturen, aber es ist eine sehr gute Übung die selber zu 
implementieren. m.M.n. lohnt es wenig, das in C zusammen zu fummeln wenn 
man das in C++ auch richtig machen kann. In C kann man sich mit Makros 
behelfen, oder als Listenelement Typ immer void* nutzen und beim Zugriff 
immer umcasten.

Rogerger92 schrieb:
> Wenn nun ein definierter Zusammenhang der Adressen zwischen struct-Typ
> und Listen-TypEN besteht, kann ich mir doch ohne Probleme über nen
> Algorithmus alle Datentypen nacheinander abfragen, oder?!

In C ist so etwas aber nicht definiert, und daher nicht erlaubt (obwohl 
es meistens vermutlich funktioniert) .

von imonbln (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Warum brauchst du mehr als ein Listen Type? Der Trick hier ist eine 
generische Liste zu schreiben und dann die Konkreten Datentypen da 
einzuhängen. Im Trivialfall schreibst du dir eine Liste mit einen 
Pointer auf void und hängst an den void Pointer die jeweiligen Daten, 
der C Standard garantiert dir das du jeden Pointer zu void * und zurück 
casten kannst ohne Eigenschaften zu verlieren.
1
struct dlist {
2
    struct dlist *next;
3
    struct dlist *prev;
4
    void         *ctx;  // hier ein Pointer auf deine Struktur dranhängen.
5
}

die angerissen struct ist deine generische liste und an den ctx 
(context) hängst du nun einfach die jeweilige Struktur welche du 
brauchst. dann kannst du für die dlist alle möglichen Operationen 
bereitstellen und musst die Liste  nur einmal Implementieren.

Nachteil dieser Implementierung du kannst in die liste alles hängen und 
somit einfach Apfel mit Birnen vergleichen. Du musst also etwas beim 
Implementieren acht geben.

Alternativ gibt es noch andere Möglichkeiten eine generische Listen zu 
erstellen, mir Persönlich gefällt die des Linux Kernel, aber die ist 
etwas Komplexer da sie mit C Makros und etwas Pointerarithmetik arbeitet 
und ist IMHO daher nicht für Anfänger zu empfehlen, obwohl sie sehr 
bequem zu benutzen ist.

von c-hater (Gast)


Bewertung
-4 lesenswert
nicht lesenswert
Dr. Sommer schrieb:

> U.a. deswegen gibt es C++, dort kann man das nämlich mit templates
> sauber und sicher (ohne gefährliche casts) machen.

Casts als solche sind nicht gefährlich. Sie sind nur dann gefährlich, 
wenn man einen Fehler bei ihrem Einsatz macht. Das gilt aber für 
praktisch jedes andere Sprachelement einer Hochsprache ganz genauso.

Übrigens auch im RL: wenn man einem Dreijährigen eine Flex in die Hand 
drückt, wird eher selten was Gutes bei rauskommen. Also gib's für 
Dreijährigen eben besser nur Quietsche-Entchen...

Aber: manche Leute werden irgendwann erwachsen und können dann eben auch 
mit einer Flex korrekt umgehen. Andere Leute bubbeln lieber bis in alle 
Eqigkeit mit ihrem Quietsche-Entchen...

Wobei C++ im Rahmen dieses Vergleiches eher ein Quietsche-Entchen mit 
verstecker Flex mit nahezu unberechenbarem Schalter ist. Aber Hauptsache 
schön bunt und quietscht beim Bussy...

von Rolf M. (rmagnus)


Bewertung
2 lesenswert
nicht lesenswert
c-hater schrieb:
> Dr. Sommer schrieb:
>
>> U.a. deswegen gibt es C++, dort kann man das nämlich mit templates
>> sauber und sicher (ohne gefährliche casts) machen.
>
> Casts als solche sind nicht gefährlich. Sie sind nur dann gefährlich,
> wenn man einen Fehler bei ihrem Einsatz macht.

Autorennen ist an sich auch nicht gefährlich. Nur wenn man dabei in die 
Wand fährt, ist es gefährlich.
Und wenn man von einem Turm springt, ist das auch völlig ungefährlich. 
Erst der Aufprall birgt eine gewisse Gefahr.

> Aber: manche Leute werden irgendwann erwachsen und können dann eben auch
> mit einer Flex korrekt umgehen.

Die lernen aber auch, eine Flex nur dann zu benutzen, wenn man sie auch 
braucht und nicht z.B. um sich die Fingernägel zu schneiden, wenn dafür 
ein besser geeignetes Werkzeug zur Verfügung steht.

von Dr. Sommer (Gast)


Bewertung
1 lesenswert
nicht lesenswert
c-hater schrieb:
> Casts als solche sind nicht gefährlich. Sie sind nur dann gefährlich,
> wenn man einen Fehler bei ihrem Einsatz macht

Genau so ist es. C++ templates und die davon ermöglichte Typsicherheit 
hilft einem, diese Fehler zu vermeiden, indem der Compiler die Typen 
überprüft und Fehler Meldungen ausgibt. Ohne diese Hilfe muss man diese 
Fehler durch Debuggen finden, was ganz schön lästig sein kann. Der 
perfekte Programmierer macht natürlich keine Fehler, aber die meisten 
sind eben nicht perfekt...

von imonbln (Gast)


Bewertung
1 lesenswert
nicht lesenswert
Der TO hat aber nach C gefragt.

Ja C++ ist schoen und wenn man die Wahl hat eine gute Sache welche 
vieles leichter macht. Aber das ist Python auch nur wird das nie 
vorgeschlagen da es kein Ersatz fuer C ist.

Aber jedesmal wenn hier jemand mit C kommt, fangt die Diskussion an nimm 
doch besser C++ wegen $TOLLES_FEATURE. Das mal kurz anzusprechen finde 
ich auch Okay, aber es kann Grunde geben warum der TO nach C fragt, sei 
es eine Legacy Codebase oder akademische.
Denn TO ist jedenfalls nicht gehofen wenn bei jeden C Thread reflexartig 
die Antwort kommt nimm C++ das ist besser.

Das ist Pyhthon auch und das kann duck typing, nur das nutz denn TO 
nicht bei sein C Problem.

von QO (Gast)


Bewertung
-4 lesenswert
nicht lesenswert
> Das ist Pyhthon auch und das kann duck typing, nur das nutz denn TO
> nicht bei sein C Problem.

Doch: Python ist OpenSource UND in C implementiert - der TO kann 
also in den Quellcode von Python versinken und sehen wie es da gemacht 
ist.

von Nase (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Man kann auch einen generischen Strukturkopf definieren, den man dann 
wiederverwertet:
1
struct item_data_t {
2
  struct item_t *next;
3
  struct item_t *previous;
4
};
5
6
struct item_t {
7
  struct item_data_t item_data;
8
};
9
10
void insert(struct item_t *pos, struct item_t *item) {
11
  item->item_data.next = pos;
12
  /* ... */
13
}
14
15
16
17
18
struct mein_item_t {
19
  struct item_data_t item_data;
20
21
  int mein_integer;
22
  double mein_array[17];
23
  /* ... */
24
};
25
26
27
struct mein_item_t *a = malloc(sizeof(struct mein_item_t));
28
struct mein_item_t *b = malloc(sizeof(struct mein_item_t));
29
/* ... */
30
insert(a, b);

Das spart dann halt eine Indirektion.
Die glib macht das ziemlich exzessiv so.

von imonbln (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Nase schrieb:
> struct mein_item_t {
>   struct item_data_t item_data;

Setzt aber voraus dass die struct item_data_t item_data;
immer als erstes in der Struktur definiert ist.
Ansonsten geht der implizite cast

(item_t) mein_item_t

schief und du hast vermeintliche Pointer wild in den Speicher zeigen.

von Nase (Gast)


Bewertung
0 lesenswert
nicht lesenswert
imonbln schrieb:
> Nase schrieb:
>> struct mein_item_t {
>>   struct item_data_t item_data;
>
> Setzt aber voraus dass die struct item_data_t item_data;
> immer als erstes in der Struktur definiert ist.

Ja natürlich, das ist ja auch der ganze Trick dabei.

von B. Stroustrup (Gast)


Bewertung
-3 lesenswert
nicht lesenswert
Rogerger92 schrieb:
> Wenn ich mit verschiedenen structs arbeite, müsste
> ich mit meinem aktuellen Wissensstand für jede Liste eine extra Funktion
> schreiben

Nein, du musst gar keine Funktion schreiben. Du kannst einfach einen 
Befehl nach dem anderen ausführen. Es gibt sogar einen Geheimtip, der 
nennt sich for-loop, damit kannst du mit einem Statement die gesamte 
Liste bearbeiten.

Ändere mal deinen Wissensstand und komm in 1-2 Jahren wieder. Viel 
Erfolg!

von Nase (Gast)


Bewertung
-2 lesenswert
nicht lesenswert
B. Stroustrup schrieb:
> Ändere mal deinen Wissensstand und komm in 1-2 Jahren wieder.
Du hast das Problem überhaupt nicht verstanden.
Setzen, 6.

von Wilhelm M. (wimalopaan)


Bewertung
1 lesenswert
nicht lesenswert
imonbln schrieb:
> Der TO hat aber nach C gefragt.
>
> Ja C++ ist schoen und wenn man die Wahl hat eine gute Sache welche
> vieles leichter macht. Aber das ist Python auch nur wird das nie
> vorgeschlagen da es kein Ersatz fuer C ist.

Genau, der TO hat nach C gefragt - und er hat das Problem ja auch 
erkannt, dass er eigentlich generischen Code schreiben muss/möchte! 
Super: er hat über den C-Tellerrand hinausgeblickt! Das ist ein Chance 
...

Andererseits ist es ja das Schöne an C++, dass es eine 
Multiparadigmen-Sprache ist. Der TO muss also weder objektorientiert 
noch objektbasiert programmieren, in diesem Fall reicht es ja aus, wenn 
er sich  mit der generischen Programmierung (templates) befasst (rein 
prozedural). Und dann wird der TO erkennen, welch ein mächtiges Werkzeug 
er da kennengelernt hat. Vor allem hat er damit sehr viel Sicherheit 
gewonnen, da die meisten Fehler das Programm gar nicht erst wohlgeformt 
machen und damit nicht compilierbar!

Leider provozieren die meisten Threads hier ein Gegeneinander von C und 
C++. Dabei sollte man m.E. ein gutes Miteinander fördern: für bestehende 
Projekte wie beim TO führt das zu einem selektiven Feature-Picking. Und 
bei neuen Projekten sollte man gleich mit C++ beginnen: auch wenn es 
vielleicht "nur" aus dem (gelegentlichen) Einsatz von Funktionstemplates 
oder domänen-spezifischen Datentypen besteht (was m.E. ein ganz grosser 
Schritt nach vorne bedeutet) oder dem Verbannen von nicht-typsicheren, 
nicht ge-scoped-ten Präprozessormacros besteht.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.