Hi
Wie ihr sicherlich aus meinen anderen Threads bereits herauslesen könnt,
bin ich in Sachen Programmieren noch energetischer Anfänger.
Ich möchte gerne von meiner jetzigen Mentalität wegkommen, ein
Programmierproblem direkt vom Kopf in Zeilen Code umzuwandeln, und dann
über Try&Error einen funktionierenden Code zu entwickeln. Ich möchte
stattdessen gerne das Problem in Teilprobleme unterteilen, und dann
strategisch diese Teilprobleme lösen, die dann letztendlich das
Gesamtproblem lösen.
Ich mache mir entsprechend gerade darüber Gedanken, wie man am
geschicktesten ein gegebenes Problem (gegebene Aufgabe) in Teilprobleme
(Teilaufgaben) untergliedert, und diese dann einzeln löst. Das
eigentliche (Programmier)Problem ist dabei erstmal zweitrangig, genau so
wie die optimale/effiziente Implementierung (ob nun ein Zeiger verwendet
wurde, oder ein Struct, oder eine for-Schleifer oder eine while-Schleife
etc.).
Gibt es diesbezüglich eine Art best practices (auch wie tiefgründig die
Teilprobleme getestet werden sollten), die ihr empfehlen könnt?
Wie lernt man, sich in diese Art von Problemunterteilung
hineinzuversetzen?
Danke und Gruß,
Einfach machen. Kontinuierlich darüber nachdenken, ob einem was besseres
einfällt, als das was man in der Vergangenheit gemacht hat. Falls ja,
ändern, und evaluieren ob es jetzt tatsächlich besser ist.
Die Fähigkeit, am Anfang gleich eine gute Struktur zu raten kommt dann
mit der Zeit dazu. Ist mE reine Erfahrungssache aus der Praxis und lässt
sich nicht spezifisch lernen.
Gerd schrieb:> Wie lernt man, sich in diese Art von Problemunterteilung> hineinzuversetzen?
Das allerwichtigste hast Du schon gelernt, nämlich den richtigen
"Mindset", die eigene Retrospektive. Das ist das Fundament.
Es ist nicht ganz einfach das Dickicht von Techniken, Methoden und
Mode-Erscheinungen (ja, in der Softwareentwicklung gibt es auch Mode)
zur Software-Entwicklung zu durchdringen.
Einige Einstiegspunkte zur Thematik:
https://de.wikipedia.org/wiki/Kategorie:Vorgehensmodell_(Software)
und:
https://de.wikipedia.org/wiki/Kategorie:Programmierparadigma
Ganz konkret möchte ich Dir den Tipp geben, bevor Du loslegst etwas zu
Programmieren, einfach ein paar Notizen, völlig formlos, auf eine Seite
Papier zu schreiben, wie Du das konkrete Problem lösen willst. Das
können irgendwelche "Kästchen" sein, die mit Pfeilen in Beziehung
gesetzt werden, oder auch nur eine Liste von was Du in welcher
Reihenfolge lösen willst.
Es geht nicht darum, irgendwelche UML-Diagramme oder so zu malen, sonder
nur darum, die eigenen Gedanken zu sortieren und einen "Master-Plan" zu
haben, bevor Du los programmierst.
Als "Über-Konzept" möchte ich Dir einen Zyklus aus "Vorwärts gehen" und
"Aufräumen" beim Programmieren vorschlagen, der ständig wiederholt wird.
D.h. es gibt beim Programmieren die Phase "Erschaffen", in der schreibst
Du neuen Code und gehst nach Vorne, und es gibt die Phase "Aufräumen",
in der machst Du das was Du neu geschrieben hast, wieder "schön". Also
immer abwechselnd "Vorwärts" und "Aufräumen".
Viel Erfolg!
Ach, und such Dir jemanden oder mehrere, mit denen Du Dich austauschen
kannst. So lernt ihr voneinander. Das ist recht effektiv. Gerade beim
Programmieren ist es ein großer Fehler, alleine im Kämmerchen zu sitzen.
Zum "drauflos programmieren": das ist wie Schreibmaschine schreiben oder
Klavierspielen.
Als Anfänger bist Du mit Papier und Bleistift immer schneller.
Mit entsprechender Übung wäre es dumm, den Roman oder die Komposition
oder Konzepte nicht gleich in die Tastatur zu hacken, wenn man etwas
ausprobieren will. Und für Brainstorming oder Mindmaps halt was anderes
zu nehmen
Solange man den Scheiß vom Anfang auch in die Tonne kloppt, wenn man es
besser versteht.
Der Vorteil von Papier und Bleistift ist dann nur noch, dass man es in
jedem Fall nochmal ins reine schreiben muss.
Ansonsten: whatever floats your boat.
Im Grunde ist das eine alltägliche Fähigkeit, die man im Laufe der
ersten 10 Jahre des Lebens ausbildet.
Erst die Schuhe anziehen oder die Hosen? Erstmal duschen, dann in
Pfützen springen und dann zu Oma zum Kaffee? Oder doch lieber
andersherum?
Erst die Brote oder erst die Bücher einpacken? Die Brote noch einpacken,
so das keine Fettflecken entstehen? Erst Mathe machen und dann Deutsch
und zwischendurch (Interrupt) von Mutter zum Essen gerufen werden.
Dann irgendwann mal der zweite Umzug. Erst die Schränke ausräumen, bevor
man sie zerlegt. Das Werkzeug ganz am Schluss. Dann kehren und den Besen
noch mitnehmen. Etcpp.
Das tägliche Leben besteht aus Planung (und ein Gutteil Improvisation).
Man schaut sich die Stadt an in der man lebt. Es gibt Mülltonnen an
bestimmten Orten und an anderen nicht. Sonntags ist alles zu. Dienstags
ist Markt. Feuerwehr ist immer bereit. An bestimmten Stellen gibt es
Halteverbote. Anderswo einen Park. Der Müll wird in meiner Strasse am
Freitag alle 2 Wochen geholt und beim Kumpel zwei Strassen weiter am
Mittwoch. Erst die Strassendecke aufstemmen und dann die Rohre darunter
wechseln und vorher noch die Anwohner wg. Stromunterbrechung
benachrichtigen. Warum ist das so? Hast Du dazu Thesen entwickelt?
Es ist nun die Frage, was genau beim Programmieren dabei anders ist.
Eigentlich sind es nur die Subjekte (Daten) und die "Werkzeuge"
(Anweisungen, Ausdrücke, Funktion, Algorithmen). Sie haben und bilden
wie eine Wohnung, eine Stadt, das Leben eine Struktur, bestimmte
Abhängigkeiten oder eben keine. Denk Dir, Du baust eine neue Stadt und
organisierst sie.
Genau so macht man das.
Häufig hilft es, das Problem über die nötigen Datenstrukturen anzugehen.
Welche Daten gehören auf welchem Layer zusammen, welche Hilfsstrukturen
brauche ich, um die Daten zu organisieren und die gewünschten
Manipulationen durchzuführen. (Tipp: Redundanz so weit wie Möglich
vermeiden.) Daraus folgt dann auch eine Einteilung des Programms in
bestimmte Unterprobleme.
Ach so, wie andere schon schrieben: üben, üben, üben. Programmieren ist
ein Handwerk (ja, im direkten Sinne, wie Schreiner, Schmied, Steinmetz,
Koch, etc), bei dem man sich im Laufe von Jahren immer weiter
verbessert. Das lernt man nicht über Nacht. Eine gute Quelle, neue
Herangehensweisen/Methoden/Algorithmen/Strukturen/Kniffe kennen zu
lernen, ist es, fremden Code zu analysieren/studieren[1]. So sammelt
sich im Laufe der Zeit ein immer größeres Repertoir an, mit dem man
arbeiten kann.
[1] Dazu gehört auch das Suchen von Bugs in fremden Code - bringt einem
eine Menge!
Als ich damals anfing zu programmieren (Pascal und Fortran) habe ich die
Ergebnisse, auch die der Subroutinen, in einfache Textdateien
geschrieben. Und dann verglichen: was-steht-da, und was soll-da-stehen?
Das läßt sich auf jede Sprache übertragen.
Gerd schrieb:> Ich möchte gerne von meiner jetzigen Mentalität wegkommen, ein> Programmierproblem direkt vom Kopf in Zeilen Code umzuwandeln, und dann> über Try&Error einen funktionierenden Code zu entwickeln.
PAP= Programmablaufplan. Da kann man die Struktur mit festlegen.
Aber ehrlicherweise mache ich das auch nicht.
Was ich immer noch mache, weil ich das mit dem Debugging noch nicht
wusste/konnte, ist das Led Debugging.
An verschiedenen Stellen im Programm den Blinkcode eingefügt und
geschaut, ob das Programm dort hin kommt.
Im Anfang habe ich die Geschwindigkeit von so einem Controller völlig
unterschätzt. Da lagen bei mir eigentlich die meisten Fehler.
Gerd schrieb:> Ich möchte gerne von meiner jetzigen Mentalität> wegkommen, ein Programmierproblem direkt vom Kopf> in Zeilen Code umzuwandeln, und dann über Try&Error> einen funktionierenden Code zu entwickeln.
Schrittweise vorgehen: Ehe man ein Problem LÖSEN
kann, sollte man es erstmal klar FORMULIEREN.
Dafür ist es i.d.R. hilfreich, die vorläufigen
Formulierungsversuche NIEDERZUSCHREIBEN und
schrittweise zu verbessern.
Es kann hilfreich sein, dabei auf eine gewisse
Vollständigkeit zu achten :)
Dabei auf Überraschungen gefasst sein: Es kann sein,
dass man ursprünglich vorhatte, eine OCR-Software
zu entwickeln, mitten drin aber feststellt, dass
das eigentliche Problem gar nicht die Erkennung der
Einzelzeichen ist, sondern die LAYOUTANALYSE.
> Ich möchte stattdessen gerne das Problem in> Teilprobleme unterteilen, und dann strategisch> diese Teilprobleme lösen, die dann letztendlich> das Gesamtproblem lösen.
Ob und wie gut das funktioniert, hängt entscheidend
vom Innovationsgrad ab. Für hinreichend neuartige
Dinge führt dieser Ansatz zielsicher in die Sackgasse.
Das heißt nicht, dass man nicht zerlegen soll. Es
heißt nur, dass man bereit sein sollte, die
Zerlegung ungefähr zwanzig Mal zu ändern, weil sie
noch nicht funktioniert.
Die richtige Struktur ist ein wesentliches Entwicklungs-
ergebnis -- keine Voraussetzung!
> Ich mache mir entsprechend gerade darüber Gedanken,> wie man am geschicktesten ein gegebenes Problem> (gegebene Aufgabe) in Teilprobleme (Teilaufgaben)> untergliedert, und diese dann einzeln löst.
Anfängerfehler.
Projektarbeit ist nach meiner bitteren Erfahrung für
den sorgfältigen, akribischen Techniker ein Alptraum:
Er neigt dazu, die Dinge, die schlecht funktionieren,
so zu verbessern, dass sie gut funktionieren. Das ist
aber bei Projektarbeit ein kapitaler Fehler: Es ist
viel wichtiger, erstmal die Dinge ÜBERHAUPT IRGENDWIE
zum Funktionieren zu bringen, die dringend erforderlich
sind, aber bisher von allen übersehen wurden.
Soll heißen: "am geschicktesten" ist der Garant für
einen Reinfall. Wähle erstmal irgendeine Zerlegung,
die Dir halbwegs sinnvoll scheint, und verbessere
diese schrittweise. Mache nichts ohne guten Grund,
d.h. formuliere immer den Mangel und die gewählte
Abhilfe. "Macht man nicht", "ist nicht professionell"
sind keine guten Gründe!
> Das eigentliche (Programmier)Problem ist dabei erstmal> zweitrangig, genau so wie die optimale/effiziente> Implementierung (ob nun ein Zeiger verwendet wurde,> oder ein Struct, oder eine for-Schleifer oder eine> while-Schleife etc.).
Trenne möglichst das Bearbeiten realer Probleme vom
Erlernen und Erkunden der Programmiersprache. Ein
Musikschüler plagt sich zusätzlich zu den Sonatinen
und Divertimenti, die er üben darf, auch noch mit
Tonleitern, Dreiklängen, Doppelgriffen und zahllosen
weiteren technischen Übungen herum. Das lässt sich
nicht vermeiden.
Zunächst macht es Sinn sich damit abzufinden,
dass programmieren IMMER ein iterativer Prozess ist..
man löst das eine Problem, einem fällt beim zweiten auf,
dass man beim ersten was vergessen hat oder besser machen könnte
und man geht wieder einen Schritt zurück.
Nun kann man sich direkt einen logicflow basteln und sich daran halten,
ooder (was mir selbst der natürlichere Weg ist)
man geht direkt ins Eingemachte und bastelt wie ne Flipperkugel...
Sven B. schrieb:> Einfach machen.Lerninstructor schrieb:> learning by doing.
ist schon ziemlich genau der Weg...
man schreibt alles in die main()
und schwupp läuft hello world ;)
irgendwann wird's voll und man merkt, das etwas da weg muss,
also gliedert man es aus,
und gliedert mehr und klassifiziert ausgegliedertes was irgendwie
zusammengehört;
erstellt zwischendurch Platzhalter
(bool checkVal(int x){return true;})
bis man sich dazu was hat einfallen lassen,
so bleibt der Code fast zu jeder Zeit lauffähig, und man kann
einzelne Aspekte (mMn) "einfacher" debuggen.
ohne sich direkt über das grosse Ganze im Klaren zu sein.
Platzhalter sind dafür super...
Und ja das heisst, dass man sehr viel hin und her springen muss im
Zweifel
(Flipperkugel eben),
aber eben auch, dass man sich dem grossen Ganzen in kleinen Schritten
nähern kann (und ich denke so ist Dein Ziel, wenn ich Dich richtig
verstanden hab)
Im Idealfall (ich bin fast immer zu faul) macht man ausreichend Notizen
(kommentare oder extern)
Ansonsten kann es sein, dass man in nem Monat draufguckt und sich nur
wundert (oder schlimmer: ärgert)
Ich ärger mich viel über meine hobby kritzeleien..
Zusammengespuckter Code der zwar läuft aber eben nahezu unwartbar ist.
Manchmal so schlimm, dass es mir nach nem Jahr einfacher scheint von
Vorne zu beginnen als irgendwas mitzunehmen ;)
Also mach lieber mehr Notizen als ich :D
Am Ende das Gedöns noch aufgeräumt und ggf hübsch gemacht
und schon hast Du ein ziemlich ordentliches Projekt.
'sid
> Manchmal so schlimm, dass es mir nach nem Jahr einfacher scheint von> Vorne zu beginnen als irgendwas mitzunehmen ;)
Manchmal? Immer!
Wir sollten uns damit abfinden. Die agile Entwicklung mit ihrem
continuous delivery funktioniert nicht. Nach 2-3 Jahren ist die Software
in einem Zustand, wo wegwerfen und neu schreiben schneller geht.
Ich schlage ein 2-Phasen-Modell vor. Wir planen von vorn herein ein: 2
Jahre kloppen wir Features rein. Bringen jede Woche neue Releases
heraus. Dann werfen wir den Programmcode weg, arbeiten eine neue
Struktur aus. Einige Monate lang gibt es keine neuen Features.
Wir ändern unsere Einstellung zum Wert des Programmcodes.
Der Wert des Codes ist die Arbeit, die wir einsparen, wenn wir ihn
weiter verwenden. Wenn neu schreiben schneller geht, hat der Code einen
negativen Wert. Das Wertvolle sind die Notizen.
Bewährte Programmuster, Algorithmen und Datenstrukturen ansehen
verstehen und verinnerlichen.
Selber welche umsetzen. Mehrere davon umsetzen und Vor- & Nachteile
bewerten.
Viele, möglicht gut gemachte, Programmquelltexte lesen und verstehen.
Weniger gute auch lesen und verstehen, besonders warum sie nicht gut
sind.
Noch eine Meinung schrieb:> Nach 2-3 Jahren ist die Software> in einem Zustand, wo wegwerfen und neu schreiben schneller geht.
Das ist doch Quatsch. Schau dir mal erfolgreiche Projekte wie KDE an, wo
der Großteil vom Code eigentlich relativ lesbar ist; im Kern findet man
da oft noch Dinge von 1999 oder vergleichbarer Zeit.
Der Punkt ist hier, dass alter Code kontinuierlich verbessert wird und
man nicht super viel Angst haben darf ihn anzufassen. Klar, den alten
Core-Code anfassen verursacht oft Bugs -- aber die muss man m.E. durch
ein geeignetes Release-Modell ausgleichen.
Noch eine Meinung schrieb:> Manchmal? Immer!
neee, immer nicht .. glücklicherweise ;)
Manchmal schaffe ich auch bei "ist doch nur für mich jetzt" Zeug
sinnvolle Kommentare zu hinterlassen.
und manchmal ist der Code so kurz (sag <200kb) dass rekapitulation gut
funktioniert ;)
Noch eine Meinung schrieb:> Nach 2-3 Jahren ist die Software> in einem Zustand, wo wegwerfen und neu schreiben schneller geht.
Das kann man zum Glück so auch nicht verallgemeinern ;)
Noch eine Meinung schrieb:> Ich schlage ein 2-Phasen-Modell vor. Wir planen von vorn herein ein: 2> Jahre kloppen wir Features rein. Bringen jede Woche neue Releases> heraus.
Hübsche Idee, funktiniert leider nicht wenn der Code eine
"Auftragsarbeit" für single-shot Kunden ist.
Fertig bei Abgabe und ggf ein bugfix max zwei sonst bleibt's bei
"einmaliger Kunde"
Noch eine Meinung schrieb:> Wir ändern unsere Einstellung zum Wert des Programmcodes.>> Der Wert des Codes ist die Arbeit, die wir einsparen, wenn wir ihn> weiter verwenden. Wenn neu schreiben schneller geht, hat der Code einen> negativen Wert. Das Wertvolle sind die Notizen.
Schöner Ansatz
so läuft's ja eh schon
wenn man den Code zwar geschrieben hab, er einem aber nichtmehr gehört
(in dem Fall ist aber die zweite Neuschrift mindestens günstiger als die
Urschrift, da man sich kaum gegen Erinnerungen schützen kann und sich so
auch mal selber 'kopiert')
Aber Du hast Recht.. der Wert einer Codebibliothek die man sich selber
aufbaut ist nur so hoch wie die Zeitersparnis die man sich damit
verschaffen kann.
Das gilt auch für Code der zwar dem Grunde nach schlecht ist,
solange er einen aber auf gute Ideen bringen kann.
(liegt im Ordner Inspirationsbeschleuniger ;))
'sid
"Dieser Weg kann wie folgt charakterisiert werden: Ein Problem, eine
Aufgabenstellung sei möglichst präzise beschrieben. Auf dieser Grundlage
wird ein Modell entwickelt, welches mit quantitativen und qualitativen
Methoden eine formale Beschreibung der wesentlichen Objekte sowie deren
Attribute und Relationen darstellt. Dieses Modell ist Grundlage für die
Problemlösung mit algorithmischen und damit einem Computer übertragbaren
Methoden."
https://www.springer.com/de/book/9783519002116
Das Modell ist immer der Schlüssel zum Algorithmus, der das Problem
löst.
Viel Erfolg!
Wenn ich ein Programm schreiben, dann stelle ich mir folgende Fragen:
- Was sind die Eingangsdaten, und wie lese ich die ein?
- Wie soll die Ausgabe ausschauen?
- Wie schaut die interne Datenstruktur aus?
- Wie wird die interne Datenstruktur verarbeitet und
durch den angewandten Algorithmus geändert?
Bei den Datenstrukturen hat mir der Sedgewick "Algorithm in C"
gut gefallen.
Das Buch gibt es auch in einer C++ und Java Variante.
Darin sind die grundlegenden Datenstrukturen und Algorithmen
verständlich erklärt. Es eignet sich auch als Buch für
Learning by doing.
Viel lernen kann man auch, indem man gute Open Source Projekte
anschaut, und versucht, zu verstehen, wie die Probleme dort gelöst
wurden. Das ist aber eher für fortgeschrittene Programmierer.
Als Anfänger versuche einfach mal ein Programm, dass dich
interessiert zu bauen (Spiel, Spass, Technik..).
Interesse ist das wichtigste um weiterzukommen!
Um auf einem Gebiet gut zu werden braucht es aber einige Jahre.
Nicht die Geduld verlieren, sondern einfach dranbleiben.
> Schau dir mal erfolgreiche Projekte wie KDE an
Ein gutes Beispiel.
Das Konzept stammt von 1972. Aus dem Smalltalk. Als Trolltech mit der Qt
anfing hatten wir bereits 30 Jahre lang Smalltalk-artige Frameworks
entwickelt und wieder weggeworfen. Diese 20 Jahre alten Komponenten
basieren auf 1000 Projekten, die wieder eingestampft wurden.
Das verrückte an diesem Beispiel:
Objektorientierte GUI Frameworks sind inzwischen ausgereift.
Funktionieren einwandfrei. Aber aus irgend welchen Gründen ignorieren
wir 50 Jahre Erfahrung und fangen mit HTML/JS/CSS neu an.
"Best practices"? In allen anderen Ingenieur-Berufen werden den
Studenten die bewährten best practices beigebracht. Wenn ein Ingenieur
ein Auto konstruiert überlegt er gar nicht, wie er ein Auto in
Teilprobleme zerlegt. Er macht einfach das, was er gelernt hat.
Auf die Programmierung übertragen: KDE ist ausgereift. Das ist besser
als alles, was wir selbst erfinden können. Ein Programmierer sollte im
Studium lernen, wie er so ein Framework benutzt. Erst gar nicht
nachdenken, ob man so etwas auch anders anpacken kann.
Aber das wollen wir doch nicht.
Lieber probieren wir unsere eigenen Ideen aus, fliegen damit auf die
Schnauze und ärgern uns mit verkorksten Programmcode rum.
Lerne C++.
Wenn du unbedingt in C proggen mußt:
Programmiere in Bausteinen.
Jedes Teilproblem kommt separat in eine C- Datei.
Globale Variablen werden in dieser dateistatisch angelegt,
sind also nur im Baustein gültig.
Funktionen sind alle dateiprivat, werden als
wie die Variablen statisch angelegt.
Von außen wird nur auf public - Funktionen
zugegriffen, die dann die static - Funktionen aufrufen
Das ist dann dein Interface. So kannst du innerhalb
des Bausteins Änderungen vornehmen, ohne
jedesmal die main neu schreiben zu müssen.
Lasse deine Bausteine durch Konstruktoren
initialisieren, die am anfang der main in einer
Initialize - Funktion aufgerufen werden.
Jeder Baustein muß zur Bereinigung ne Destruktor Funktion haben,
die dann am Ende Deiner main nacheinander aufgerufen werden.
mfg
Hi,
vielen Dank für die rege Diskussion und die aufmunternden Worte.
Ich werde mich - auch um meine Programmierkenntnisse zu erweitern - mit
dem Buch "Introduction to Algorithms von Cormen" einlesen, und die dort
beschriebenen Probleme in aller Ruhe durcharbeiten. Ich glaube, das
hilft mir, Probleme von Gedanken auf ein Blatt Papier niederzuschreiben
und dann anschließend in C zu programmieren.
Ich werde sicherlich bei weiteren Fragen wieder hier auftauchen :)
Danke und Gruß,
Ich würde noch gerne folgende Stichworte erwähnen: das Untergliedern
eines Programms in Komponenten und damit auch zerlegen eines Problems in
Teilprobleme wird als Softwarearchitektur bezeichnet. Sie hat
statische (welche Komponenten gibt es?) und dynamische (Ablauf)
Ansichten.
Für den Entwurf von Software kann man sich auch die Ansprüche an die
Softwarequalität im Hinterkopf behalten (Änderbarkeit, Benutzbarkeit,
...). Das sorgt auch schon dafür, dass man zu besser überblickbaren
Teilfunktionen neigt.
Im Prinzip aber alles in allem gesunder Menschenverstand. Wenn du mal
rückwirkend etwas ändern musst und merkst dass du dabei Spaghettis
entheddern musst, kriegst du ein Gefühl wie es besser ginge.
Software schrieb:> Ich würde noch gerne folgende Stichworte erwähnen: das Untergliedern> eines Programms in Komponenten und damit auch zerlegen eines Problems in> Teilprobleme wird als Softwarearchitektur bezeichnet. Sie hat> statische (welche Komponenten gibt es?) und dynamische (Ablauf)> Ansichten.> Für den Entwurf von Software kann man sich auch die Ansprüche an die> Softwarequalität im Hinterkopf behalten (Änderbarkeit, Benutzbarkeit,> ...). Das sorgt auch schon dafür, dass man zu besser überblickbaren> Teilfunktionen neigt.
Hi Software,
vielen Dank für die Stichwörter. Ich werde danach mal suchen.
Was mich noch interessieren würde, ist, wie man am besten Software
testet, um sie robust und fehlerfrei zu bekommen?
Lasst uns dazu ein Beispiel nehmen: Meinen selbstgeschriebenen Code
eines Bubble Sort Algorithmus.
Wie würde man diesen Code auf mögliche Fehler testen? Bis jetzt habe ich
für meinen "target" alle im Array arr[] vorhandenen Werte getestet,
sowie Werte auserhalb (z.B. target = 11). Ich habe aber z.B. nicht den
Fall angehandelt, wenn "target" negativ ist, z.B. target = -1. Dann
funktioniert das Programm nicht mehr so, wie es soll.
Danke und Gruß,
Gerd schrieb:> Lasst uns dazu ein Beispiel nehmen: Meinen selbstgeschriebenen Code> eines Bubble Sort Algorithmus.> Wie würde man diesen Code auf mögliche Fehler testen?
Sorry, aber was hat der Code mit Bubblesort zu tun?
Und nein, Werte zu ändern und neu kompilieren ist kein testen sondern
maximal entwickeln, debuggen, verzweifeln.
Zum testen muss die Funktionalität als Funktion separiert werden (und
zuvor verstanden sein).
Dann ruft man die Funktion mit bekannten Datensets auf und prüft (im
Programm) das Ergebnis. 3 Möglichkeiten der Prüfung (bzw die
Referenzlösung zu finden):
1 per hand (Taschenrechner, einfache Beispiele, die offensichtlich sind,
...)
2 bisherige (funktionierende) Implementierung (wenn etwas neu, besser,
schneller gemacht wird).
3 Erzeugung "auf anderem, einfacherem Weg". Beispiel: um
Primzahlzerlegung zu testen, kann ich Testzahlen durch Multiplikation
zufällig erzeugter Lösungsmengen erzeugen.
Was ich dir auf jeden Fall aus Erfahrung sagen kann ist, dass
seitenlange Diskussionen in Internetforen über an den Haren
herbeigezogene Beispiele fast immer komplett sinnlos sind.
Die fehlende Einordnung des Problems in einen Kontext stachelt jeden
dazu an, das Problem in seinen eigenen Alltagskontext einzuordnen und
entsprechend zu betrachten, und entsprechend hast du dann 20 Leute, die
sich verbissen darüber streiten, ob man die Klasse Auto mit 4 Rädern und
1 Lenkrad lieber als 2-Zeilen-Perlskript oder als 40kloc Rust-Framework
mit 3000 Unit-Tests implementiert.
Gerd schrieb:> Gibt es diesbezüglich eine Art best practices (auch wie tiefgründig die> Teilprobleme getestet werden sollten), die ihr empfehlen könnt?
Es gibt sehr viele "best practice" Ansätze. Welcher dann geeignet ist
hängt von der eigenen Struktur und der Aufgabe ab.
Ein "kreativer Wirrkopf" braucht eher strukturelles und ein
schematischer Fleißarbeiter eher Techniken wie Brainstorm.
>> Wie lernt man, sich in diese Art von Problemunterteilung> hineinzuversetzen?
In dem man es macht. Eine Aufgabe stellen, in Funktionen zerlegen,
grober Pseudocode, dann coden wäre z.B. ein Ansatz.
Viele Techniken entwickeln sich. Entwürfe mach ich z.B. in der Bahn und
schreib Sie in mein Notizbuch. Damit bin ich beim proggen min. doppelt
so schnell wie nur vorm Rechner.
Beschäftige Dich mit C++!
Du kannst damit wie in C proggen, also ohne
Vererbung, aber mit Klassen und mit Templates.
C++ bietet Dir Klassen, die deine Bausteine werden,
und Template (Vorlagen) - Funktionen, die
für alle Typen nur einmal geschrieben werden müssen.
Jeder Baustein kann dann seperat geschrieben, geändert
und getestet werden,
sowie dann in die Bibliothek aufgenommen werden.
In Deinen Programmen werden diese Bausteine dann
aufgerufen und haben dann "keine Fehler" mehr.
Wozu zum Beispiel ne Dateiverwaltung in jedem Programm
neu proggen, wenn man ne getestete als Baugruppe in der
Bibliothek hat?
mfg
A. S. schrieb:> Sorry, aber was hat der Code mit Bubblesort zu tun?>> Und nein, Werte zu ändern und neu kompilieren ist kein testen sondern> maximal entwickeln, debuggen, verzweifeln.
Das hat nichts mit bubble sort zu tun, und sollte auch eigentlich binary
search heißen. Gut gesehen :)
Danke für deine Rückmeldung. Dann weiß ich schon mal, dass mein Ansatz
nicht ausreichend ist. Aber genau da muss man dann ansetzen (auch, um
sich selbst weiterzuentwickeln ;) )
Sven B. schrieb:> Was ich dir auf jeden Fall aus Erfahrung sagen kann ist, dass> seitenlange Diskussionen in Internetforen über an den Haren> herbeigezogene Beispiele fast immer komplett sinnlos sind.
Mag sein. Aber wenn sich 10 Leute melden und am Ende 12 veschiedene
Meinungen dabei herauskommen, habe ich im Endeffekt 12 mögliche Ansätze
gewonnen. Wenn einer von den Ansätzen mit mir harmoniert, dann ist alles
genau richtig gelaufen.
Die Kernessenz meines Anliegens:
Wie entwickle ich Code, der funktioniert UND robust ist. Und wie teste
ich (ausreichend), um am Ende zu dem Entschluss zu kommen, dass der Code
funktioniert UND robust ist.
Da muss es doch einen strategischen Ansatz geben, und Erfahrung alleine
kann nicht ausreichend sein.
Test cases? Unit tests?
Danke und Gruß,
Lolita schrieb:> Beschäftige Dich mit C++!
Ich bin in der Hardwareentwicklung und möchte gerne hardwarenah
programmieren.
Zu einem späteren Zeitpunkt kommt ggf. eine andere Programmiersprache
als Zusatz in Erwägung :)
Gruß,
> Wie entwickle ich Code, der funktioniert UND robust ist.> Da muss es doch einen strategischen Ansatz geben
Ja klar. Die Generation James Watt, Thomas Edison, Werner von Siemens...
hat eine Lösung gefunden. Arbeitsteilung.
Kreative Wirrköpfe entwickeln neue Ideen. Hauptsächlich Rohrkrepierer.
Ab und zu etwas weltbewegendes. Hartnäckige Ingenieure arbeiten die
Details aus. Bürokraten überprüfen, ob alle formalen Qualitätskriterien
eingehalten werden. Prüfingenieure suchen nach unbekannten
Fehlerquellen. Und der größte Teil der Menschen macht stupide Routine.
Aber das wollen wir doch nicht! In der Softwareentwicklung sammeln sich
Menschen, die diese Art von Arbeitsteilung nicht mitmachen wollen.
Softwareentwicklung ist das letzte mittelalterliche Handwerk. Kreative
Wirrköpfe übernehmen auch die Tätigkeiten, für die es Bürokraten oder
stupide Fließbandarbeiter bracht.
Wenn wir das Problem lösen, darfst du gar nicht mehr darüber nachdenken,
wie du ein Problem in Teilprobleme zerlegst. Wie ein Bauingenieur
bekommst du ein Teilproblem und eine Haufen Vorschriften vorgesetzt.
Musst das umsetzen, was andere vor dir ausdiskutiert haben.
Gerd meinte:
> Lolita schrieb:> Beschäftige Dich mit C++!> Ich bin in der Hardwareentwicklung und möchte gerne hardwarenah> programmieren.
Kein Problem! auch mit C++ kannst du Bitfriemelei
machen! Das mach ich ja auch!
Kannst Du C++, hast Du C gleich mit gelernt.
Kannst Du C ist der Sprung auf C++ schwer. ;-((
und wenn du in C++ die Klassen wegläßt, ists wie C.
Ich übe auf dem Pc, da ist der servise mit Debugging u.s.w.
besser.
Und C++ erzieht Dich zu mehr Korrektheit beim programmieren,
weils typsicherer ist. Das kommt Dir dann bei C zu gute!
mfg
Popcorn- Lolita
Lolita schrieb:> Kannst Du C++, hast Du C gleich mit gelernt.
Das ist ein Gerücht. Wenn du Desktop-Anwendungen in C++ programmiert
hast, und dir dann den Linux-Kernel anschaust (der in C geschrieben ist)
wirst du merken, dass du von der Sprache eigentlich kaum Ahnung hast.
Denn die Sprache ist ja nicht "um die Bedingung vom if kommen zwei
Klammern" sondern "wie löse ich $problem typischerweise".
Sven meinte:
> Wenn du Desktop-Anwendungen in C++ programmiert> hast, und dir dann den Linux-Kernel anschaust (der in C geschrieben ist)> wirst du merken, dass du von der Sprache eigentlich kaum Ahnung hast.
nicht schlecht.
Du hast recht, aber mal ein Vergleich:
trotzdem kann ich C. Ich bin denn aber ein kleiner Zeitungsschmierer
und nicht ein Crack wie unsere besten Schriftsteller.
Der Zeitungsschmierer spricht das Selbe Deusch oder English
wie der Schriftsteller, dieser kann sich dann aber besser
ausdrücken. ;--))
Die Freaks, die Linux geschrieben haben sind die Schriftsteller,
ich bin der Zeitungsschmierer, wir beide können C,
trotzdem man eigendlich immer zuende lernt.
mfg
Sven B. schrieb:> Lolita schrieb:>> Kannst Du C++, hast Du C gleich mit gelernt.>> Das ist ein Gerücht. Wenn du Desktop-Anwendungen in C++ programmiert> hast, und dir dann den Linux-Kernel anschaust (der in C geschrieben ist)> wirst du merken, dass du von der Sprache eigentlich kaum Ahnung hast.
Kannst du das näher erlätern? Warum hat man, wenn man von C++ kommt,
keine Ahnung von C? Das was du erwähnst (du implizierst es ja selbst)
ist eher eine Sache von "Desktopanwendung" und "Kernel".
Vn N. schrieb:> Warum hat man, wenn man von C++ kommt, keine Ahnung von C?
In C hantiert man routinemäßig mit Sprachkonstrukten, für die man in C++
nur schiefe Seitenblicke und Kopfschütteln ernten würde.
Vn,n meinte:
> Kannst du das näher erlätern? Warum hat man, wenn man von C++ kommt,> keine Ahnung von C? Das was du erwähnst (du implizierst es ja selbst)> ist eher eine Sache von "Desktopanwendung" und "Kernel".
So hab ich das nicht gemeint.
Man kann auch in C++ nen Kernel, bare Metal, auf nem Mikrorechner
implementieren. Das geht zB. mit der IAR Workbench.
"C" war nur zuerst da, und schon länger im Geschäft.
C und Cpp sind Computersprachen. Mit ihnen kannn man viel machen,
zum Beispiel Märchen erzählen.
So wie es Linus Thorwalds gemacht hat. Ein Märchen
von Freiheit und Zukunft.
Ja. Linus ist einer der Größten Märchenerzähler der Menschheit.
Andere haben seine Märchen wahrgemacht. Sie haben zum Beispiel
mir ein Gehör gegeben.
C++ ist für große Projekte das Ding. Mann kann seine Bausteine
entwickeln,
testen und dann in seinen Programmen verwenden.
Das kann man natürlich mit C auch, aber C++ "schreit" richtig nach der
Anewendung der Methode.
Wenn man ne Weile in C++ geproggt hat, merkt man, das der Quelltext
durch die Klassen, die ja auch nur C- Strukturen sind, die aber
Funktonen
enthalten können besser lesbar, wartbar und wieder verwendbar sind.
Das geht auch in C , erfordert aber mehr Disziplin, den der Anfäner
noch nicht mitbringt.
Wenn man dies aber einmal gelernt hat, wird man dieses Paradigma
auch in c anwenden.
mfg
Vn N. schrieb:> Kannst du das näher erlätern? Warum hat man, wenn man von C++ kommt,> keine Ahnung von C? Das was du erwähnst (du implizierst es ja selbst)> ist eher eine Sache von "Desktopanwendung" und "Kernel".
Weil man Probleme in C++ einfach anders löst als in C, und weil "Ahnung
von einer Sprache" nicht bedeutet, dass man die Syntax perfekt
beherrscht, sondern dass man Erfahrung im praktischen Umgang mit der
Sprache hat. Sprich, praktische Erfahrung darin, wie man typische
Aufgaben löst.
Und da gibt es in C eben einen Haufen typischer Pattern, die in C++
deplaziert sind, und die nie verwendet werden, wie z.B.
- Fehlerbehandlung und Ressourcenverwaltung mit goto
- über char-Pointer iterieren um nullterminierte Strings zu handhaben
- der CONTAINER_OF-Trick, den man auch oft sieht
- das ganze "negative Integer-Rückgabewerte zur
Fehlerbehandlung"-Konzept
- Linked Lists überall
- das "void* user_data"-Pattern
- Utility-Funktionen oder Makros, die Algorithmen für spezifische Typen
implementieren (for_each_foo, etc)
- etc etc
Natürlich macht nicht jedes C-Programm all diese Dinge oder kein
C++-Programm irgendeins davon. Es ergibt aber einen völlig anderen
Programmierstil und auch ein völlig anderes Denkmodell als C++.
Ich finde modernes C++ mit Python noch näher verwandt als mit C.
Sven meinte:
> Weil man Probleme in C++ einfach anders löst als in C, und weil "Ahnung> von einer Sprache" nicht bedeutet, dass man die Syntax perfekt> beherrscht, sondern dass man Erfahrung im praktischen Umgang mit der> Sprache hat. Sprich, praktische Erfahrung darin, wie man typische> Aufgaben löst.
Genau. "Märchenerzählen" muß man können.
Ein Gefühl für die Sache haben, dann
kann man auch in Brainfuck coden, ;---))
> Ich finde modernes C++ mit Python noch näher verwandt als mit C.
nicht schlecht.
Darauf wäre ich allein nicht gekommen. ;-O
@Sven, was machst du eigendlich beruflich?
mfg
Lolita schrieb:> C++ ist für große Projekte das Ding.
Die Trennung läuft nicht zwischen groß/klein, alt/neu oder
einfach/komplex. Die Trennung ist zwischen virtuell und konkret oder
Steuerung und Edv. Eine SW die Dokumente, Daten, Grafikobjekte etc.
bearbeitet, hat viele abstrakte Operationen, an denen Templates,
Objekte, Vererbung, Überladung etc. super sind.
Konkrete Objekte, die Räder eines Autos etwa, mit konkreter
Funktionalität (ABS, ESP) profitieren viel weniger davon, als man
angesichts der vielen Lehrbeispiele glaubt.
In C++ ist die größte gefahr der Kontrollfluss-Verlust.
Lolita schrieb:> Wenn man ne Weile in C++ geproggt hat, merkt man, das der Quelltext> durch die Klassen, die ja auch nur C- Strukturen sind, die aber> Funktonen> enthalten können besser lesbar, wartbar und wieder verwendbar sind.
Das ist eher eine Frage von Erfahrung. Man sollte auch in C Strukturen
und deren Methoden beisammen halten.
Lolita schrieb:> Lerne C++.
Ja, das wäre auch mein Ratschlag. Leider gibt es für kleine Projekte mit
starken Ressourcenbeschränkungen viele C++-Konstrukte, auf die man
besser verzichten sollte, als Beispiele seien hier dynamische
Speicherallokation (inklusive STL), Exceptions, virtuelle Methoden und
so weiter genannt.
> Wenn du unbedingt in C proggen mußt:> Programmiere in Bausteinen.> Jedes Teilproblem kommt separat in eine C- Datei.
Von Elecia White gibt es da ein wunderbares Buch mit dem Titel "Making
Embedded Systems", das (auch) diese Dinge sehr schön erklärt.
Ansonsten haben hier viele Leute dem TO ihre Ratschläge gegeben, und mit
Ausnahme von "Noch eine Meinung" waren die meisten auch sinnvoll und
gut. Allerdings ist mein Eindruck, daß sich mit den jeweiligen
Ratschlägen nur jeweils bestimmte Kategorien von Problemen lösen lassen,
während anderere Probleme besser anders angegangen werden. Es ist eben
ein Unterschied, ob man ein (relativ) überschaubares Programm für einen
Mikrocontroller, oder eine fette Verarbeitungspipeline auf einem
verteilten Cluster entwickelt.
Deswegen erscheinen mir die Tipps von "foobar" besonders gut, und zwar
zunächst auf die zu verarbeitenden Daten und Datenstrukturen zu schauen,
dadurch ergibt sich sehr häufig schon eine sehr gute Idee, was einzelne
Programmteile tun müssen und im Umkehrschluß eben auch, in welche Teile
dieses Programm also letztlich aufgeteilt ist, und wie die dann am Ende
zusammengesetzt werden sollten.
Und dann heißt es, da bin ich ebenfalls ganz bei "foobar": üben, üben,
und nochmals üben. Mit der Zeit und der wachsenden Erfahrung kommt dann
schon eine meistens (nicht immer ;-)) recht gute Einschätzung, wie man
welches Problem am effizientesten angehen kann. Eine besser
strukturierte, vulgo: objektorientierte Technik kann dabei oft sehr
hilfreich sein, sogar, wenn die verwendete Sprache nicht streng oder
sogar gar nicht objektorientiert ist -- aber da sind wir wieder bei der
Konzentration auf die Daten und die Datenstrukturen... ;-)
Egon D. schrieb:> Schrittweise vorgehen: Ehe man ein Problem LÖSEN> kann, sollte man es erstmal klar FORMULIEREN.> Dafür ist es i.d.R. hilfreich, die vorläufigen> Formulierungsversuche NIEDERZUSCHREIBEN und> schrittweise zu verbessern.
Das ist die alte Vorgehensweise mit Lasten- und Pflichtenheft, die aber
nur bei Problemen mit einer eher überschaubaren Komplexität
funktioniert. Bei jedem größeren Problem wird man damit scheitern, denn
dabei entwickeln sich während der Implementierungszeit meist neue
Probleme, die bei der initialen Planung nicht berücksichtigt wurden.
Es gibt Gründe dafür, warum dieser Ansatz heute zunehmend hinterfragt
und durch andere Ansätze abgelöst oder zumindest ergänzt wird. Der
Hauptgrund ist wohl, daß sich im Laufe der Jahre und mit zunehmender
Komplexität der Probleme gezeigt hat, daß die meisen (je nach Quelle 60
bis 80 %) aller Entwicklungen am Ende gescheitert sind, indem sie die
Zeit- und / oder Budgetannahmen und -Vorgaben gesprengt haben...
Deswegen haben sich viele mehr oder weniger kluge Menschen Gedanken
gemacht und neue Entwicklungsmodelle ausgedacht: Agile, XP, SCRUM, und
so weiter. Und richtig angewendet funktionieren diese Ansätze in der
Praxis besser als ihr Ruf, auch wenn viele alte Entwickler darüber
schimpfen -- Widerstand gegen Veränderungen ist ja auch ein menschlicher
Instinkt... ;-)
Gerd schrieb:> Ich werde mich - auch um meine Programmierkenntnisse zu erweitern - mit> dem Buch "Introduction to Algorithms von Cormen" einlesen, und die dort> beschriebenen Probleme in aller Ruhe durcharbeiten. Ich glaube, das> hilft mir, Probleme von Gedanken auf ein Blatt Papier niederzuschreiben> und dann anschließend in C zu programmieren.
Algorithmen sind sicherlich ein guter Ansatz, aber ich möchte Dir
dennoch zusätzlich empfehlen, danach ein gutes Buch zum Thema "Design
Patterns" zu lesen und dann einige Artikel im Netz zum Thema
"Antipatterns"...
Design Patterns, oder zu deutsch Entwurfsmuster, beschreiben
grundsätzliche Ansätze, um bestimmte Probleme zu lösen. Viele davon sind
gut, manche bergen aber Risiken, die man für eine qualifizierte
Entscheidung kennen muß.
Sheeva P. schrieb:> Algorithmen sind sicherlich ein guter Ansatz, aber ich möchte Dir> dennoch zusätzlich empfehlen, danach ein gutes Buch zum Thema "Design> Patterns" zu lesen und dann einige Artikel im Netz zum Thema> "Antipatterns"...
Vielen Dank, Sheeva.
Der Grund, mich zunächst mit Algorithmen zu beschäftigen, ist, dass der
Code in den meisten Fällen relativ kurz und überschaubar ist, und ich
"logisch" denken kann - und einfach um das Programmieren zu üben:
Problem -> Gedanken -> Papir&Bleistift -> Code.
Da wären Design Patterns vielleicht zu viel des Guten (es sei denn, ich
wiederverwerte die Algorithmen in größere Programme)?
Danach kämen dann Projekte, die komplexer sind. Und danach eine
objektorientierte Sprache, bis jetzt vorzugsweise C#.
Der Weg ist lang, aber das Interesse ist da :)
Logisches Denken alleine genügt nicht. Willst du die Qualität erreichen,
die im Maschinenbau üblich ist, braucht es Menschen mit
unterschiedlichen Fähigkeiten.
Hinter den Design Pattern stecken tausende von Versuchen. Menschen wie
Du hatten es ausprobiert - einiges hat funktioniert, der größte Teil
wird unter Erfahrung und Antipattern verbucht. Du brauchst einen
starrsinnigen Bürokraten, der Anhand von rein formalen Kritärien
überprüft, ob du ein Antipattern wiederentdeckst. Und einen
Prüfingenieur, der testet, ob du etwas Unbekanntes übersiehst.
Wir drehen uns seit 50 Jahren im Kreis.
Agile Programmierung - da waren wir vor 50 Jahren schon mal weiter. Das
Smalltalk war agiler alles, was heute als agil gehypt wird. Die
Entwicklungsumgebung blieb im Anwendungsprogramm enthalten. Während der
Arbeit den Klassenbrowser aufrufen. Methode ändern und weiter arbeiten.
https://www.youtube.com/watch?v=9H79_kKzmFs&t=11m40s
Der Grund, warum die Opas die Agile Programmierung ablehnen, ist nicht
weil sie nicht neues mehr lernen wollen. Der Grund ist, die haben es
schon mal ausprobiert und erkannt, es funktioniert genau so schlecht wie
das Wasserfallmodell.
Wir müssen dickere Bretter bohren.
Ich würde nicht so viele Bücher lesen sondern einfach mal was machen.
Die Bücher kannst du später noch lesen. Mit einem Erfahrungsschatz von
700 geschriebenen Codezeilen ein Buch über Design Patterns zu lesen ist
Quatsch.
Ich finde auch dass die meisten Design Patterns Ähnlichkeit mit den
meisten Patenten haben: die sind nicht wahnsinnig clever oder schwer zu
entdecken. Wenn man das Problem hat, zu dessen Lösung der Pattern taugt,
kommt man meist selber drauf. Es kann dann instruktiv sein, so ein Buch
durchzublättern, in welchem man die typischen Muster mal schön
tabelliert findet, aber mehr irgendwie auch nicht.
Heiko L. schrieb:> Vn N. schrieb:>> Warum hat man, wenn man von C++ kommt, keine Ahnung von C?>> In C hantiert man routinemäßig mit Sprachkonstrukten, für die man in C++> nur schiefe Seitenblicke und Kopfschütteln ernten würde.
Die da wären?
Sven B. schrieb:> Und da gibt es in C eben einen Haufen typischer Pattern, die in C++> deplaziert sind, und die nie verwendet werden, wie z.B.> - Fehlerbehandlung und Ressourcenverwaltung mit goto
Ist auch in C deplatziert. Wer macht bitte Fehlerbehandlung mit goto?
Also außer Bastlern und Leuten, die in den Neunzigern hängengeblieben
sind. Funktionen und Kontrollstrukturen gibts auch in C, kein Bedarf für
goto.
Sven B. schrieb:> über char-Pointer iterieren um nullterminierte Strings zu handhaben
Äh, ja eh. Weil über ein Array iterieren ist ja so kompliziert.
Sven B. schrieb:> der CONTAINER_OF-Trick, den man auch oft sieht
Achso, C++-Programmierer sollen also nicht fähig sein, ein simpelstes
Makro zu verstehen?
Sven B. schrieb:> das ganze "negative Integer-Rückgabewerte zur> Fehlerbehandlung"-Konzept
Ja, irrsinnig kompliziert zu kapieren. Wie soll ein C++-Entwickler nur
ein "return -1" verstehen?
Sven B. schrieb:> Linked Lists überall
Und wo siehst du das Problem?
Sven B. schrieb:> Ich finde modernes C++ mit Python noch näher verwandt als mit C.
Mich würde ja echt mal deine Qualifikation interessieren.
Lolita schrieb:> @Sven, was machst du eigendlich beruflich?
Vermutlich Sportlehrer oder so. Also, nix gegen Sportlehrer.
Sheeva P. schrieb:> Leider gibt es für kleine Projekte mit> starken Ressourcenbeschränkungen viele C++-Konstrukte, auf die man> besser verzichten sollte, als Beispiele seien hier dynamische> Speicherallokation (inklusive STL), Exceptions, virtuelle Methoden und> so weiter genannt.
Naja, "malloc nicht verwenden" gilt auch in Bare Metal C.
Vn N. schrieb:> Sven B. schrieb:>> Und da gibt es in C eben einen Haufen typischer Pattern, die in C++>> deplaziert sind, und die nie verwendet werden, wie z.B.>> - Fehlerbehandlung und Ressourcenverwaltung mit goto>> Ist auch in C deplatziert. Wer macht bitte Fehlerbehandlung mit goto?> Also außer Bastlern und Leuten, die in den Neunzigern hängengeblieben> sind. Funktionen und Kontrollstrukturen gibts auch in C, kein Bedarf für> goto.
Ich mache mal in ein paar bekannten C-Libs zufällige C-Dateien auf:
libpng: https://github.com/glennrp/libpng/blob/libpng16/png.c
Fehlerbehandlung mit goto.
openssl: https://github.com/openssl/openssl/blob/master/ssl/bio_ssl.c
Fehlerbehandlung mit goto.
Linux:
https://github.com/torvalds/linux/blob/master/kernel/events/core.c
Fehlerbehandlung mit goto.
libusb: https://github.com/libusb/libusb/blob/master/libusb/core.c
Fehlerbehandlung mit goto.
Also man sieht, wirklich niemand macht das. Außer Bastlern, natürlich.
> Sven B. schrieb:>> über char-Pointer iterieren um nullterminierte Strings zu handhaben>> Äh, ja eh. Weil über ein Array iterieren ist ja so kompliziert.
Das habe ich nicht gesagt. Ich habe gesagt, es ist in C++ nicht die
typische Lösung für das Problem.
> Sven B. schrieb:>> der CONTAINER_OF-Trick, den man auch oft sieht>> Achso, C++-Programmierer sollen also nicht fähig sein, ein simpelstes> Makro zu verstehen?
Es geht nicht darum, dass du nicht in der Lage bist es zu verstehen. Es
geht drum dass du es nicht kennst. Deshalb musst du a) nachlesen was es
tut, wenn es dir begegnet, und kommst b) nicht dirket auf die Idee es
selbst zu benutzen.
> Sven B. schrieb:>> das ganze "negative Integer-Rückgabewerte zur>> Fehlerbehandlung"-Konzept>> Ja, irrsinnig kompliziert zu kapieren. Wie soll ein C++-Entwickler nur> ein "return -1" verstehen?
Auch hier: es geht nicht drum, den Code zu verstehen. Es geht drum, den
Code zu schreiben. Und in C++ macht man das idR anders.
>> Sven B. schrieb:>> Linked Lists überall>> Und wo siehst du das Problem?
Dass in C++ kaum Linked Lists verwendet werden. Das ist einfach anderer
Stil.
> Sven B. schrieb:>> Ich finde modernes C++ mit Python noch näher verwandt als mit C.>> Mich würde ja echt mal deine Qualifikation interessieren.
Gut, dass wir hier so eine faktenbasierte, erwachsene Diskussion führen,
bei der es auf Argumente ankommt!
Egon D. schrieb:> Es ist> viel wichtiger, erstmal die Dinge ÜBERHAUPT IRGENDWIE> zum Funktionieren zu bringen, die dringend erforderlich> sind, aber bisher von allen übersehen wurden.
Meine Erfahrung ist, sowas führt regelmäßig in eine Sackgasse. Wenn man
Code schnell mal eben so hinschludert, hat man viele undurchdringliche
Copy&Paste Monster. Sobald Änderungen nötig sind, sucht man sich nen
Wolf, d.h. der Code ist unwartbar. Und bei Fehlern werden dann planlos
irgendwelche Timeouts hochgesetzt. Die Fehler werden dadurch nur
seltener, gehen aber nicht weg.
Ich bevorzuge daher die Bottom-Up-Methode, d.h. ich entwickele kleine
leicht zu durchschauende Module, teste sie und baue dann daraus immer
größere Module, bis das Programm fertig ist. Der Vorteil ist, man hat ja
die Testroutinen schon geschrieben, kann also jederzeit an den Modulen
Änderungen vornehmen und testen. Die Testroutinen also nicht
wegschmeißen, sondern als Testprojekte aufheben.
Auch hasse ich Codedopplungen wie die Pest. Werden ähnliche Sequenzen
mehrfach benötigt, dann als Unterfunktion oder Schleife ausführen. So
muß man Änderungen immer nur an einer einzigen Stelle vornehmen. Im
Notfall schreibt man solche Sequenzen als Macro, d.h. der Präprozessor
nimmt dann die Codedopplungen vor. Der ist da nämlich besser als der
Mensch, d.h. er vergißt keine Stelle zu ändern.
Was man leider oft sieht, sind Batterien von sscanf oder strcmp über den
ganzen Code verteilt. Ehe man da rauskriegt, welche Kommandos
unterstützt werden, vergeht ne Ewigkeit. Dopplungen und Schreibfehler
passieren dann auch gerne.
Ich benutze daher immer nur ein scanf bzw. ein printf, die sich die
Liste der Kommandos und Antworten aus einem Array holen. So bleibt auch
die Syntax einheitlich.
Auch sollte man immer daran denken, daß jede Operation Zeit kostet und
manche richtig viel Zeit. Man sollte daher immer überlegen, wie oft
etwas wirklich ausgeführt werden muß.
Z.B. ist es äußerst unklug, in einem Timerinterrupt für eine
Multiplexanzeige, die Zahl immer von neuem in Digits zu zerlegen und
nach 7-Segment zu wandeln. Das muß man nur einmal tun, wenn der neue
Meßwert vorliegt. Der Mensch kann eh nur 2..5 Meßwerte je Sekunde
streßfrei ablesen.
Auch sollte man harwareabhängige IO-Zugriffe in separate Unterfunktionen
auslagern. Soviel wie mögliche Unterfunktionen sollten harwareunabhängig
sein.
Peter D. schrieb:> Ich bevorzuge daher die Bottom-Up-Methode, d.h. ich entwickele kleine> leicht zu durchschauende Module, teste sie und baue dann daraus immer> größere Module, bis das Programm fertig ist. Der Vorteil ist, man hat ja> die Testroutinen schon geschrieben, kann also jederzeit an den Modulen> Änderungen vornehmen und testen. Die Testroutinen also nicht> wegschmeißen, sondern als Testprojekte aufheben.
Hi Peter
ich stimme deinem kompletten Beitrag zu, möchte aber speziell auf den
zitierten Teil eine Frage stellen:
Genau diese Strategie Bottom-Up möchte ich verinnerlichen und festigen.
Meine Frage zielt aufs Testen aus. Wie testest du die einzelnen Module?
Wäre das mittels Unit tests oder welche Strategie wendest du da an?
Danke und Gruß,
Peter D. schrieb:> Egon D. schrieb:>> Es ist viel wichtiger, erstmal die Dinge ÜBERHAUPT>> IRGENDWIE zum Funktionieren zu bringen, die dringend>> erforderlich sind, aber bisher von allen übersehen>> wurden.>> Meine Erfahrung ist, sowas führt regelmäßig in eine> Sackgasse.
Mag sein; vielleicht bin ich die große Ausnahme.
Ich habe auch nur eine persönliche Erfahrung mitgeteilt.
Soweit ich das einschätzen kann, bin ich relativ
akribisch und perfektionistisch. Ich habe es noch nie
erlebt, dass ein Projekt daran gescheitert ist, dass
die Qualität einer KOMPONENTE nicht ausreichend gewesen
wäre -- wohl aber, dass Projekte komplett stecken-
geblieben sind, weil ganze Problemfelder komplett
übersehen wurden.
Das hängt aber stark von den Beteiligten und ihren
persönlichen Stärken und Schwächen ab, das gebe ich
zu.
> Ich bevorzuge daher die Bottom-Up-Methode,
Ich auch.
Mir fiel nur auf, dass der TO davon redet, ein Problem
systematisch in Teilprobleme zu zerlegen und Code zu
schreiben.
Häh?
Fehlt da nicht irgendwas? Ist es inzwischen auch schon
total veraltet, Probleme zu LÖSEN, bevor man die
gefundene Lösung in Form eines Programmes formuliert?
Egon D. schrieb:> Mir fiel nur auf, dass der TO davon redet, ein Problem> systematisch in Teilprobleme zu zerlegen und Code zu> schreiben.>> Häh?>> Fehlt da nicht irgendwas? Ist es inzwischen auch schon> total veraltet, Probleme zu LÖSEN, bevor man die> gefundene Lösung in Form eines Programmes formuliert?
Ach komm... Da hast du mich total missverstanden.
Wenn du zum allerersten Mal einen Sortieralgorithmus in Code
niederschreiben sollst, formulierst du das Problem und den Algorithmus
nicht erstmal in eine Art Pseudocode auf ein Blatt Papier bevor du
überhaupt anfängst Code niederzuschreiben?
Ist aber auch egal. Ich bezweifle, dass dieser Thread noch wesentlich an
Qualität zu unterbieten ist. :)
Gruß,
Was auch nicht schaden kann: OOP lernen. Auch wenn C nicht OO ist, man
lernt trotzdem Denkmodelle und Strategien kennen, die hilfreich sind und
den Horizont erweitern.
Peter D. schrieb:> Egon D. schrieb:>> Es ist>> viel wichtiger, erstmal die Dinge ÜBERHAUPT IRGENDWIE>> zum Funktionieren zu bringen, die dringend erforderlich>> sind, aber bisher von allen übersehen wurden.>> Meine Erfahrung ist, sowas führt regelmäßig in eine Sackgasse. Wenn man> Code schnell mal eben so hinschludert, hat man viele undurchdringliche> Copy&Paste Monster. Sobald Änderungen nötig sind, sucht man sich nen> Wolf, d.h. der Code ist unwartbar. Und bei Fehlern werden dann planlos> irgendwelche Timeouts hochgesetzt. Die Fehler werden dadurch nur> seltener, gehen aber nicht weg.
Peter, Du bist sehr erfahren und machst vermutlich viele Dinge die Du in
der ein oder anderen Form schon kennst.
Der TE ist ein vollkommener Anfänger. Und da ist es m.E. wirklich
ratsam, auch diesen Weg (laufen, irgendwie) zu verfolgen, um überhaupt
die Dinge zu verstehen, von denen "die anderen" immer reden.
Das man einen solch einen Code nicht behalten sollte ist klar. So wie
die ersten Bilder, die ersten Holzarbeiten oder der erste Windvogel
nicht Basis für die nächsten sein sollten.
Ein absolut geiler Thread!!
Macht Euch bitte nicht andauernd gegenseitig schlecht.
"Dumme" finden das Board hier nicht und diesen Thread
erst recht nicht! ;--P
@Gerd:
Wenn du schon fortgeschritten bist, solltest Du das Buch
"C im 21. Jahrhundert" von Ben Klemens lesen.
Da sind gute Techniken drinne, die es erlauben auch
mit den Möglichkeiten von C an "Micro Python"( C++ ) ;--P
heranzukommen.
Zum Beispiel die Benutzung von asprintf(), was dann die
ganze "Zeigerei" bei der Stringbearbeitung sehr vermindert.
Ich hätte auch ein Proggy für Dich ( Easycode ), was professionell
aus "C" Nassi-Schneidermann-Diagramme macht und umgekehrt.
Leider bin ich noch in Quarantäne, und wie lang dies dauert weiß
zur Zeit keiner.
Aber, wenn du dich anmeldest, kannst Du mal oben auf "Lolita"
klicken... ;--P
mfg
Hi,
du liest jetzt den Cormen und beschäftigst dich mit Algorithmen.
Ein guter Anfang.
Dazu passt gut testgetriebene Entwicklung
https://de.wikipedia.org/wiki/Testgetriebene_Entwicklung.
Dazu kann man jetzt ein Framework benutzen (gTest oder cpputest,
alle C++ Frameworks gehen auch für C)
oder selbst ein kleines Gerüst bauen. Das ist auch eine gute Übung.
Z.B. für einen Sortieralgorithmus kommen die zu sortierenden Daten
in eine externe Datei. Ebenso die Lösungen, also die sortierten Daten.
Deine Testroutinen lesen die Daten, sortieren sie mit deinen
Sortieralgorithmen und vergleichen das Ergebnis mit den Lösungen.
Vorteil dabei ist
- Testdaten / Lösungen erzeugen ist mehr Benutzersicht
- du musst den Sortieralogrithmus verwenden. Dabei fällt auf ob das
Interface was taugt oder es total kompliziert und unbenutzbar ist.
etc.
Stichworte für weitere Schritte: Clean Code, Refaktorierung, Design
Pattern, Architektur, Spezifikation, Kommunikation.
Nur man kann nicht alles gleichzeitig machen und jetzt zu allen Themen
ein Buch lesen.
Verzettel dich nicht.
Vn N. schrieb:> Sven B. schrieb:>> Und da gibt es in C eben einen Haufen typischer Pattern, die in C++>> deplaziert sind, und die nie verwendet werden, wie z.B.>> - Fehlerbehandlung und Ressourcenverwaltung mit goto>> Ist auch in C deplatziert. Wer macht bitte Fehlerbehandlung mit goto?> Also außer Bastlern und Leuten, die in den Neunzigern hängengeblieben> sind. Funktionen und Kontrollstrukturen gibts auch in C, kein Bedarf für> goto.
Das ist... äh... nur die halbe Wahrheit. In vielen Fällen ist es einfach
sinnvoll, maximal performant und korrekt, zum Beispiel tief
verschachtelte Kontrollstrukturen im Fehlerfall mit einem goto zu
verlassen. In hochoptimierten Anwendungsfällen wie Betriebssystemkerneln
(Linux, SunOS/Solaris) oder Datenbankcores (PostgreSQL) ist das manchmal
die schnellste und einfachste Möglichkeit, aus einer Fehlersituation
heraus zu kommen.
Richtig ist allerdings auch, daß goto möglichst sparsam und nur in sehr
wenigen, begründeten Ausnahmefällen genutzt -- und dann gut dokumentiert
werden sollte.
> Sheeva P. schrieb:>> Leider gibt es für kleine Projekte mit>> starken Ressourcenbeschränkungen viele C++-Konstrukte, auf die man>> besser verzichten sollte, als Beispiele seien hier dynamische>> Speicherallokation (inklusive STL), Exceptions, virtuelle Methoden und>> so weiter genannt.>> Naja, "malloc nicht verwenden" gilt auch in Bare Metal C.
Ja, natürlich. Aber zum Beispiel Exceptions, virtuelle Methoden, oder
eben auch jede Form von dynamischer Speicherallokation können auf den
ganz kleinen uCs wegen deren beschränkter Ressourcen problematisch sein
-- und trotzdem hin und wieder die beste Lösung sein...