• Guten Abend RetroTown :) Mit diesem Thread möchte ich einen Guide veröffentlichen welcher euch allen helfen soll übersichtlicheren und effizienteren Code zu schreiben. Solltet ihr ein Thema haben was euch interessiert aber hier nicht angesprochen wird, lasst es mich als Antwort wissen und ich werde ein Kapital davon aufstellen und im Inhaltsverzeichnis verlinken! Vorschläge, Verbesserungen und Ideen sind erwünscht8) Im Moment sind es noch wenige Kapitel, es ist vorgesehen dass dieser Thread mit der Zeit wächst und von der Moderation unterstützt wird. Viel Spaß beim lesen, diskutieren und Feedback abgeben:saint: Vergisst nicht, habt ihr weitere Beispiele zu einem Kapital in einer anderen Sprache dann lasst es mich via Antwort wissen. Der Thread wird wahrscheinlich mehrfach umstrukturiert.


    Inhaltsverzeichnis

    1. Copyright & Lizenzen
    2. Übersicht von Variablen
    3. Functions & Native Strings
    4. Low Level I/O & Insights
      1. Architecture
      2. Threading
      3. Socket I/O
    5. High Level Insights
      1. ~ Coming soon ~


    1. Copyright & Lizenzen

    Wenn du dich andauernd fragst wie du am besten in der neuen leeren Datei anfangen sollst, dann bist du beim Thema Copyright & Lizenz genau richtig. Stellen wir uns vor du willst einen seriösen Code veröffentlichen, z.B. um dein Können bei einer IT-Firma zu beweisen, so ist es wichtig ein Copyright zu setzen, falls du möchtest auch eine Lizenz wie BSD-2/3, Apache oder GPL anhängen. Dadurch machst du nicht nur den Eindruck dass der Code den du schreibst dein Können zeigen soll, sondern nebenbei zeigt es auch von Stil und gewisser Erfahrung in der Open-Source Welt.

    Ein einfaches Copyright setzt du eigentlich schon so wie unten angedeutet:

    CJwDM7v.png

    Falls du dich für eine Lizenz entschieden hast, sei es mal die BSD-3 Klausel Lizenz als Beispiel, fügst du sie nicht anders wie oben ein:

    4gPwITu.png

    Nachdem du dein Copyright oder gewählte Lizenz eingefügt hast, lässt du am besten 3. Zeilenbrüche aus, um die Lesbarkeit zu verbessern:saint:

    LhnH2wJ.png


    2. Übersicht von Variablen

    Deklariere Variablen am besten in den ersten Zeilen der Funktion, definiere sie niemals Kreuz und Quer, das gibt sonst ein Variablesalat :aws: Variablesalate werden die Lesbarkeit deines Codes erschweren, in einer Kooperation mit einem Team kann das negative folgen haben. Egal ob PHP, C, Javascript oder C++, vermeidet überall Code wie

    A0uMgPM.png

    Sondern deklariert, bzw. definiert die Variablen zuerst am "Kopf" der Funktion,

    T3z16FL.png

    Es ist wichtig dass die Variablen am Kopf der Funktion deklariert werden, bzw. definiert je nach Sprache, da es die Lesbarkeit unterstützt. Außerdem erhältst du auch eine bessere Übersicht über die Aufgabe der Funktion, ob es sich um eine mathematische Berechnung oder nur um eine Ver- oder Entschlüsselung einer Nachricht handelt. Man sollte zuerst alle nötigen Variablen mit deren zugehörigen Typ deklarieren, dann erst die Tasks schreiben nach dem dies getan wurde, dadurch hast du die Variablen allesamt auch auf einem Blick;)

    Salat Beispiel:

    SY7Vh09.png

    Die Instandhaltung oder Weiterentwicklung solch eines Salats ist recht schwierig, man muss überall den Array Index bearbeiten falls man z.B. $get['username'] in $get['user_name'] überarbeiten möchte. Deswegen lohnt es sich die Variablen am Kopf der Funktion zu deklarieren, siehe Beispiel drunter.

    Übersichtlich:

    NjLAa2B.png

    Wenn man nun einen Array Index überarbeiten möchte muss man die Änderung nur noch am Kopf der Funktion übernehmen:) Schon hat man sich mehrere Strg+Fs $get[index] gespart:saint:

    3. Functions & Native Strings

    Was Funktionen sind sollte dir bekannt sein. Jeder kennt sie und hat mit Sicherheit schon einige geschrieben. Bei großen Funktionen ist es wichtig sie in kleineren, sogenannten helper-functions, aufzuteilen, damit verbesserst du nicht nur die Lesbarkeit deines Codes, sondern auch die Instandhaltung. Gebe den Funktionen einen angemessenen Namen welcher z.B. die Aufgabe der Funktion beschreibt. Der Zweck hierbei ist eine art Kommunikation zwischen Leser und Code aufzubauen, Lesbarkeit ist neben Effizienz absolut wichtig. Vermeide unnötige Parameter, halte Funktionen kurz und knapp, verbanne unnötige Operationen.

    Wenn du im Low-Level Bereich programmierst, verlange immer die N bytes via Parameter! Ein klassisches C Beispiel:

    XEodBog.png

    Wer Erfahrung hat wird sehen das die Verwendung von strcat sehr gefährlich sein wird! Ein Buffer-Overflow kann hier ausgenutzt werden um Stack Informationen abzurufen oder gar den Instructionpointer zu manipulieren. Der Loop kann das Programm zu einer Termination führen lassen falls das Betriebssystem eingreift, gegeben ein Strack Protector existiert. Zwar hat der frühere Author strcat kurz und knapp gehalten, jedoch in einer negativen Art und Weise. Um sich gegen Buffer-Overflows zu schützen musst du deswegen immer auf Längen und n-bytes achten! Implementieren wir strncat kurz und knapp, a la positiven Art, aka. die sichere Variante von strcat:

    f3wk8WN.png

    Diese variante der Funktion verlangt einen dritten Parameter, welche die maximale Anzahl der zu anfügenden characters übergibt. Um den Code zu verstehen müssen wir erstmals schauen wie Strings eigentlich aufgebaut sind, dafür habe ich dir diese Grafik herausgesucht:

    arduino-string-example.png


    Was wir hier abgebildet sehen ist ein typischer nativer String mit Platz für 5 Elementen. Du als Neuling siehst hier ein in rot markiertes 6tes Element welches die Terminating Null 0x00 ('\0') beinhaltet. Doch in der Welt der Programmierung wird die 0 immer mit gezählt, bzw. es wird beim Zählen von 0 angefangen. 0, 1, 2, 3, 4, 5!

    5 Elemente also. Vielleicht fragst du dich jetzt weshalb die Terminating Null gerade bei Strings so wichtig ist... Ein Computer kann nicht wissen wann der String endet, gäbe es keine Terminating Null würden bekannte Funktionen wie strlen() immer weiter und weiter ins unendliche zählen, quasi ein while(true) loop mit continue; Um das zu verhindern gibt es die Terminating Null, welche festlegt wo der String aufhört.

    Neben 0x00 haben wir 4 weitere Elemente die als Unused markiert sind:) Wollen wir nun den buffer des Strings vollständig nutzen und "Hello" in "Hello You" abändern lassen, so brauchen wir die strncat() Funktionen die wir oben geschrieben haben. Was machen wir denn in strncat() genau?

    Wir haben eine Variable namens d, welche ein Pointer ist. Pointer zeigen auf die Adresse der Memory Location in welcher sich der Wert dieser Variable befindet. Würde d also auf Memory Location 0x1000 zeigen, hätten wir solch ein Layout mit je 1 Byte Abstand:

    dkns1zL.png

    Da wir mit strncat() einen weiteren String an das Ende des zu modifizierenden Strings anfügen wollen, müssen wir also zu Position d 0x1005, sprich *d Element 5.

    Bedeutet so viel wie, Loope durch die Werte der Adressen bis die Terminating Null erreicht wurde, das Ende des Strings:saint: Übersetzt würde dann

    Sy5pvvZ.png

    heißen, wenn der Wert von d (*d) nicht 0 ist, dann inkrementiere die Adresse (d) um 1 Byte und prüfe erneut. Falls auf das Ende des Strings getroffen wurde, wird im nächsten Loop Character für Character an das Ende des Strings angefügt und erneut auf 0 geprüft, außerdem wird n dekrementiert um einen Buffer Overflow zu vermeiden:whistling: Ich denke das nach den oberen Erklärungen die Funktion von strncat() selbst erklärend ist, versucht es!:)


    4. Low Level I/O & Insights

    I/O bedeutet Input/Output. Darunter versteht man im deutschen Eingabe und Ausgabe von Daten. Es gibt verschiedene Arten von I/O. Zu den bekanntesten zählen

    • Memory I/O
    • File I/O
    • Socket I/O
    • Port I/O
    • Piped I/O

    Zu den teuersten I/O Operationen zählen das lesen und schreiben von Dateien (Read/Writes). In der Regel hat ein Prozess kein Zugriff auf Dateien, ohne ein syscall, anfragen an den Kernel, kann kein File I/O stattfinden. Die sogenannten syscalls zählen zu den expensive calls, man muss sich also im klaren sein dass mehrfache Verwendung von syscalls zu einem Effizienzdefizit beitragen. Eine Socket Kommunikation ist ebenfalls nichts anderes als das Öffnen, Lesen, Schreiben und Schließen von Dateien auf dem Rechner. Man kann nicht sagen Unix Sockets seien besser als Windows oder POSIX Sockets, am Ende liegt es an deiner Implementierung der Kommunikation zwischen Server und Client(s) ~ Wie du solch eine Kommunikation am besten Implementierst erfährst du natürlich theoretisch, teilweise auch praktisch, in diesem Thread! Doch vorerst lass mich dir die Welt von I/O etwas genauer erläutern um dich auf ein gewissen Standpunkt zu informieren. Um einen Dienst aufzubauen, sei es Web Solutions, Streaming Service oder Social Media Plattform, musst du dir im klaren seien dass du eine riesige Verantwortung trägst gegenüber der Latenz und Qualität. Um die Latenz zu verbessern musst du Server Seitige grundlegende Entscheidungen treffen. Vergiss nicht, die Kommunikation zwischen Server und Client ist I/O, Input & Output - Neben der zur Verfügung stehenden Bandbreite, wird das Dateisystem des Servers eine entscheidende Rolle spielen, die Architektur deiner Anwendungen, alles muss geplant sein.


    4.1 Architektur

    Die Architektur und Struktur deines Server wird das Basement deines Resultats der Zufriedenheit pflastern. Du musst Planen aus welchen Komponenten du deine Server-Architektur aufbauen möchtest. Entscheide dich für das richtige Betriebssystem, empfehlen kann ich dir eine selbst angepasste Version des FreeBSD Systems. Wie du FreeBSD modifizieren und kompilieren kannst erfährst du auf deren offiziellen Seite! Neben dem Betriebssystem, welches nur ein Stück des Grundgerüsts ist, wird das Dateisystem eine entscheidende Rolle spielen, sei dir bewusst dass es mehrere davon gibt. Nennenswerte sind ZFS, EXT4 und BTRFS. Ohne das zu den Tasks ausgelegte Dateisystem wirst du früh oder später auf die ersten Mängel beim bearbeiten von Anfragen stoßen, falls du Streams anbietest wird sich das negativ auf das Buffering auslegen. Zu den besten Implementierungen von Streaming Services lohnt es sich auf ein Cache-friendly Dateisystem zu setzen. Das Internet bietet Webserver wie IIS8, NGINX und H2O an, gerne könnt ihr auch selbst einen Webserver schreiben. Das bringt den Vorteil dass man nicht auf Patchs angewiesen ist, verfügt bessere Kontrolle über Vorgängen und die Applikationen können jeder Zeit ausgebaut, verbessert und weiterentwickelt werden8) Da der Thread Programming Guide heißt nehme ich an du entscheidest dich für housemade Applications (eigenen Webserver, Content Management System, NoSQL Server, ...).

    Es ist wichtig dass man sich eine saubere Ordner Struktur anlegt, du willst sicherlich nicht alleine am Projekt arbeiten wollen und stellst deswegen wahrscheinlich ein kleines Team auf. Einigt euch auf einen Coding Style, schreibt keinen Code der sich vom Geschmack der anderen unterscheidet. Die Lesbarkeit des Projekts ist entscheidend beim weiterentwickeln, solltet ihr trotzdem in Situationen kommen in denen es verständlich nicht geht, Kommentiert die Lines ausführlich!


    Tut mir leid Leute, irgendwie gibt es kein "Temporär Speichern", deswegen Poste ich den Thread jetzt schon, er ist aber noch nicht fertig !!! :) Wenn ihr möchtet könnt ihr ja schon mal Themen die euch interessieren posten, gute Nacht wünsche Ich euch:evil::sleeping::saint:

    Sincerely,

    John F. Kennedy

    7 Mal editiert, zuletzt von Rockstar* (3. November 2018 um 18:18)

  • An und für sich ist der Guide schon umfangreich,aber es wäre eventuell wenn du einige Wörter noch genauer erläuterst.

    Wenn zum Beispiel ein Otto Normalverbraucher sich damit beschäftigen will und den Thread hier findet dann wird er zum Beispiel bei dem Satz "Jetzt wird nur noch nach n gelooped, ein Overflow kann hier ausgeschlossen werden" Fragezeichen über dem Kopf haben, da nicht deutlich zu erkennen ist was du damit genau meinst.

  • yyxxddddd Ich denke, dass dieser Guide denen helfen soll, die schon mal Erfahrung mit C++ gemacht haben und ggf. schon selber etwas kleines programmieren können.

    Siehe Update.

    Ich möchte mit diesem Thread auch Kenntnisse übermitteln welche Anfängern helfen könnten. Wie beschrieben, der Thread soll wachsen und vieles mehr (nicht nur über C, C++, PHP usw.) ansprechen!

    Ein natives Grundwissen ist nun mal ein sehr wichtiger Baustein beim Programmieren, ansonsten wird man auch im High Level versagen, bzw. versagen in Form von Optimierung, Effizienz und Infrastruktur. Ich bin nur ein Hobby Programmierer welcher seine Erfahrungen teilen möchte da Programmieren etwas sehr tolles ist. Es muss nicht bedeuten dass man den ganzen Tag Kaffee schlürft, ein Krüppel ist sondern es geht darum anderen von Bildung teilhaben zu lassen, dafür gibt es ja auch Schulen aber meiner Meinung nach entspricht das theoretische nicht dem praktischen.

    Sincerely,

    John F. Kennedy

  • Frühzeitiges Beenden da bedingungen nicht erfüllt wurden.

    Beispiel:

    register char *p1, *p2; sind uninitialisiert, genau wie *b1, *b2 und len liegen die Variablen im Stack, klar haben die eine gewisse Größe im Speicher, aber keine signifikante. Es geht um die Lesbarkeit der Funktion damit man sich einen guten Überblick beschaffen kann. Hier ist ein Beispiel wie Apple bcmp(...) implementiert hat: Source

    Nginx nutzt denselben Style um die Readability zu garantieren: Source

    Im Endeffekt kann man seine Funktionen so gestalten wie es demjenigen lieb ist, aber ich persönlich finde es halt cooler und schöner wenn man einen gewissen Style verwendet der eben auch die Readability verdeutlicht und den nächsten Entwicklern die Weiterentwicklung erleichtert. Gerade bei Open Source Projekten ist das vom Vorteil. Ob du jetzt aufhören willst zu lesen ist ja deine persönliche Sache, ist garantiert keiner wütend gegenüber deiner Meinung aber falls du fragen hast werde ich sie dir auf alle Fälle beantworten.


    @Everyone, tut mir leid dass ich den Thread nicht derzeitig verbessern kann. Es fehlt mir leider an Zeit, aber definitiv werden hier noch #Updates dazu kommen.

    Wünsche Euch viel Spaß beim programmieren.

    Sincerely,

    John F. Kennedy

  • Klar hat es in diesem Beispiel so gut wie keine Auswirkung auf die Performance, jedoch kann dir das bei größeren, performancelastigeren Projekten, einige Sekunden klauen. Ein int wahrscheinlich eher weniger, eigene Klassentypen dafür umso mehr

  • Klar hat es in diesem Beispiel so gut wie keine Auswirkung auf die Performance, jedoch kann dir das bei größeren, performancelastigeren Projekten, einige Sekunden klauen. Ein int wahrscheinlich eher weniger, eigene Klassentypen dafür umso mehr

    Nein, Sekunden wohl kaum. Du hast ja gesehen dass nginx selbst von dem Style gebrauch macht. Das sei aber mal kein Argument.

    Bei Klassen gibt es an spezielle Anwendungsmethoden, je größer die Klasse desto mehr Impact hat sie im Speicher.

    Parameterübergaben z.B., Pass by Value oder Pass by Reference.

    Pass by Reference:

    Wenn ein Parameter als Pass by Reference übergeben wird, bedeutet das dass der caller und callee ein und dieselbe Variable verwenden. Falls der callee den Wert der übergebenen Variable verändert dann ist die Veränderung global, bzw. für den caller sichtbar, bzw. die Variable des callers ist von der Modifizierung betroffen. Ob Operationen bei Passed-By-Reference Variablen atomic sind, weiß ich nicht - Soviel ich weiß ist bei Programmiersprachen keine Operation als Atomic garantiert... Deswegen, bei Pass-By-Reference sollte man bei Multithreaded Applications ein Auge auf die nebenläufigen Operationen haben, ansonsten kann es zu sehr miesen unerwarteten Kopf zerbrechenden Fehlern kommen.

    Pass by Value:

    Anders als Pass By Reference, wird ein Parameter als Pass By Value übergeben, bedeutet das dass der caller und callee zwei, von sich unabhängige, Variablen mit demselben Wert haben. Sollte der callee den Wert der übergebenen Variable verändern dann ist die Veränderung für den caller nicht sichtbar, bzw. die Variable des caller ist von der Modifizierung nicht betroffen.

    Klassen sind in den meisten Fällen sehr große Objekte, also wissen wir dass der Kopiervorgang einer Klasse als Pass By Value Parameterübergabe teuer werden kann. Nun kommen wir zum Teil der schweren Entscheidung, ist so als ob man eine Weiche stellen muss die den Zug

    1. nach links in eine 10-köpfige Menschenmenge rasen lässt oder
    2. nach rechts in eine 2 köpfige Menschenmenge.

    Je nach Szenario kann Pass by Value der linke weg sein, oder auch mal der rechte, dasselbe gilt für Pass by Reference. Bei beiden wird man bei manchen vorhaben Opfer einstecken müssen.

    Glücklicherweise gibt es aber ein empfohlener Standard, kann man wie eine Faustregel für Klassen sehen:

    • Falls Kopiervorgang zu teuer, Werte müssen aber modifiziert werden: Nicht konstante Pass by Reference Parameterübergabe
    • Falls Kopiervorgang zu teuer, Werte müssen aber gelesen werden: Konstante Pass by Reference Parameterübergabe
    • Falls Kopiervorgang billig (int, double, char, bool, etc..), Werte müssen modifiziert werden: Nicht konstante Pass by Value Parameterübergabe mit return
    • Falls Kopiervorgang billig (int, double, char, bool, etc..), Werte müssen aber gelesen werden: Konstante Pass by Value Parameterübergabe

    Kopiervorgänge bei nativen Datentypen sind in fast allen Fällen, wenn nicht allen, sehr schnell. Pass by Reference Parameterübergaben von nativen Datentypen (int, double, char, bool, etc..) sind sogar langsamer als Pass by Value Parameterübergaben von nativen Datentypen. Weshalb? Native Datentypen sind sehr klein, bis auf long long und double, welche etwas größer ausfallen. Bei Microsoft .NET kannst du dich über die Blittable types informieren, da steht auch etwas drin über die Memory Copy Operations.

    Sincerely,

    John F. Kennedy

    Einmal editiert, zuletzt von Rockstar* (19. Dezember 2018 um 14:25)

Jetzt mitmachen!

Sie haben noch kein Benutzerkonto auf unserer Seite? Registrieren Sie sich kostenlos und nehmen Sie an unserer Community teil!