• Offizieller Beitrag

    Hallo!


    Ich versuche mich ein wenig in C einzulesen, und wollte dann das Unix-C Banner Programm auf die Sage bringen und vertsehen, warum es tut, was es tut.


    Vielleicht mag mir jemand die Hauptschleife erklären? Das Programm ist zwar kurz, aber für mich nicht a priori ersichtlich.



    Vielen Dank.


    Zumindest mein GCC unter Linux kompiliert das Programm anstandslos. Auf der Sage fehlt mir noch das "String.h". Da kann ich vermutlich ein beliebiges von einem anderen System nehmen, oder?
    Gruß
    Stephan

    • Offizieller Beitrag

    Du meinst die Zeile

    Code
    for (argv++; --argc; argv++) {

    ?
    argc ist eine int Variable, die die Anzahl der Argumente in der Kommandozeile enthält.
    Mit argv kannste mal eben 2 DIN A4 Aeiten voll schreiben.


    Auf die Schnelle:
    Programmaufruf:
    banner Bla1 Test2 Murks3
    Dann wird Bla1, Test2, Murks3 in der for (...) verarbeitet. Der jeweilieg String steht in *argv.
    Was in der for(...) genau passiert, hab ich jetzt nicht geschaut, aber war auch glaube ich nicht deine Frage.


    Alle Klarheiten beseitigt?


    Eine meiner ersten Begegnung mit C war ein Listing fuer den Atari.

    Code
    if (*++*++argv == 'c') {


    Dahinter stand ein Kommentar (sinngemäß): Wer das nicht versteht, soll erstmal C lernen!


    Also weisst du was du zu tun hast. :)


    Viel Erfolg

  • Das Programm scheint die Eingabe auf der Kommandozeile einfach in gross auszugeben?


    Die äusserte Schleife a sind die Zeilen? Die Schleife b dann die Zeichen in der Zeile? Und c dann die Pixel von der Buchstabentzeile?


    Von string.h scheinst Du ja nur strlen zu brauchen? Das kannst Dir zu not ja auch schnell selbst schreiben?


    int strlen( char *c) {
    int len = 0;
    while( *c++ != 0) {
    ++len;
    }


    return len;
    }


    , oder so ähnlich.


  • Wobei der eingefleischte C-Hacker natürlich

    Code
    while(*c++)


    schreiben würde, "0" als "false" und "!=0" (ungleich null) als "true" interpretiert wird.
    Das ist wahrscheinlich auch das Problem beim Verstehen von fremdem C-Code, dass
    man in C viele Sachen sehr "kompakt" schreiben kann.
    Ein schönes Beispiel dafür ist die for-Schleife im Beispiel.
    Ein for in C hat eigentlich die Form:

    Code
    for(<Initialisierung>;<Vergleich>;<De-/Inkrement) <Anweisung>


    Im Unterschied zu anderen Sprachen, kann man aber beliebiegen Code an allen
    vier Stellen einsetzen.
    Damit wird aus "for i:= 1 to 10 do <etwas>;" wird in C "for(i=1;i<=10;i++) <etwas>", wenn man
    aber abwärts zählt kann man das in C noch anders machen
    Aus "for i:= 10 down to 1 do <etwas>;" wird in C "for(i=11;--i;) <etwas>".
    "--i" verringert i um 1 und verwendet das Ergebnis an der Stelle, die eigentlich der
    Vergleich ist. Die Schleife bricht tatsächlich bei 1 ab, da i eben wieder erst verringert
    wird und dann ausgewertet. i ist dann 0 also false und damit ist die Schleife zuende.
    Wenn man C kann, ist das ganz logisch, kommt man aber von einer "strengeren"
    Programmierschleife ist das schon etwas schwer verständlich und das ist noch ein
    einfaches Beispiel. 8-)

    Das Genie beherrscht das Chaos

  • In Variable a steht die Zeile des Banner-Buchstabenss, der aus 8 Zeilenen (0 to 7) besteht.
    In b steht die Position des gerade bearbeiteten Buchstabens aus dem Übergabewert. Dabei ist zu beachten, dass übergebene Wörter, die mehr als 10 Zeichen haben, auf 10 Zeichen gekürzt werden! Da ein Großbuchstabe 7 Zeichen breit ist, und hinter jedem Großbuchstaben ein Leerzeichen als Lücke zum nächsten Großbuchstabens eingefügt wird ("line[b * 8 + 7] = ' ';"), ergibt sich daraus, dass maximal die volle Breite der Textzeile, 80 Zeichen, mit diesen maximal 10 Buchstaben gefüllt werden. So wird ein ungewollter Zeilenumbruch innerhalb des Banners verhindert.
    Variable c ist das "Pixel" des Großbuchstabens aus der obigen "Data-Wüste", welche die Großbuchstaben enthält.
    Variabe line beinhaltet die zusammengesetzte Zeile des Großbuchstabentextes, bestehend aus "#" und " ".


    Die Hauptschleife holt nacheinander alle Übergabeparameter, z.B. alle Worte eines Satzes, in die innere Schleife. Es wird ein Wort nach dem anderen ausgegeben und wenn ein komplettes Wort ausgegeben wurde, wird eine neue Großbuchstabenzeile aufgemacht.
    In der Schleife darin wird also Zeile für Zeile der Ausgabetext in Großbuchstaben zusammengesetzt, die Hauptschleife durchläuft Zeile für Zeile die Großbuchstaben und holt sich dabei die "Pixel" (also " " oder "#") für die aktuelle Zeichenposition im Parameter-Text. Der puts(line) Befehl jeweils die komplette Zeile aus.


    Die untere Schleife mit dem Break macht nochmal was mit der zusammengesetzten Zeile bevor sie ausgegeben wird, scheinbar ersetzt sie die "#" durch "\0", was auch immer das für einen Sinn hat.

    1ST1

    • Offizieller Beitrag

    Moin,


    ich persönlich halte die Art der C Programmierung für höchst unnötig und ist eher ein auf "Dicke Hose machen".


    Bei meiner Programmierung von Steuergeräten versuche ich den Code möglichst einfach und lesbar zu halten. Lieber nutze ich dedizierte Variablen als das ich alles in eine Zeile quetsche.
    Meist ist der komplexe Code am Ende nicht wirklich schneller, im Gegenteil hängt es stark vom Compiler ab was daraus wird.


    Klar macht es Spaß solche Anweisungen zu basteln...


    Gruss,
    Peter

    • Offizieller Beitrag

    Die untere Schleife mit dem Break macht nochmal was mit der zusammengesetzten Zeile bevor sie ausgegeben wird, scheinbar ersetzt sie die "#" durch "\0", was auch immer das für einen Sinn hat.


    Es werden von hinten nach vorne alle Leerzeichen durch \0 ersetzt bis ein Zeichen ungleich Leerzeichen ist. Sonst hatte line[] kein \0-Ende, was meist sehr unschoen aussieht!

  • Bei meiner Programmierung von Steuergeräten versuche ich den Code möglichst einfach und lesbar zu halten. Lieber nutze ich dedizierte Variablen als das ich alles in eine Zeile quetsche.
    Meist ist der komplexe Code am Ende nicht wirklich schneller, im Gegenteil hängt es stark vom Compiler ab was daraus wird.


    Klar macht es Spaß solche Anweisungen zu basteln...


    Da gebe ich dir vollkommen recht - bei beidem.
    In meiner frühen Sturm und Drang Zeit des Programmieren, fand ich es cool möglichst kurzen und somit vermeindlich schnellen Code
    zu schreiben. Doch wenn man dann mal nach einiger Zeit selbst eigenen Code erst aufwändig analysieren muss, um zu verstehen was
    man da geschrieben hat, wird einem klar, dass Wartbarkeit ein durchaus wichtiger Aspekt von Code ist. Solche C-Frickeleien hab ich
    immer als WOC = Write Only Code bezeichnet. Trotzdem finde ich es durchaus lehrreich, ich mit C zu beschäftigen. Momentan
    programmieren die meisten um mich herum in C# und haben m.E. oft keine Ahnung über die Performance-Auswirkungen von den
    komfortablen Mechanismen zur automatischen Speicherverwaltung.

    Das Genie beherrscht das Chaos

  • Ein for in C hat eigentlich die Form:

    Code
    for(<Initialisierung>;<Vergleich>;<De-/Inkrement) <Anweisung>


    Im Unterschied zu anderen Sprachen, kann man aber beliebiegen Code an allen
    vier Stellen einsetzen.


    ich persönlich halte die Art der C Programmierung für höchst unnötig und ist eher ein auf "Dicke Hose machen".


    Besonders bemerkenswert find ich so Schleifen mit zwei oder mehr Variablen. Ist ganz extrem (un)übersichtlich und wenn man das noch nie gesehen hat vorher, denkt man, daß die einen veralbern wollen.


    Das mit der dicken Hosen ist sicher manchmal durchaus so. Manchmal ist es aber auch wirklich einfacher, wenn man in dem Maschinendenkmodell drinsteckt, sowas so zu schreiben. Daß das am Ende - heutzutage - noch den eigentlichen Programmcode verbessert, glaube ich auch nicht. Es verschlechtert aber für alle nicht-C-Könner die Lesbarkeit ungemein - was ja wiederum für die Aufrechterhaltung des eigenen Coolnessfaktors unabdingbar ist.


    Anscheinend hat aber bisher noch niemand das Geheimrezept gefunden, mit dem man "Programmierung für Alle" einführen kann. Python und Ruby, evtl. Lua u.ä. versuchen sowas zwar, aber zwischen dem Google-Scratch Ansatz und den Textlösungen klafft da irgendwie eine gewaltige Lücke. Rechenpower genug sollte mittlerweile ja da sein, daß das allgemein nutzbar wird. Das wirklich bemerkenswerte an Rechnern ist doch, daß die das machen, was man ihnen sagt, nur die Art der Vermittlung ist gewaltig barrierenbewehrt, immer noch.

    -- 1982 gab es keinen Raspberry Pi , aber Pi und Raspberries

  • Anscheinend hat aber bisher noch niemand das Geheimrezept gefunden, mit dem man "Programmierung für Alle" einführen kann. Python und Ruby, evtl. Lua u.ä. versuchen sowas zwar, aber zwischen dem Google-Scratch Ansatz und den Textlösungen klafft da irgendwie eine gewaltige Lücke. Rechenpower genug sollte mittlerweile ja da sein, daß das allgemein nutzbar wird. Das wirklich bemerkenswerte an Rechnern ist doch, daß die das machen, was man ihnen sagt, nur die Art der Vermittlung ist gewaltig barrierenbewehrt, immer noch.


    Ich selbst hab zwar nie etwas mit Smalltalk gemacht, hab aber im Hinterkopf abgespeichert, dass das ein sehr konsequenter Ansatz in Richtung "einfache Mensch-Maschine-Schnittstelle" war.
    In Wikipedia hab ich jetzt gerade nachgeschaut, dass Smalltalk mit Squeak gerade wieder eine Renaissance erlebt
    https://de.wikipedia.org/wiki/…_%28Programmiersprache%29
    und
    https://de.wikipedia.org/wiki/Squeak

    Das Genie beherrscht das Chaos

  • OK. Werd ich mal angucken. Lustige Maus als Symbol haben sie ja schonmal.
    Smalltalk steht bei mir seit laaangem auf gleichen Liste wie Prolog - unbedingt mal bißchen Angucken; aber bisher nicht passiert.



    Zum C-Code noch: Wenn da glyphs ganz oben als glyphs[] Array mit einer Dimension belegt wird, ist das ja noch verstehbar. Wo dann der "mindfuck" dazukommt ist unten in der wichtigsten Zeile "von das Ganze", wenn zum Auslesen das Feld als glyphs[][] wieder auftaucht und damit plötzlich Zeilen und Spalten hat

    Code
    line[b * 8 + c] = glyphs[(ind / 8 * 7) + a][(ind % 8 * 7) + c];


    und quasi nach Buchstaben geordnet abgefragt wird mit einem Index (ind) der sich i.P. direkt aus dem ASCII Code des Zeichens ergibt

    Code
    if ((ind = (*argv)[b] - ' ') < 0)
    	  ind = 0;


    und daher, wie gedacht, von 0 bis ( 128 -32 ) läuft. Auch die Konstruktion hier oben ist eigentlich total simpel, aber schon fast unleserlich, für BASIC Mannen ( mich eingeschlossen ). Da wird nämlcih erstmal innerhalb der Klammer dem ind ein Wert zugewiesen und danach wird dann ind mit <0 verglichen und auf 0 gesetzt, wenns kleiner war ( alle Steuercodes ). Daß ind jetzt den Wert hat geht dabei beim lesen fast unter.
    Und ind wird danach zum Auslesen eines Array verwendet, wobei es dem C völlig egal ist, wie man sich das anschaut. Solche Array Konstrukte sind i.P. nur sowas wie eine Schreiberleichterung für den Programmierer. Sie haben aber für den Rechner nicht viel Bedeutung, weshalb man eben auch nachträglich die Art des Auslesen anders verwenden kann, als das Belegen. Man muß sich das einfach als eine lange Reihe von "#" und " " im Speicher vorstellen, wo man halbwegs geschickt wissen muß, an welcher Stelle man was auslesen will. Es gibt da auch keine Tests darauf, ob das Sinn macht oder zulässig ist, was man als glyph[][] beschreibt. Sollte eigentlich bei dem Programm aus testbar sein, wenn man einen Wert größer als das letzte Zeichen eingibt, also irgendwas mit ASCII >= 128. Da solltte dann durchaus was angezeigt werden, aber nix Sinnvolles mehr, weil da plötzlich aus dem Bereich hinter der glyphs[] Variablen ausgelesen wird. Wenn dort zufällig Paßworte liegen, ist das dann schon interessant ( OK. Unwahrscheinlich. ). Einfach mal probieren !

    -- 1982 gab es keinen Raspberry Pi , aber Pi und Raspberries

    2 Mal editiert, zuletzt von ThoralfAsmussen ()

    • Offizieller Beitrag
    Zitat

    Zum C-Code noch: Wenn da glyphs ganz oben als glyphs[] Array mit einer Dimension belegt wird, ist das ja noch verstehbar. Wo dann der "mindfuck" dazukommt ist unten in der wichtigsten Zeile "von das Ganze", wenn zum Auslesen das Feld als glyphs[][] wieder auftaucht und damit plötzlich Zeilen und Spalten hat


    Wobei es oben ja schon zweidimensional durch die Zuweisung deklariert wird *c[]={...} Pointer, Arrays, alles egal ... ;)


    Ich habe bei meinen Zeichenausgaben häufiger mit solchen Konstrukten Ärger gehabt. Daher verwende ich gerne Typendeklarationen.


    Einfaches Beispiel für verkettete Strukturen für eine Warnungsanzeige im Auto:



    Gruss,
    Peter


  • Auch ich stimme Dir zu. Der Code sollte für Menschen gut verständlich sein, ohne daß man beim Lesen lange über überkandidelte Ausdrücke grübeln muß. Für Optimierungen ist der Compiler zuständig. Kleine Abkürzungen wie if (x) statt if (x != 0) sind schon in Ordnung, aber beim Hantieren mit verschachtelten *,&,--,++ schießt man sich leicht selbst ins Bein, und wenn es dann nicht tut, wie es soll, verbrät man mehr Zeit mit Debuggen als mit Programmieren.


    Als man noch um jedes Byte kämpfen mußte, weil Speicher knapp und teuer war, waren die Maßstäbe natürlich andere.


    Gruß,
    Markus

    • Offizieller Beitrag

    Man seid ihr alle schlau. :anbet: Ich muss das erst mal lesen und verstehen, was ihr geschrieben habt.


    Kann mir jemand erklären, was es mit den Sternchen auf sich hat? Also zB "char **argv"?


    Sternchen haben doch irgendwas mit Zeigern zu tun, aber ich muß gestehen, daß mir das Konzept noch reichlich nebulös vorkommt.

    • Offizieller Beitrag

    char **argv


    Du wolltest es so:


    argv ist eine Variable, die auf eine Speicherstelle zeigt, deren Wert zeigt auf eine Speicherstelle, in dr ein char steht.
    Alles klar?


    Jetzt die Langform:
    Deine Kommandozeile sieht so aus:
    prog.exe Param1 Test2 Bla3


    Daraus wird eine Zeigertabelle erzeugt:
    0: Zeiger auf prog.exe
    1: Zeiger auf Param1
    2: Zeiger auf Test2
    3: Zeiger auf Bla3


    argv dann auf diese Tabelle.


    Jetzt klar?


    Wenn nicht, was bestimmt kein Mackel ist, schreibst du am besten mal ein kleines Programm und startest es im Debugger. Danach schaust du dir den Speicher in Ruhe an.


    Viel Erfolg

    • Offizieller Beitrag

    C kennt keinen String.
    In C stehen alle Zeichen im Speicher hintereinander und \0 ist die Endekennung.


    char c
    c ist eine Variable vom Typ char (-128 .. +127)


    char * cp
    cp ist eine Variable, die als Inhalt einen Zeiger (Adresse) auf eine Speicherstelle enthält, die einen char enthaelt.


    char ** cpp
    cpp ist eine Variable, die als Inhalt einen Zeiger (Adresse) auf eine Speicherstelle enthält, die als Inhalt einen Zeiger (Adresse) auf eine Speicherstelle enthält, die einen char enthaelt.

    • Offizieller Beitrag


    char ** cpp


    C kennt keine Strings. Soso.


    Das mit dem einen Stern ist mir noch klar. Also ist bei *c der Inhalt beispielsweise Adresse 100, und der "String" "Hallo" belegt die Adressen
    100 = "H", 101="a", 102="l", 103="l", 104="o", 105="\0" ?


    Aber die 2 Sterne?
    Welchen Sinn macht ein Zeiger auf einen Zeiger? Ist das ein Äquivalent eines Array of String?


    Danke!
    Stephan

  • Doppelpointer, auch verketteter Zeiger, wird u.a. bei mehrdimensionalen Arrays verwendet.
    Hab ich in meiner C-Zeit nie verwendet (weil nicht verstanden)

    "There is no reason for any individual to have a computer in his home." Ken Olson, president, chairmen and founder of Digital Equipment Corp, 1977

    • Offizieller Beitrag

    C kennt keine Strings. Soso.


    Auf jeden Fall keinen Typ String, so wie z.B. in Pascal.


    Auch in C wird "Hello World" als String bezeichnet, aber eigentlich liegen nur Werte (ASCIIs) im Speicher.


    Probier mal aus:
    printf("Hello World!" + 6);


    Aber hier verweis ich auf Schroeders Post u.a. zur Lesbarkeit von Code und Spielerein mit diesem.
    Also nur zum Verstaendnis machen!


    Also ist bei *c der Inhalt beispielsweise Adresse 100


    Jein!


    c = 100, besser: c = (char *) 100
    *c = 'H', nicht "H"!!!


    "H" entspricht 'H', '0' und hat als Wert die Adresse von 'H'.


    Mal ganz rudimentaer:
    C ist eine maschinenunabhaengige Assemblersprache, die ohne GOTOs auskommt.
    Assembler kennt auch keine Strings, nur Speicherstellen, char, integer und Pointer.

    • Offizieller Beitrag

    Hab ich in meiner C-Zeit nie verwendet (weil nicht verstanden)


    Unterschreib ich dir sofort! ;)
    Weil ein Doppelpointer nichts miit mehrdimensionalen Arrays zu tun hat.


    Ein int array[10][20] ist und bleibt ein int* (Ist nicht ganz korrekt ausgedrueckt! Sorry)
    Auch hier liegen nur 200 Werte im Speicher. Nur der Compiler benutzt die [20] um die richtige Speicherstelle zu adressieren.


    Nachtrag:
    Die Speicherstelle von array[2][5] ist das gleiche wie von array[2 * 20 + 5]

    • Offizieller Beitrag

    Aber die 2 Sterne?
    Welchen Sinn macht ein Zeiger auf einen Zeiger? Ist das ein Äquivalent eines Array of String?


    So direkt benutzt man sowas selten.
    Meistens entsteht ein ** durch Zeiger-Tabellen, z.B. beim char ** argv.

  • Wobei es oben ja schon zweidimensional durch die Zuweisung deklariert wird *c[]={...} Pointer, Arrays, alles egal ... ;)


    Da wir ja nun doch da gelandet sind, kannst Du das evtl. noch kurz ausführen - warum zweidimensional, bzw. wie meintest Du das ?
    (Bei mir war zweite Dimension die zweite Doppelklammer.)


    Sternchen haben doch irgendwas mit Zeigern zu tun, aber ich muß gestehen, daß mir das Konzept noch reichlich nebulös vorkommt.


    Du wolltest es so:


    Au weia, gleich direkt die richtige Frage erwischt...



    Das schlimme an dem Zeug ist, das kann man halbwegs kapiert haben, und wenn dann ein Vierteljahr vergangen ist und das nächste Basteln ansteht, kommen einem wieder tausend unverstandene Probleme dabei auf. Meist ist man selbst dran schuld, aber es braucht auch oft lang bis man kapiert hat warum eigentlich.



    Zum Doppelpointer: Das kann sehr sinnvoll sein und jeder der Pascaldemos kennt, hat sowas i.P. schonmal gesehen. Dort werden z.B. X,Y Paare + Farbwerte o.ä.in einem Array abgelegt und man könnte jetzt mit X[] und i den Wert auslesen und plotten. Sollen die Sachen aber vor dem Plotten sortiert werden, kann es sinnvoll sein, nur für das Sortieren ein Extra Array anzulegen, in das man nur die "i" Werte reinschreibt - am Anfang als Extra[0] bis Extra[last]. Wenn man nun die Sortierroutine laufen läßt, tauscht man nur die Werte in der Extra[] Liste um und faßt die eigentlichen Daten gar nicht mehr an. X[],Y[],Farbe[] bleiben also dabei unangetastet. Der Vorteil es ist um miin. einen Faktor 3 schneller und trotzdem sortiert.


    Wenn man das gleiche Spiel nun mit Strings machen will, hat man das Problem, daß die Strings ja unterschiedlich lang sein können. Wenn man Speiherplatz sparen will, legt man also die Buchstaben einzeln in den Speicher und merkt sich z.B. die jeweilige Länge oder man benutzt eine Endemarkierung. Das wäre das Modell wie es in C gemacht wird. Da klebt am Ende diese "\0" mit dran. Damit man den Speicherplatz, der ja nicht mehr wie bei einem Array an einer definierten Stelle liegt, wiederfindet, braucht man aber schon den ersten Zeiger - also
    char * pointer
    wenn man diese Strings jetzt sortieren will, kann man den kompletten Speicher einmal umsortieren. Oder man tauscht direkt diese Zeiger aus, dann verliert man aber die Zuordnung, wie es am Anfang gewesen ist ( Reset auf Normalanordnung ist dann schwierig ). Oder man führt eine Liste mit Zeigern, die auf die Zeiger zeigen, die auf die Wortanfänge, d.h. die Einzelstring, zeigen. Das ist dann dieses
    char ** doppelpointer


    War hoffentlich ein bißchen nachvollziehbar ?

    -- 1982 gab es keinen Raspberry Pi , aber Pi und Raspberries

    2 Mal editiert, zuletzt von ThoralfAsmussen ()

  • Hehe, lustiger Thread.


    Nur kurz zu dem Doppelpointer, da ich am Tablet tippere und es da schon blöd ist Code zu tippern:


    Früher zur Kerninghan-Richie-C Zeit hat man argv (und envp) tatsächlich gerne als **argv deklariert.


    Im Speicher ist argv als NULL-terminiertes Pointer-Array abgelegt. Das meint: argv ist ein Array aus Pointern. Enthält einer der Pointer den Wert NULL, ist das Array zuende. Die einzelnen Pointer zeigen auf Strings, die die einzelnen Argumente enthalten.


    Mit ANSI-C wird das heutzutage daher so deklariert: char *argv[ ];


    Wenn der Compiler und das OS korrekt arbeiten, machen folgende beiden Codeabschnitte das selbe (Programmname und alle Argumente ausgeben):

    Code
    int i;
    for (i = 0; i <= argc; i++) puts(argv[i]);
    
    
    char **p;
    for (p = argv; *p != NULL; p++) puts(*p);


    Der zweite Fall ist für den Laien natürlich etwas schwieriger zu verstehen, hat aber den Vorteil, dass keinerlei Index-Offsets errechnet werden müssen, was bei großen Datenmengen oder langen Schleifen schon merkbar ins Gewicht fallen kann.


    Ansonsten kann ich nur zustimmen: Lieber leserlichen Code, als extrem gepackte Konstrukte. Erst wenn die Laufzeitanalyse zeigt, dass man dringend optimieren muss, sollte man tiefer in die Trickkiste greifen. Aber dann bitte mit einem verständlichen Kommentar.


    Was allerdings unleserlich ist, hängt natürlich auch vom Background des Lesenden ab. Ich selbst finde z.B. Stringbearbeitung mit Pointern verständlicher und eleganter, als manch Riesenausdruck mit tief geschachtelten LEN und MID Ausdrücken im Index bei BASIC. ;)


    -- Klaus


    EDIT: Natürlich ein paar Sternchen vergessen ^^

    [ ... to boldly code where no byte has gone before ... ]

    2 Mal editiert, zuletzt von ktf ()

  • Macht aber schon Sinn, dass sie jetzt in C++ so nach und nach die Java string Klasse nach programmieren. Einige wichtige Dinge wie trim, endsWith usw fehlen ja immer noch.

  • Daraus wird eine Zeigertabelle erzeugt:
    0: Zeiger auf prog.exe
    1: Zeiger auf Param1
    2: Zeiger auf Test2
    3: Zeiger auf Bla3


    Das erklärt auch die Zeile 132 des Codes: for(argv++ ...
    Damit zeigt der Zeiger schon im ersten Schleifendurchlauf auf das zweite "Array"-Element, nämlich den ersten Parameter.

    Das Genie beherrscht das Chaos

  • Ganz gut kommt man, wenn man sich bei jeder for Schleife vorstellt, daß das Konzept des Durchzählens da noch komplett unbekannt gewesen sein muß, weshalb Die Herren Kerninghan und Richie das nicht wissen konnte, und das darum einfach eine seltsame Abart einer while Schleife ist. Dann wird i.a. auch für PASCAL Menschen recht verständlich.


    Und zu den Pointern noch: Ein wichtiger Punkt, der auch in Büchern oft nur so beiläufig "mit"erwähnt wird ist, das so ein C Pointer immer eine Typbindung hat. Soll heißen, der Pointer weiß, auf was für eine Struktur er zeigt und wieviel Platz die im Speicher benutzt. Also char* 1 Byte vs. int* 4 Byte. Nur dadurch funktionieren solche Konstrukte wie argv++ , also Adresse um 1 Byte vs. 4 Byte erhöhen, überhaupt. Auch das Bewegen in Arrays mittels eines Pointers basiert darauf, daß der Pointer weiß, was für Datentypen im Array liegen.
    Interessante Fehler und Fehlermeldungen bekommt man, wenn man das nicht weiß und z.B. Pointer auf unterschiedliche Strukturen einander zuweist.
    Ich weiß nicht mehr, wie das in Pascal war, aber im BBC Basic gibt es auch so eine Art Pointer, dort wird aber einfach eine Adresse gemerkt. Der zweite wichtige Teil eines C-Pointers, eben diese Typbindung, gibts da nicht.



    ( Und noch als Ergänzung zu "Sortieren per Extra-Array" respektive Doppelpointer: Man kann so eine sortierte Liste auch aufheben und eine zweite nach einem anderen Suchkriterium sortieren und eine dritte wieder anders. Da die nur Pointer enthalten sind, ist sie recht klein, aber das Ergebnis ist "instantan" da. Im Prinzip ist das was, was viele großen Datenbanken benutzen - oracle mal als Stichwort. Bei Neuaufnahmen von Daten müssen die halt genau einmal in diese Sortierlisten einsortiert werden - thats all. Es gibt also durchaus ein paar Anwendungen für sowas. )

    -- 1982 gab es keinen Raspberry Pi , aber Pi und Raspberries

    • Offizieller Beitrag

    Mahlzeit,


    ich denke Pointer/Zeiger sind der Weg Speicherplatz zu sparen wenn man mit konstanten Zeichenketten arbeiten die eine unterschiedliche Länge haben.
    Hier verliere ich bei einer Arraydeklaration unter Umständen viel Speicher. Selbst heutige Controller für Embedded Devices drücken einen doch starke Speicherlimitierungen auf. Da ist man schnell wieder 20 Jahre in der Vergangenheit.


    Eine echte Hilfe sind Pointer aber bei verketteten Listen wie in meinem Beispiel vorher gezeigt. Ich arbeite bei meinen Fahrzeugprojekten sehr viel mit Strukturen, Unions und die Verlistung solcher. In den Strukturen deklariert man dann Zeiger auf die nächste Struktur.


    Hier ein Beispiel für eine Warnungsausgabe für ein Cockpitdisplay im Auto wo die Struktur einmal Elemente "*content" für die formatierte Ausgabe, den Status der Warnung und einen Anzeigetimer enthält, wobei content wieder eine andere Struktur ist:


    Code
    struct warning{
    	struct warning *next;
    	warningcontent *content;
    .
    .
    .
    	char timer;
    }


    Diese weise ich den Zeigern wbegin,wpointer und den Inhalten zu:

    Code
    *wbegin,*wpointer,woelpressure,wairbag,wengine,woellevel,wcoolentlevel,wgen,wbreak,wbreaklever,wfluid,wgas,wfrost;


    Dann bilde ich die Liste:


    Jetzt zeigt der Zeiger wbegin auf die Adresse der ersten Struktur: wbegin=&woelpressure und diese auf die nächste: woelpressure.next=&wairbag usw. Das letzte Element der Liste zeigt auf 0.


    Und nun kann ich mit einer Schleife durch die Liste gehen und die Elemente abhandeln:


    Code
    for(wpointer=wbegin;wpointer;wpointer=wpointer->next){
      if(wpointer->timer)...
    }


    Ich persönlich finde, das illustriert den Vorteil der Zeigertechnik.


    Gruss,
    Peter

  • Lest mal nach, warum die Herren Kernighan und Ritchie C kreiert haben. Damals mußte das Betriebssystem für jeden neuen Computertyp neu geschrieben werden, weil das alles Assemblerprogramme waren. Mit dem C-Compiler mußten im Prinzip nur die Assembler-Mnemonics geändert und der Code neu compiliert werden. Dementsprechend ist C eigentlich nur ein "Luxusassembler". Daher kommen solche Sachen wie "var++". Das wird einfach zu einem "inc var". Jeder Prozessor kann indirekt adressieren. Beim Z80 beispielsweise gibt es ein "ld a,(bc)". Da wird der Inhalt der Speicherzelle mit der Adresse, die in bc steht, in den Akku geladen. Damit ist (bc) ein Pointer. Diese Aufzählung könnte fortgesetzt werden.


    Gruß Gerhard

  • Nu ja, also es gab schon durchaus Algol ( ok, kann man evtl. streichen ) , aber in benutzbar und bis heute in "Betrieb" : Fortran (!) ( super für SMP und SIMD ) , Basic , Cobol . Nicht nur Assembler.
    Und eine Indirektion bei Ladebefehlen etc. auf Maschinensprachebene ist meist irgendwie begrenzt auf einen bestimmten Umgebungsbereich der mitübergebenen Adresse, wogegen ein vernünftiger Pointer den kompletten Adressraum abdecken kann.

    -- 1982 gab es keinen Raspberry Pi , aber Pi und Raspberries