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!
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) .
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
structdlist{
2
structdlist*next;
3
structdlist*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.
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...
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.
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...
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.
> 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.
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.
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.
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!
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.