frankx: Anfängerfrage zu Strings in C

Hellihello,

*line ist ein string "norton,porton,blasiblub\n":

folgende funktion wird damit aufgerufen

  
char **  
csvlinesplitting(char *line)  
{  
        int counter;  
        counter = 0;  
        char elements[120];  
  
       printf("Elements am Anfang: %s\n\n", elements);  
        while(*line != '\n') {  
                if (*line != ',') {  
                        elements[counter] = *line;  
                        printf ("xxx linechar: %c yyy elements: %s\n", *line,elements);  
                        counter++;  
                }  
                else {  
                        printf("\n\nes ist ein komma da: %c - elementsinhalt ist : %s\n", *line, elements);  
                        printf("lineinhalt ist : %s\n", line);  
                        counter=0;  
                }  
                line++;  
  
        }  
}  

Ausgabe:

++++++++++++++++

Elements am AnfangÐÓÿ¿

xxx linechar: n yyy elements: ÐÓÿ¿
xxx linechar: o yyy elements: ÐÓÿ¿
xxx linechar: r yyy elements: nÐÓÿ¿
xxx linechar: t yyy elements: norÐÓÿ¿
xxx linechar: o yyy elements: nortoÐÓÿ¿
xxx linechar: n yyy elements: nortoÐÓÿ¿

++++++++++++++++++++

etc. pp.;

Diese "ÐÓÿ¿" kommt daher, dass das Charakter-Array "elements" im Speicher irgendwo angesiedelt wird und so die Inhalte dort übernimmt?

Wie kriege ich das "weg" bzw. wie mache ich das richtig?

Ich will den String "lineinhalt" bis zum nächsten Komma durchgehen und in der variablen (Char-Array) "elements" speichern, ohne "ÐÓÿ¿" da stehen zu haben (;-).

In PHP hätte ich dann wohl gemacht:

elemenst[0] += lineinhalt[n...]; und nach jedem komma dann
elements[1] += lineinhalt[n...]

Konnte ich mich verständlich machen? Es geht hier eher um ein Grundverständnis, keine "Anwendung" im Blick.

Dank und Gruß,

frankx

--
tryin to multitain  - Globus = Planet != Welt
  1. Hallo,

    if (*line != ',') {
            elements[counter] = *line;

    // ergänze hier:
               elements[counter+1] = 0;

    printf ("xxx linechar: %c yyy elements: %s\n", *line,elements);
            counter++;
         }
         else {
            printf("\n\nes ist ein komma da: %c - elementsinhalt ist : %s\n", *line, elements);
            printf("lineinhalt ist : %s\n", line);
            counter=0;
         }

    Diese "ÐÓÿ¿" kommt daher, dass das Charakter-Array "elements" im Speicher irgendwo angesiedelt wird und so die Inhalte dort übernimmt?

    Ja, im Prinzip schon. Da du elements[] nicht initialisierst (also mit einem definierten Anfangswert belegst), ist der Inhalt das, was "zufällig" an der Stelle im Speicher rumliegt.

    Wie kriege ich das "weg" bzw. wie mache ich das richtig?

    Bedenke, wie Strings in C organisiert und gespeichert werden: Ein Nullbyte markiert das Ende. Wenn du den String Zeichen für Zeichen aufbaust, musst du also dieses Nullbyte auch mit anfügen. Entweder erst am Ende der Schleife, wenn der String vollständig ist, oder in jedem Durchlauf aufs Neue. Letzteres hat den Vorteil, dass du auch in jedem Schleifendurchlauf ohne "Dreckeffekte" auf den Inhalt des Strings zugreifen kannst, nicht erst am Ende.

    Konnte ich mich verständlich machen? Es geht hier eher um ein Grundverständnis, keine "Anwendung" im Blick.

    Ja, und noch was zum Grundverständnis: Deine Schleife hat keine Prüfung daraufhin, ob der übergebene String überhaupt in elements[] passt. Übergibt man der Funktion einen längeren String als 120 Zeichen, dann schreibt deine Schleife irgendwann in Speicherbereiche, die sie nichts mehr angehen. Das ist dann ein klassischer Fall für einen "Buffer Overflow".
    Dasselbe passiert, wenn ich einen String übergebe, der kein \n enthält. Denn deine while-Schleife bricht nur ab, wenn sie ein \n findet, läuft aber munter über das String-Ende hinaus, wenn kein \n vorkommt!

    Ach so, und deine Funktion liefert kein Ergebnis zurück.

    So long,
     Martin

    --
    Kleine Geschenke erhalten die Freundschaft.
    Große verderben sie aber meist auch nicht.
    1. Hellihello Martin,

      merci.

      Hallo,

      if (*line != ',') {
              elements[counter] = *line;
         // ergänze hier:
                 elements[counter+1] = 0;

      Warum denn elements[1] = 0; und nicht elements[0]?

      Ja, im Prinzip schon. Da du elements[] nicht initialisierst (also mit einem definierten Anfangswert belegst), ist der Inhalt das, was "zufällig" an der Stelle im Speicher rumliegt.

      Das wäre dann das            elements[counter+1] = 0;?

      Und das 0 ist gleich mit \0, dem Stringende?

      Dank und Gruß,

      frankx

      --
      tryin to multitain  - Globus = Planet != Welt
      1. Hellihello

        Warum denn elements[1] = 0; und nicht elements[0]?

        Falsch gedacht. Innerhalb des "while" ja, und da cann elements[counter+1]=0; heißt, dass hinter den bereits eingelesenen String deben die 0 gesetzt wird. Jetzt "funzt" es auch (;-).

        Dank und Gruß,

        frankx

        --
        tryin to multitain  - Globus = Planet != Welt
  2. Hallo,

    *line ist ein string "norton,porton,blasiblub\n":

    Konnte ich mich verständlich machen? Es geht hier eher um ein Grundverständnis, keine "Anwendung" im Blick.

    Zeichenketten in C sind Null-terminiert. Dafür musst *Du* sorgen, das nimmt Dir keiner ab, siehe z.B: http://de.wikibooks.org/wiki/C-Programmierung:_Arrays_und_Strings#Strings

    Schau Dir dort die Funktionen an ...
    Ich käme übrigens nicht auf die Idee, C für Zeichenkettenverarbeitung nutzen zu wollen. So ziemlich jede andere Programmiersprache, die ich kenne, ist dafür besser geeignet - auch C++.

    Freundliche Grüße

    Vinzenz

    1. Hello,

      Zeichenketten in C sind Null-terminiert. Dafür musst *Du* sorgen, das nimmt Dir keiner ab, siehe z.B: http://de.wikibooks.org/wiki/C-Programmierung:_Arrays_und_Strings#Strings

      Ist das erst in C++ so, dass bei der Zuweisung

      char* zkette = "abrakadabra";

      die \0 automatisch angehängt wird? Musste man das bei C noch selber machen?

      Jetzt hast Du mich wirklich auf dem linken Fuß erwischt.

      Liebe Grüße aus Syburg bei Dortmund

      Tom vom Berg

      --
      Nur selber lernen macht schlau
      http://bergpost.annerschbarrich.de
      1. Hallo Tom,

        Ist das erst in C++ so, dass bei der Zuweisung
            char* zkette = "abrakadabra";
        die \0 automatisch angehängt wird? Musste man das bei C noch selber machen?

        nein, wenn du eine Stringkonstante im Quelltext stehen hast, wird dieser String auch vom Compiler schon mit einem Nullbyte versehen.
        Aber Achtung!

        Fall 1:    char *s1    = "hallo";
        Fall 2:    char  s2[4] = "hi";
        Fall 3:    char  s3[4] = "hiho";

        Im Fall 1 wird der String "hallo" mit einem abschließenden Nullbyte irgendwo (das "wo" soll uns nicht interessieren) im Arbeitsspeicher abgelegt, also die Bytesequenz 68 61 6C 6C 6F 00. Und der Zeiger s1 verweist auf diesen Speicherbereich.

        Im zweiten Beispiel wird ein 4 Byte langes char-Array angelegt und mit der Bytesequenz 68 69 00 00 initialisiert - das Nullbyte ist also auch hier automatisch mit dran.

        Im Fall 3 wird schließlich auch ein char-Array mit einer festen Länge von 4 Elementen angelegt, das aber keinen Platz mehr für die abschließende Null hat. Hieraus resultiert eine Bytesequenz 68 69 68 6F, die keinen ordentlichen String-Abschluss hat. Würde man das als String interpretieren, ist sowohl die Länge als auch der Inhalt ab dem fünften Zeichen fraglich.

        Jetzt hast Du mich wirklich auf dem linken Fuß erwischt.

        Dann steig doch wieder auf den anderen um! ;-)

        So long,
         Martin

        --
        PCMCIA: People Can't Memorize Computer Industry Acronyms
        1. Hello Martin,

          Jetzt hast Du mich wirklich auf dem linken Fuß erwischt.

          Dann steig doch wieder auf den anderen um! ;-)

          Jau, mach ich.
          Mit Deiner Hilfe wird es klappen :-)

          Wegen der Incrementierung des Zeigers auf das Char-Array hatte im Hinterhirn, dass man dann nicht mehr weiß, wo es begann, und wenn man dann nochmals das gesamte Char-Array benötigt, könnte man ein Problem haben.

          Deshalb ist es auch nicht verboten, sondern nur "verboten"  [Wegen der Häkchen -> Grüße an at an dieser Stelle :-), Auto ist am WE mal wieder in der Werkstatt, ich würde gerne mal mit dem Rad vorbeischauen, sonst benutze ich das doch nicht mehr diese Jahr... ]

          Aber Du hast schon Recht, in C darf man wohl noch alles versuchen, ob man es machen durfte, zeigt einem später die Laufzeitversion des Programms *gg*

          Ich habe mich ganz schön umgeguckt, wie oft einem hingegen der Compiler bei C++ auf die Finger klopft.

          Liebe Grüße aus Syburg bei Dortmund

          Tom vom Berg

          --
          Nur selber lernen macht schlau
          http://bergpost.annerschbarrich.de
          1. n'Abend Tom,

            Jetzt hast Du mich wirklich auf dem linken Fuß erwischt.
            Dann steig doch wieder auf den anderen um! ;-)
            Jau, mach ich.
            Mit Deiner Hilfe wird es klappen :-)

            zuviel der Ehre ...

            Wegen der Incrementierung des Zeigers auf das Char-Array hatte im Hinterhirn, dass man dann nicht mehr weiß, wo es begann, und wenn man dann nochmals das gesamte Char-Array benötigt, könnte man ein Problem haben.

            Ja, wenn man den *einzigen* gültigen Zeiger auf einen Speicherblock verändert, dann verliert man damit natürlich eine wertvolle Information.
            Solange man aber nur mit Kopien arbeitet, kann man auch in Opas Testament rumkritzeln ...

            Aber Du hast schon Recht, in C darf man wohl noch alles versuchen, ob man es machen durfte, zeigt einem später die Laufzeitversion des Programms *gg*

            Solange man weiß, was man tut, darf man alles tun. :-)

            Ich habe mich ganz schön umgeguckt, wie oft einem hingegen der Compiler bei C++ auf die Finger klopft.

            Naja, das kommt auf den Compiler an. Aber die Komplexität und die damit verbundene Problematik, dass man nicht alles einfach machen darf, ist einer der Gründe, warum ich C++ nicht so mag.

            Ciao,
             Martin

            --
            F: Was ist schlimmer: Alzheimer oder Parkinson?
            A: Parkinson. Lieber mal ein Bier vergessen zu zahlen, als eins verschütten.
          2. Hallo.

            Grüße an at an dieser Stelle :-)

            Danke, für irgendetwas muss ein C-Thread -- nein, das ist keine Wertung -- ja auch für mich gut sein.
            MfG, at

            1. Hellihello

              für mich stehen mittlerweile zwei Fragen eher abstrakter Art im Raum.

              C reizt ja dazu, den Speicher, wenn man ihn schon selbst verwalten muss/kann, auch optimal zu nutzen. Bei so einer String-Action ist für mich nicht klar, wie sowas optimal zu lösen ist. Schon beim Einlesen mit fgets Zeilenweise wird ja Speicher belegt. In Zeilenlänge. Da ich beim CSV bestenfalls nur die Kommas in '\0' umzuwandeln muss, könnte ich ja die Zeile so lassen, die Kommas durch Nullen ersetzen und Stringpointer für die einzelnen Zellen auf diesen Zeilenspeicher setzen. Ist es aber möglich, den beim Einlesen durch fgets automatisch reservierten Speicher per malloc persistent zu machen. Oder denk ich da was falsch. Muss man immer ein Kopie davon irgendwo anlegen? Und weiter, ist es besser, den Zelleninhalt einzeln zu allokieren, oder besser eine größeres Stück in einem (Zeile fortlaufend, darin die Zellen). Platzmäßig ist das ja wohl egal. Eher eine Fragmentierungsfrage. Oder aber ist es am Besten, gleich die komplette Größe der einzulesenden CSV-Datei zu ermitteln und soviel en-bloc im Arbeitsspeicher zu definieren?

              Da es keine Objekte/Strukturen mit Methoden gibt, in denen dann per "this" oder "self" auf Klassen/Strukturvariablen zugreifen kann, muss man das zu bearbeitende Objekt ja immer per Speicheradressenangabe rumreichen. Entweder man lässt sich eine Speicheradresse zurückgeben, oder man übergibt eine,  deren Inhalt mit der Funktion dann manipuliert wird. Sind denn größere Projekte damit dann objektorientierungslike genauso übersichtlich realisierbar. Relativ abstrakt gesehen sind Klassen ja zum Teil "nur" Namensräume innerhalb derer dann interne "globale" Variablen definierbar sind. Diese Strukturierungsmöglichkeit fehlt ja bei C. Mit der Funktionsbenennung lässt sich da vermutlich noch ein Teil strukturieren. Ist also ObjectivC oder C++ dann die Folge, oder gibt es Gründe, bei C zu bleiben?

              Dank und Gruß,

              frankx

              --
              tryin to multitain  - Globus = Planet != Welt
              1. Hi,

                C reizt ja dazu, den Speicher, wenn man ihn schon selbst verwalten muss/kann, auch optimal zu nutzen.

                stimmt. ;-)

                Ist es aber möglich, den beim Einlesen durch fgets automatisch reservierten Speicher per malloc persistent zu machen.

                War das eine Frage.
                Im Ernst: Du hast von dem Moment an die Kontrolle über die Speichernutzung, da fgets() zurückkehrt und den eingegebenen String ans das Anwenderprogramm übergibt.
                Was fgets() intern anstellt, welche und wieviele Zwischenpuffer es verwendet oder ob es direkt eine geeignete Betriebssystemfunktion aufruft, darauf hast du keinen Einfluss.

                Muss man immer ein Kopie davon irgendwo anlegen?

                Wieso? Du rufst fgets() auf und sagst: Hier habe ich einen Puffer reserviert, leg mir die Eingabedaten bitte dorthin. Von da an brauchst du keine Kopien zu erzeugen, wenn dein Algorithmus ohne auskommt.

                Und weiter, ist es besser, den Zelleninhalt einzeln zu allokieren, oder besser eine größeres Stück in einem (Zeile fortlaufend, darin die Zellen). Platzmäßig ist das ja wohl egal. Eher eine Fragmentierungsfrage.

                Jetzt wird's philosophisch. ;-)
                Sagen wir's mal so: Das OS muss über jeden angeforderten Speicherblock Buch führen. Wenn du viele kleine Blöcke anforderst, steigt natürlich der Varwaltungs-Overhead im Verhältnis zum Nutzen an. Aber das ist IMHO eher akademisch; verlass dich drauf, dass das Betriebssystem damit umgehen kann.

                Da es keine Objekte/Strukturen mit Methoden gibt, in denen dann per "this" oder "self" auf Klassen/Strukturvariablen zugreifen kann, muss man das zu bearbeitende Objekt ja immer per Speicheradressenangabe rumreichen.

                Das ist im Prinzip dasselbe wie "this".

                Sind denn größere Projekte damit dann objektorientierungslike genauso übersichtlich realisierbar.

                Ja, eigentlich schon. Man muss eben nur Spaß daran haben, sich die Möbel selbst zu bauen, anstatt sie fertig liefern zu lassen. :-)

                Relativ abstrakt gesehen sind Klassen ja zum Teil "nur" Namensräume innerhalb derer dann interne "globale" Variablen definierbar sind. Diese Strukturierungsmöglichkeit fehlt ja bei C.

                Jein. Mit normalen structs kann man das "im Prinzip" auch haben.

                Mit der Funktionsbenennung lässt sich da vermutlich noch ein Teil strukturieren. Ist also ObjectivC oder C++ dann die Folge, oder gibt es Gründe, bei C zu bleiben?

                Ich kenne ObjectiveC nicht. Aber C++ mag ich nicht so sehr, weil einerseits vieles "automagisch" hinter meinem Rücken passiert. Außerdem verleitet es durch die etwas laxen Syntaxregeln verleitet es zur Schlamperei.
                Ansonsten: Wer's mag ...

                So long,
                 Martin

                --
                Wenn die Amerikaner eines Tages von jeder Tierart ein Pärchen nach Cape Canaveral treiben ...
                ja, DANN sollte man endlich misstrauisch werden.
                1. Hallo.

                  Außerdem verleitet es durch die etwas laxen Syntaxregeln verleitet es zur Schlamperei.

                  Offenbar kein nur C++ betreffendes Problem.
                  MfG, at

                  1. Hi,

                    Außerdem verleitet es durch die etwas laxen Syntaxregeln verleitet es zur Schlamperei.
                    Offenbar kein nur C++ betreffendes Problem.

                    danke, ja ...
                    Das kommt davon, wenn man die Formulierung noch zwei- bis dreimal umstellt und dabei nicht bei der Sache ist. *g*

                    Ciao,
                     Martin

                    --
                    Eine Nonne kommt in den Himmel. An der Pforte fragt Petrus: "Wer bist du?" - "Ich bin die Braut Jesu." Petrus stutzt einen Moment, ruft dann nach hinten: "He Freunde, habt ihr schon gehört? Der Juniorchef will heiraten!"
    2. Hellihello

      Ich käme übrigens nicht auf die Idee, C für Zeichenkettenverarbeitung nutzen zu wollen. So ziemlich jede andere Programmiersprache, die ich kenne, ist dafür besser geeignet - auch C++.

      Ich auch nicht (;-). Aber im Sinne vom C-Verständnis und gepaart bzw. ideenmäßig initialisiert von der Interessenslage eines Schülers fand ich es ein mögliches Beispiel, mal zu kapieren - auch für mich - wie sich das mit den Pointern und Strings in C verhält. Mein Groschen fällt langsam. Der Schüler weiß das eh schon alles und will ein bisschen mit C und CSV rumspielen, explizit selbst was programmieren.

      Dank und Gruß,

      frankx

      --
      tryin to multitain  - Globus = Planet != Welt
  3. Hello,

    ich versuche gerade, zu verstehen, was Du eigentlich wolltest.
    Ohne Kommentare ist das nicht ganz einfach.
    Jedenfalls sehe ich keinen String im Sinne von <string>, sondern nur ein Character-Array.

    Außerdem incremntierst Du den Zeiger auf das Character-Array, das ist "verboten".

    Um aauf ein Folgeelement zuzugreifen, kannst Du einen Zähler i incrementieren und diesem dem Zeiger auf die Zeichenfolge hinzuaddieren:

    Also z.B

    *(line + counter)

    Aber line solltest Du nicht verändernd anfassen.

    *line  ist gleichbedeutend mit line[0]
    Es ist also ohnehin einfacher, das Displacement [] zu verändern, als den Zeiger zu dereferenzieren.

    *line ist ein string "norton,porton,blasiblub\n":

    folgende funktion wird damit aufgerufen
    [code lang=c]

    char **     // das verstehe ich nicht? Wozu steht das da?
                  // Soll das der Typ (Rückgabewert) der Funktion sein?

    Liebe Grüße aus Syburg bei Dortmund

    Tom vom Berg

    --
    Nur selber lernen macht schlau
    http://bergpost.annerschbarrich.de
    1. Hi,

      Jedenfalls sehe ich keinen String im Sinne von <string>, sondern nur ein Character-Array.

      in C gibt es keinen intrinsischen Datentyp "String". Ein Character-Array wird aber in C als String verwendet.

      Außerdem incremntierst Du den Zeiger auf das Character-Array, das ist "verboten".

      Durchaus nicht. Es ist gängige Praxis, mit einem Zeiger "durch ein char-Array zu laufen".

      Um aauf ein Folgeelement zuzugreifen, kannst Du einen Zähler i incrementieren und diesem dem Zeiger auf die Zeichenfolge hinzuaddieren:

      Kann man machen - aber warum so umständlich. ;-)

      Also z.B
           *(line + counter)

      ... was gleichbedeutend ist mit
             line[counter]
      nur dass die Indexschreibweise für die meisten leichter zu verstehen ist.

      Aber line solltest Du nicht verändernd anfassen.

      Das ist, wie gesagt, völlig unproblematisch, solange es eh nur eine lokale Kopie ist - und ja, auch Funktionsargumente sind lokale Variablen.

      char **     // das verstehe ich nicht? Wozu steht das da?
                    // Soll das der Typ (Rückgabewert) der Funktion sein?

      Syntaktisch gesehen ja. Darüber habe ich mich auch gewundert. Also möchte frankx eigentlich einen Zeiger auf Zeiger auf char zurückgeben - im Prinzip ein Array aus Strings.
      Das passt aber nicht so ganz mit der Arbeitsweise der Funktion zusammen, außerdem hat er ohnehin versäumt, ein Ergegbnis zurückzuliefern.

      Ciao,
       Martin

      --
      Wenn man keine Ahnung hat - einfach mal Fresse halten.
        (Dieter Nuhr, deutscher Kabarettist)
      1. Hellihello

        char **     // das verstehe ich nicht? Wozu steht das da?
                      // Soll das der Typ (Rückgabewert) der Funktion sein?

        Syntaktisch gesehen ja. Darüber habe ich mich auch gewundert. Also möchte frankx eigentlich einen Zeiger auf Zeiger auf char zurückgeben - im Prinzip ein Array aus Strings.
        Das passt aber nicht so ganz mit der Arbeitsweise der Funktion zusammen, außerdem hat er ohnehin versäumt, ein Ergegbnis zurückzuliefern.

        Jau, darauf sollte es hinauslaufen, insofern ist das aber beim Beispiel falsch. Rückgabe sollte eben ein array von strings also ein array von char-arrays sein.

        Dank und Gruß,

        frankx

        --
        tryin to multitain  - Globus = Planet != Welt
        1. Hallo,

          Syntaktisch gesehen ja. Darüber habe ich mich auch gewundert. Also möchte frankx eigentlich einen Zeiger auf Zeiger auf char zurückgeben - im Prinzip ein Array aus Strings.
          Das passt aber nicht so ganz mit der Arbeitsweise der Funktion zusammen, außerdem hat er ohnehin versäumt, ein Ergegbnis zurückzuliefern.

          Jau, darauf sollte es hinauslaufen, insofern ist das aber beim Beispiel falsch. Rückgabe sollte eben ein array von strings also ein array von char-arrays sein.

          Das geht nicht. Du musst den Zeiger auf Dein Array von Strings per Referenz übergeben, zum Beispiel:

          /***
           * SplitLinesCsv
           * splittet eine Textzeile am Komma auf,
           * füllt die Teilzeichenketten in result
           *
           * @param line   Zeiger auf eine Zeichenkette
           * @param result Zeiger auf ein Array von Zeichenketten
           * @return       Anzahl der Teilzeichenketten
           *
           * Seiteneffekt: Speicher auf dem Heap wird allokiert
           */
          int SplitLinesCsv(char* line, char** result) {
              /* your code goes here */
              // Ermittle, wieviele Teilzeichenketten es gibt
              // Allokiere den Speicher für das notwendige Array von Zeigern auf char
              // Ermittle die Teilzeichenketten
              // Allokiere für jede Teilzeichenkette den notwendigen Speicher
              // ...
          }

          Und *Du* solltest eigentlich wissen, was man bei CSV eigentlich alles so berücksichtigen müsste: Trennzeichen, Maskierzeichen, ...

          Freundliche Grüße

          Vinzenz

          1. Hellihello Vinzenz,

            Danke für den Comment-Code.

            * @return       Anzahl der Teilzeichenketten

            einen Zeiger auf ein Array kann ich nicht returnen? Returen eines int ist ja offenbar unpromblematisch:

              
            #include <stdio.h>  
            int add(int a,int b)  
            {  
                    return a+b;  
            }  
            int main()  
            {  
                    printf("%d plus %d gleich %d", 1, 2, add(1, 2));  
            }  
              
            
            

            *
            * Seiteneffekt: Speicher auf dem Heap wird allokiert
            */
            int SplitLinesCsv(char* line, char** result) {

            warum "char* line" und nicht "char *line"?

            // Allokiere den Speicher für das notwendige Array von Zeigern auf char

            auf "char"?

            Wie werden denn in "C" Variablen per Referenz übergeben? Müssen sie vorher global definiert sein.

            Über den Heap und das Allozieren muss ich mich wohl noch schlau machen. Mit den Tiefen der CSV-Logik hat das natürlich nur wenig zu tun. Das hatte ich mit dem Impulsgeber aber schon besprochen und wir hatten uns darauf geeinigt, diesen herausgelösten Aspekt als Übung zu verstehn.

            Der Schüler macht/versucht das übrigens mit "*result_string++ = *line++".

            Dank und Gruß,

            Robert aka

            frankx

            --
            tryin to multitain  - Globus = Planet != Welt
            1. Hello,

              einen Zeiger auf ein Array kann ich nicht returnen?

              Doch kannst Du. Aber Du musst dafür sorgen, dass das Array nach dem Ende der Funktion auch noch da ist. Das schrieb ich Dir schon in https://forum.selfhtml.org/?t=177894&m=1172341

              Wie funktioniert das mit dem Funktionsaufruf?

              Auf dem Stack wird Platz reserviert für

              • Rückgabewert (Anzahl der Bytes nach Typ, denn der eigentliche Wert ist ja noch nicht bekannt)
              • Aufrufparameter
              • Register
              • Return (far)-Befehl  mit Anzahl der Bytes die später wieder entfernt werden müssen

              Dann wird die Kontrolle (BP) an den Funktionskörper übergeben.

              Wenn Du nun innerhalb dieser Funktion weitere Variablen aufmachst, werden die ebenfalls auf den Stack draufgepackt. Wenn die Funktion endet, wird der Stack wieder bereinigt, oder genauer gesagt, der Stackpointer wieder auf den Wert vor dem Funktionsaufruf zurückgesetzt. Dein Array liegt zwar nun noch auf dem Stack, aber außerhalb des referenzierten Bereiches, also im nächsten Moment vermutlich schon innerhalb des Arbeitsbreiches der nächsten aufgerufenen Funktion.

              Nur der Rückgabewert, und das war ja nur ein Zeiger auf das Array, bleibt der Parent-Funktion auf dem Stack solange erhalten, bis sie ihn abgeholt hat.

              Wenn Du nun über diesen Zeiger auf das Array zugrifen willst, greifst Du in den Stack hinein, an eine Stelle, von der Du nicht weißt, welchem Programmteil dieser inzwischen zugeordnet ist.

              Daher musst Du Dir den Speicher mit malloc() oder  mit new oser sonstigen Methoden auf dem Heap beschaffen. Dort bleibt er solange für den zeiger erhalten, bis er mit delete, dispose, free oder wie der Rückgabebefehl sonst auch immer heißt, wieder freigegeben wird. Deshalb darf dir der Zeiger auf den allokierten Speicher auch nicht verloren gehen, weil dieser gleichzeitig der Key für den Eintrag in der Speicherverwaltung ist. Diese weiß, wieviel Speicher zu welchem Zeiger allokiert wurde.

              Liebe Grüße aus Syburg bei Dortmund

              Tom vom Berg

              --
              Nur selber lernen macht schlau
              http://bergpost.annerschbarrich.de
            2. Hallo,

              *
              * Seiteneffekt: Speicher auf dem Heap wird allokiert
              */
              int SplitLinesCsv(char* line, char** result) {

              warum "char* line" und nicht "char *line"?

              Reine Geschmackssache, für den Compiler ergibt sich kein Unterschied.
              Meine Variable heißt line und hat den Typ "Zeiger auf char". Sie ist nicht vom Datentyp char und heißt *line. Für *mich* ist meine Notation verständlicher und lesbarer.

              auf "char"?

              Wie werden denn in "C" Variablen per Referenz übergeben? Müssen sie vorher global definiert sein.

              Nein, natürlich nicht, wieso auch? Für globale Variablen gilt (sprachunabhängig): So wenige wie möglich, nur so viele, wie unbedingt notwendig. In vielen Fällen kommt man ohne globale Variablen aus.

              C verwendet call by value. Willst Du den Inhalt einer "übergebenen" Variablen ändern, so übergibst Du eben einen Zeiger auf den Speicher, in dem diese Variable steht, d.h. die (Speicher)adresse dieser Variablen.

              Typisches Beispiel für so einen Zweck ist eine Tauschfunktion:

                
              /**  
               * tausche  
               * tauscht den Inhalt der beiden übergebenen Variablen  
               *  
               * @param a: Zeiger auf die erste Zahl  
               * @param b: Zeiger auf die zweite Zahl  
               * Anmerkung: C kann maximal einen Wert zurückgeben,  
               * es müssen aber zwei Variablen geändert werden. Daher erfolgt der  
               * Aufruf über call by reference, indem Zeiger auf die Variablen übergeben  
               * werden.  
               *  
               * Code ungetestet, vermutlich wie vor etwa 20 Jahren mal in der ersten oder  
               * zweiten Woche der C-Programmierung gelernt ...  
               */  
              void tausche(int* a, int* b) {  
                  // wir benötigen einen Zwischenspeicher  
                  int merker;  
                  // schreibe den Inhalt der Variablen a in den Zwischenspeicher  
                  merker = *inhalt;   // schreibe in merker das, auf was inhalt zeigt.  
                  // schreibe in den Speicher, auf den a zeigt, das was dort steht, worauf  
                  // b zeigt  
                  *a = *b;  
                  // schreibe nun an die Stelle, auf die b zeigt, das Zwischengespeicherte.  
                  *b = merker;  
              }  
                
              // Aufruf:  
                
              // auf argc und argv verzichte ich einfach mal ...  
              int main(void) {  
                  int x = 4;  
                  int y = 17;  
                
                  // übergebe der Tauschfunktion die Adressen der zu tauschenden Variablen  
                  tausche(&x, &y);  
                
                  // gebe die beiden aus ...  
                  printf("x = %d, y = %d \n", x, y);  
                  // Ausgabe:  
                  // x = 17, y = 4  
              }  
              
              

              Wenn Du nun bei Deinem CSV-Kram von vornherein einen Puffer anlegst:

              // Wir reservieren Platz für maximal 20 Spalten. Jeder Eintrag darf maximal
              // 1024 Zeichen Nutzinhalt haben (plus ein Zeichen für das Nullbyte, das die
              // Zeichenkette beendet)
              char csv[20][1024+1];

              Wenn Du so den Speicher vorbereitest, brauchst Du keinen Speicher dynamisch anzufordern (und wieder freizugeben), dafür hast Du etwas mehr als 20 Kilobyte verbraten und bist unflexibel, weil Du nicht mehr als 20 Spalten verarbeiten kannst und kein einziger Eintrag mehr als ein Kilobyte Nutzdaten enthalten darf (sonst kommt es ruckzuck zu einem buffer overflow, wenn Du nicht aufpasst) ...

              Dagegen hat die dynamische Speicherverwaltung den Nachteil, dass *nichts und niemand* Dir die Arbeit abnimmt, den von Dir angeforderten Speicher wieder freizugeben. Da kommt es gern einmal zu Speicherlecks, d.h. die Anwendung fordert immer wieder (meist kleine) Speichermengen an, die nicht mehr freigegeben werden, obwohl sie nicht mehr verwendet werden. Viel schlimmer, Du hast wahrscheinlich keinen Zeiger mehr auf diesen Speicher und kannst ihn daher auch nicht mehr freigeben. Das erfolgt erst mit Beendigung des Programmes.

              Freundliche Grüße

              Vinzenz

              1. Hellihello

                Reine Geschmackssache, für den Compiler ergibt sich kein Unterschied.
                Meine Variable heißt line und hat den Typ "Zeiger auf char". Sie ist nicht vom Datentyp char und heißt *line. Für *mich* ist meine Notation verständlicher und lesbarer.

                Klingt logisch. Ist das auch irgendein Coding-Style?

                merker = *inhalt;   // schreibe in merker das, auf was inhalt zeigt.

                merker = *a, oder?

                Dagegen hat die dynamische Speicherverwaltung den Nachteil, dass *nichts und niemand* Dir die Arbeit abnimmt, den von Dir angeforderten Speicher wieder freizugeben. Da kommt es gern einmal zu Speicherlecks, d.h. die Anwendung fordert immer wieder (meist kleine) Speichermengen an, die nicht mehr freigegeben werden, obwohl sie nicht mehr verwendet werden. Viel schlimmer, Du hast wahrscheinlich keinen Zeiger mehr auf diesen Speicher und kannst ihn daher auch nicht mehr freigeben. Das erfolgt erst mit Beendigung des Programmes.

                Wäre hieran etwas falsch?

                  
                #include <stdio.h>  
                  
                // ist das schon eine globale variable oder nur eine definition?  
                typedef struct data_type {  
                 int age;  
                 char name[20];  
                } data;  
                  
                  
                // soll zurückgeben eine structure typdefiniert mit dem alias "data"  
                data *mymall()  
                {  
                 data *person;  
                        //was macht das (data*) davor? ein typehinting? warum das sternchen plötzlich dahinter?  
                 person = (data*) malloc( sizeof(data) );  
                 if (person !=NULL ) {  
                  person->age = 122;  
                  strcpy ( person->name, "Somename");  
                 }  
                        // nicht *person (ist ja kein array) aber auch nicht &person  
                 return person;  
                }  
                  
                int main(int argc, int *argv)  
                {  
                 data *clone_person;  
                  
                 clone_person = mymall();  
                        //wenn er jetzt die daten aus mymall zurückgibt - was "er" tut - , dann ist der test gelungen?  
                 if (clone_person !=NULL ) {  
                  printf ("%s is %d years old\n", clone_person->name, clone_person->age);  
                 }  
                        // versuch, den speicher freizugeben, da es ja ein pointer auf eine adresse ist, dürfte clone_person ja auf den selben speicherbereich zeigen wie das returnte "person", oder?  
                 free(clone_person);  
                }  
                
                

                Dank und Gruß,

                frankx

                --
                tryin to multitain  - Globus = Planet != Welt
                1. Moin,

                  merker = *inhalt;   // schreibe in merker das, auf was inhalt zeigt.
                  merker = *a, oder?

                  natürlich, da war Vinzenz wohl etwas unaufmerksam.

                  #include <stdio.h>

                  // ist das schon eine globale variable oder nur eine definition?
                  typedef struct data_type {
                  int age;
                  char name[20];
                  } data;

                  Das ist überhaupt keine Variable - du legst hier nur einen Datentyp fest, sozusagen den Bauplan für künftige Variablen dieses Typs. Warum manche Programmierer noch einen "provisorischen" Namen vor die struct-Klammer schreiben, ist mir schleierhaft; der eigentliche Typname ist das, was hinter der schließenden Klammer steht. Das vorn stehende data_type hat, soweit ich das in meiner rund 15jährigen C-Erfahrung sehe, keinen Zweck, man kann's auch ebensogut weglassen und nach typedef struct gleich die Klammer öffnen.

                  // soll zurückgeben eine structure typdefiniert mit dem alias "data"
                  data *mymall()

                  Korrekter: Einen Zeiger auf eine solche Struktur.

                  //was macht das (data*) davor? ein typehinting? warum das sternchen plötzlich dahinter?
                  person = (data*) malloc( sizeof(data) );

                  Type Hinting wäre untertrieben - der in Klammern stehende Typname ist in C die Schreibweise für ein Typecast (Typumwandlung). In diesem Fall also: Vergiss alles, was  malloc() an Typinformationen mitbringt und tu so, als sei es data*.
                  Im Fall von malloc() halte ich das aber -wenn auch formal korrekt- für Unsinn. Denn malloc() liefert sowieso den Typ void*, also "Zeiger auf einen unbekannten Typ". Ein void* ist zuweisungskompatibel zu allen anderen Zeigertypen und muss deshalb nicht extra gecastet werden. Tut man es trotzdem, macht das den Code für mich nur unübersichtlicher.

                  if (person !=NULL ) {

                  Oder einfacher und prägnanter:

                  if (person)

                  strcpy ( person->name, "Somename");
                  }
                          // nicht *person (ist ja kein array) aber auch nicht &person

                  Haha. Vom Verständnis her müsste man hier &person->name erwarten - also ein Zeiger auf das Element name innerhalb der data-Struktur, die durch person referenziert wird. Der Sprachstandard von C definiert aber: Der Name eines Arrays ist im Quellcode gleichbedeutend mit einem Zeiger auf das erste Element des Arrays. Weil das implizit per definitionem so ist, lässt man das & an der Stelle weg. Manche Compiler akzeptieren es trotzdem, andere nehmen es murrend mit einer Warnung hin.

                  clone_person = mymall();
                          //wenn er jetzt die daten aus mymall zurückgibt - was "er" tut - , dann ist der test gelungen?

                  Ja. Dann hat die Funktion myall() selbständig Speicher reserviert, mit den Angaben { 122, "Somename" } initialisiert und gibt einen Zeiger auf diesen Speicherblock zurück.

                  // versuch, den speicher freizugeben, da es ja ein pointer auf eine adresse ist, dürfte clone_person ja auf den selben speicherbereich zeigen wie das returnte "person", oder?

                  Richtig.

                  Schönes Wochenende noch,
                   Martin

                  --
                  Wer im Glashaus sitzt, sollte sich nur im Dunkeln ausziehen.
                2. Hi,

                  merker = *inhalt;   // schreibe in merker das, auf was inhalt zeigt.
                  merker = *a, oder?

                  erwischt.

                  Freundliche Grüße

                  Vinzenz

                3. Hallo,

                  Reine Geschmackssache, für den Compiler ergibt sich kein Unterschied.
                  Meine Variable heißt line und hat den Typ "Zeiger auf char". Sie ist nicht vom Datentyp char und heißt *line. Für *mich* ist meine Notation verständlicher und lesbarer.

                  Klingt logisch. Ist das auch irgendein Coding-Style?

                  tja, Du könntest Dich dran halten ...

                  // soll zurückgeben eine structure typdefiniert mit dem alias "data"

                  Da ist kein Alias. Du hast eine Funktion mymall, die einen Zeiger auf data liefert:

                  data *mymall()

                  // Ich schreib's ja lieber
                  // data* mymall()

                  {

                  data *person;
                          //was macht das (data*) davor? ein typehinting? warum das sternchen plötzlich dahinter?

                  // data* person

                  person = (data*) malloc( sizeof(data) );

                  // wird's jetzt klarer?
                  // wie Martin schon schrieb, liefert malloc void*, d.h. einen Zeiger auf
                  // void, zurück, dieser wird umgewandelt in einen Zeiger auf data, genau
                  // den Datentyp der Variablen person.
                  // void* malloc( size_t size)

                  Ich weiß schon, warum ich die Schreibweise bevorzuge, die ich bevorzuge ...

                  Freundliche Grüße

                  Vinzenz

              2. Hello,

                Nur 'ne kleine Korrektur:

                Typisches Beispiel für so einen Zweck ist eine Tauschfunktion:

                [code lang=c]

                void tausche(int* a, int* b) {
                    // wir benötigen einen Zwischenspeicher
                    int merker;
                    // schreibe den Inhalt der Variablen a in den Zwischenspeicher

                merker = *a;   // schreibe in merker das, auf was a zeigt.

                // schreibe in den Speicher, auf den a zeigt, das was dort steht, worauf
                    // b zeigt
                    *a = *b;
                    // schreibe nun an die Stelle, auf die b zeigt, das Zwischengespeicherte.
                    *b = merker;
                }

                // Aufruf:

                Liebe Grüße aus Syburg bei Dortmund

                Tom vom Berg

                --
                Nur selber lernen macht schlau
                http://bergpost.annerschbarrich.de
          2. Hellihello

              
              
            /***  
             * SplitLinesCsv  
             * splittet eine Textzeile am Komma auf,  
             * füllt die Teilzeichenketten in result  
             *  
             * @param line   Zeiger auf eine Zeichenkette  
             * @param result Zeiger auf ein Array von Zeichenketten  
             * @return       Anzahl der Teilzeichenketten  
             *  
             * Seiteneffekt: Speicher auf dem Heap wird allokiert  
             */  
            int SplitLinesCsv(char* line, char** stringlist) {  
             printf("inputline: %s\n", line);  
                /* your code goes here */  
             #define SEPARATOR ','  
             #define LINEEND '\n'  
             int totalStringSize = strlen(line);  
             int seperatorCount = 0;  
              
             printf ("der Ausgangsstring hat %d Zeichen\n", totalStringSize);  
              
                // Ermittle, wieviele Teilzeichenketten es gibt  
             while (*line++ != '\0') {  
              if (*line == SEPARATOR)  
               seperatorCount++;  
             }  
             printf ("%d mal kam das Trennzeichen ('%c') vor\n",seperatorCount, SEPARATOR);  
              
             //reset Pointer  
             line -= totalStringSize+1;  
             printf("Pointerreset Test, inputline: %s\n", line);  
              
                // Allokiere den Speicher für das notwendige Array von Zeigern auf char  
             seperatorCount++; // eins mehr als Kommas vorhanden  
             printf ("alloziere Speicher für %d Zeiger auf Strings\n", seperatorCount);  
             stringlist[0] = (char*)malloc(seperatorCount*sizeof(int));  
                // Ermittle die Teilzeichenketten  
             int stringBufferCounter = 0;  
             char stringBuffer[totalStringSize];  
             int stringlistCounter = 0;  
              
                // Allokiere für jede Teilzeichenkette den notwendigen Speicher  
             while (*line != '\0') {  
              if (*line != SEPARATOR && *line != LINEEND) {  
               printf("puffere %c",*line);  
               stringBuffer[stringBufferCounter] = *line;  
               stringBufferCounter++;  
              }  
              else {  
               stringBuffer[stringBufferCounter++] = '\0';  
               printf("gepuffert wurde %s\n", stringBuffer);  
               printf("alloziere %d*%d Speicher für stringlist[%d]\n", stringBufferCounter, sizeof(char), stringlistCounter);  
               stringlist[stringlistCounter]=(char*)malloc(stringBufferCounter*sizeof(char));  
               printf("setze stringlist[%d]\n", stringlistCounter);  
               stringlist[stringlistCounter]=stringBuffer;  
               printf("gesetzt: %s\n", stringlist[stringlistCounter]);  
               printf("in speicher: %p\n", stringlist[stringlistCounter]);  
               stringBuffer[1] = '\0';  
               stringBufferCounter = 0;  
               stringlistCounter++;  
              }  
              *line++;  
             }  
              
                // ...  
             printf("pointer: %p\n",stringlist);  
             return seperatorCount;  
            }  
              
            int main() {  
             int cellcount, i;  
             char* line = "abcd,efgh,ijkl,mnop,qrst\n";  
             char** stringlist;  
             cellcount = SplitLinesCsv(line, stringlist);  
             printf ("\ntest aus main:\n\n");  
             printf("pointer strlinglist: %p\n", stringlist);  
             printf("pointer strlinglist[0]: %p\n", stringlist[0]);  
             printf("content strlinglist[0]: %s\n", stringlist[0]);  
              
             for(i=0; i<cellcount; i++) {  
              printf("testausgabe: %d\n", i);  
              printf("pointer: %p\n", stringlist[i]);  
             }  
            }  
              
            
            

            bringt als Ausgabe:

            inputline: abcd,efgh,ijkl,mnop,qrst

            der Ausgangsstring hat 25 Zeichen
            4 mal kam das Trennzeichen (',') vor
            Pointerreset Test, inputline: abcd,efgh,ijkl,mnop,qrst

            alloziere Speicher für 5 Zeiger auf Strings
            puffere apuffere bpuffere cpuffere dgepuffert wurde abcd
            alloziere 5*1 Speicher für stringlist[0]
            setze stringlist[0]
            gesetzt: abcd
            in speicher: 0x22cc50
            puffere epuffere fpuffere gpuffere hgepuffert wurde efgh
            alloziere 5*1 Speicher für stringlist[1]
            setze stringlist[1]
            gesetzt: efgh
            in speicher: 0x22cc50
            puffere ipuffere jpuffere kpuffere lgepuffert wurde ijkl
            alloziere 5*1 Speicher für stringlist[2]
            setze stringlist[2]
            gesetzt: ijkl
            in speicher: 0x22cc50
            puffere mpuffere npuffere opuffere pgepuffert wurde mnop
            alloziere 5*1 Speicher für stringlist[3]
            setze stringlist[3]
            gesetzt: mnop
            in speicher: 0x22cc50
            puffere qpuffere rpuffere spuffere tgepuffert wurde qrst
            alloziere 5*1 Speicher für stringlist[4]
            setze stringlist[4]
            gesetzt: qrst
            in speicher: 0x22cc50
            pointer: 0x61141000

            test aus main:

            pointer strlinglist: 0x61141000
            pointer strlinglist[0]: 0x22cc50
            content strlinglist[0]: q
            testausgabe: 0
            pointer: 0x22cc50
            testausgabe: 1
            pointer: 0x22cc50
            testausgabe: 2
            pointer: 0x22cc50
            testausgabe: 3
            pointer: 0x22cc50
            testausgabe: 4
            pointer: 0x22cc50

            "stringlist[stringlistCounter]=(char*)malloc(stringBufferCounter*sizeof(char));"
            soll eigentlich jedem Element des String-Arrays einen weiteren freien Speicher zuordnen. Das ist aber immer die selbe Adresse.

            Wie man sieht, bleibt der Pointer "hängen". Was mach ich falsch? Ich dachte schon, ich blick durch, aber jetzt kurz vor Schluss den Wald vor lauter Bäumen nicht.

            Übrigens: In Zeile 58 "stringBuffer[1] = '\0'"; hatte ich zum Testen gesetzt, um die Ausgabe in Main nachvollziehen zu können (

            printf("content strlinglist[0]: %s\n", stringlist[0]);

            bring dann

            " content strlinglist[0]: q"
            ).

            Dank und Gruß,

            frankx

            --
            tryin to multitain  - Globus = Planet != Welt
            1. Hallo,

              bitte prüfe, ob die Präprozessor-Makros nicht schon definiert sind:

              if not defined SEPARATOR ','

              #define SEPARATOR ','

              //reset Pointer
              line -= totalStringSize+1;

              merktest Du Dir den Anfang, dann müsstest Du ihn nicht wieder ermitteln.

              printf ("alloziere Speicher für %d Zeiger auf Strings\n", seperatorCount);
              stringlist[0] = (char*)malloc(seperatorCount*sizeof(int));

              Viel Glück: Wenn Du Speicher für "Zeiger auf int" reservieren willst, dann solltest Du nicht die Größe von int, sondern die Größe von Zeiger auf int angeben. Mit etwas Glück sind beide gleich groß (32 Bit z.B.) Außerdem möchtest Du Dir eigentlich Zeiger auf char reservieren.

              // Leerzeichen helfen Anweisungen zu strukturieren:
              stringlist = (char*) malloc(seperatorCount * sizeof(char*));

              für den Rest habe ich momentan keine Zeit, erst am späten Abend wieder ...

              Freundliche Grüße

              Vinzenz

            2. Hallo,

              ist 'ne Menge Code so auf einmal ...

              while (*line++ != '\0') {
                if (*line == SEPARATOR)
                 seperatorCount++;
              }

              Diese Vergleichsoperation ist umständlicher als nötig.
              Eine Prüfung eines Wertes auf nicht-null ist genau das, was die if-Anweisung sowieso tut. Außerdem ist es IMHO für die Lesbarkeit des Quellcodes ungünstig, '\0' zu schreiben, wo es eine einfache 0 genauso täte. Insgesamt also:

              while (*line++) {

              //reset Pointer
              line -= totalStringSize+1;
              printf("Pointerreset Test, inputline: %s\n", line);

              Das sieht korrekt aus, nachdem du ja vorher die Stringlänge bestimmt hast; trotzdem habe ich bei dieser Operation ein ungutes Gefühl.

              stringlist[0] = (char*)malloc(seperatorCount*sizeof(int));

              Abgesehen davon, dass der Typecast (char*) den Ausdruck für den Leser unnötig verkompliziert: FEHLERQUELLE! Auf 32bit-Intel-kompatiblen Systemen geht das gut. Es ist aber nicht auf allen Systemen sizeof(int)==sizeof(*).

              Aber was noch viel schlimmer ist: Du reservierst hier nicht Speicher für stringlist, sondern für stringlist[0]! Das sollte aber schon der Zeiger auf den ersten String sein. Anhand der Logik deiner Funktion gehe ich davon aus, dass stringlist beim Aufruf undefiniert sein darf - du greifst aber schon auf das erste Element zu, auf das dieser undefinierte Zeiger zufällig verweist. Das dürfte dein entscheidender Fehler sein.

              // Ermittle die Teilzeichenketten
              int stringBufferCounter = 0;
              char stringBuffer[totalStringSize];
              int stringlistCounter = 0;

              Es ist schlechter Stil, mitten in der Funktion neue Variablen einzuführen. Das ist eine Schlamperei, die mit C++ erst aufgekommen ist; bei einem reinen C-Compiler ergibt das eine Fehlermeldung. Variablen müssen immer am Anfang eines Blocks deklariert werden.

              // Allokiere für jede Teilzeichenkette den notwendigen Speicher
              while (*line != '\0') {

              Siehe oben.

              else {
                 stringBuffer[stringBufferCounter++] = '\0';

              Auch hier: Warum '\0' und nicht 0? Schließlich ist C von Haus aus schon in manchen Punkten umständlich, man muss es ja nicht noch übertreiben. :-)

              printf("gepuffert wurde %s\n", stringBuffer);
                 printf("alloziere %d*%d Speicher für stringlist[%d]\n", stringBufferCounter, sizeof(char), stringlistCounter);
                 stringlist[stringlistCounter]=(char*)malloc(stringBufferCounter*sizeof(char));

              Hier ist die Indizierung richtig.

              printf("setze stringlist[%d]\n", stringlistCounter);
                 stringlist[stringlistCounter]=stringBuffer;

              Hier nicht. Du überschreibst den Zeiger auf den eben reservierten Speicherblock wieder mit dem Zeiger auf deinen lokalen (und damit temporären) Speicherplatz stringBuffer.
              An dieser Stelle willst du vermutlich strcpy() oder eine der Varianten davon einsetzen.

              stringBuffer[1] = '\0';

              Was will mir die Konstante 1 hier sagen?

              "stringlist[stringlistCounter]=(char*)malloc(stringBufferCounter*sizeof(char));"
              soll eigentlich jedem Element des String-Arrays einen weiteren freien Speicher zuordnen. Das ist aber immer die selbe Adresse.

              Aber nur, weil du die von malloc() gelieferte Adresse gleich wieder überschreibst.

              Wie man sieht, bleibt der Pointer "hängen". Was mach ich falsch? Ich dachte schon, ich blick durch, aber jetzt kurz vor Schluss den Wald vor lauter Bäumen nicht.

              Es sind meistens nur Kleinigkeiten ...

              Übrigens: In Zeile 58 "stringBuffer[1] = '\0'"; hatte ich zum Testen gesetzt, um die Ausgabe in Main nachvollziehen zu können (

              Ach so. Mich hat's jetzt verwirrt.

              So long,
               Martin

              --
              Wer schläft, sündigt nicht.
              Wer vorher sündigt, schläft besser.
              1. Hellihello Zusammen,

                brauch ich nicht einen char*** um den Parameter zu manipulieren?

                Ich habe versucht, das Problem zu isolieren:

                  
                /*  
                * get adress of pointer of strings (char**)  
                * and fill with a little content.  
                * parameter mystrings ist actually "&mystrings", adress of mystring  
                */  
                int fillList(void* mystrings) {  
                 //pointer to pointer of strings, to manipulate the content under the given adress  
                 char*** mystrings_tmp;  
                 // pointing to passed adress of mystrings  
                 mystrings_tmp = mystrings;  
                 //gibt some size for memory  
                 int mysize = 5;  
                 printf("----start fillList\n");  
                 printf("----before malloc\n");  
                 // some controlling/comparing of adresses and content  
                 printf("fillList: adresse &mystrings = %p\n", &mystrings);  
                 printf("fillList: adresse mystrings = %p\n", mystrings);  
                 printf("fillList: adresse &mystrings_tmp = %p\n", &mystrings_tmp);  
                 printf("fillList: adresse mystrings_tmp = %p\n", mystrings_tmp);  
                 printf("fillList: adresse mystrings_tmp[0] = %p\n", mystrings_tmp[0]);  
                 // set the conent of mystring_tmp[0], which is  
                 // to some memory (in this case space for 5 pointer to char*, only two used in example  
                 mystrings_tmp[0] = (char**) malloc(mysize * sizeof(char*));  
                 // controlling again adresses and content  
                 printf("----after malloc\n");  
                 printf("fillList: adresse &mystrings = %p\n", &mystrings);  
                 printf("fillList: adresse mystrings = %p\n", mystrings);  
                 printf("fillList: adresse &mystrings_tmp = %p\n", &mystrings_tmp);  
                 printf("fillList: adresse mystrings_tmp = %p\n", mystrings_tmp);  
                 printf("fillList: adresse mystrings_tmp[0] = %p\n", mystrings_tmp[0]);  
                 //allocating memoryspace for two strings with maxlength of 4 chars (mysize = 5 incl. \0)  
                 //setting both with some values:  
                 mystrings_tmp[0][0] = (char*) malloc(mysize * sizeof(char*));  
                 strcpy(mystrings_tmp[0][0], "abcd");  
                 mystrings_tmp[0][1] = (char*) malloc(mysize * sizeof(char*));  
                 strcpy(mystrings_tmp[0][1], "hijk");  
                 printf("----end fillList\n");  
                }  
                int main() {  
                 //my string list - empty, not initialized  
                 char** mystrings;  
                 //check Adress of var and content  
                 printf("main: adresse &mystrings = %p\n", &mystrings);  
                 printf("main: adresse mystrings = %p\n", mystrings);  
                 printf("call fillList\n");  
                 // call fillList passing adress of mystrings:  
                 fillList(&mystrings);  
                 //check Adress of var and content afterwards:  
                 printf("main: adresse &mystrings = %p\n", &mystrings);  
                 printf("main: adresse mystrings = %p\n", mystrings);  
                  
                 // testing output should b : abcd and hijk  
                 printf("main: content mystrings[0] = %s\n", mystrings[0]);  
                 printf("main: content mystrings[1] = %s\n", mystrings[1]);  
                 //free allocated space  
                 printf("free:\n");  
                 free(mystrings);  
                }  
                
                

                ergibt:

                main: adresse &mystrings = 0x22cce4
                main: adresse mystrings = 0x401560
                call fillList
                ----start fillList
                ----before malloc
                fillList: adresse &mystrings = 0x22ccc0
                fillList: adresse mystrings = 0x22cce4
                fillList: adresse &mystrings_tmp = 0x22ccb0
                fillList: adresse mystrings_tmp = 0x22cce4
                fillList: adresse mystrings_tmp[0] = 0x401560
                ----after malloc
                fillList: adresse &mystrings = 0x22ccc0
                fillList: adresse mystrings = 0x22cce4
                fillList: adresse &mystrings_tmp = 0x22ccb0
                fillList: adresse mystrings_tmp = 0x22cce4
                fillList: adresse mystrings_tmp[0] = 0x670160
                ----end fillList
                main: adresse &mystrings = 0x22cce4
                main: adresse mystrings = 0x670160
                main: content mystrings[0] = abcd
                main: content mystrings[1] = hijk
                free:

                was erstmal so ausschaut, als würde es stimmen...;

                Frage nur, ob das von hinten durch die Brust ins Auge oder ob das einfacher gingen...;

                Dank und Gruß,

                frankx

                --
                tryin to multitain  - Globus = Planet != Welt
                1. Hallo nochmal!

                  brauch ich nicht einen char*** um den Parameter zu manipulieren?

                  uffffff ...

                  Ich habe mir das Konzept nochmal genau durch den Kopf gehen lassen. Ja, im Prinzip hast du Recht. Denn du gibst einen uninitialisierten char** in die Funktion hinein, die tut irgendwas damit, aber davon bekommt die übergeordnete Funktion nix mit. Wow.
                  Ich habe solche Algorithmen bisher immer so gelöst, dass die aufrufende Funktion (caller) die Speicherreservierung vornimmt und der aufgerufenen Funktion (callee) dann einen Zeiger auf diesen reservierten Speicherbereich übergibt. Das ist einfacher zu überblicken und zu debuggen, und man kann die Funktion dann sowohl mit statisch als auch dynamisch reservierten Puffern verwenden. Nachteil: Der Speicherbedarf muss vorher bekannt sein (oder eine definierte Begrenzung haben), oder man muss Strukturen nutzen, die von sich aus eine dynamische Größe haben, etwa verkettete Listen. Einfacher ist das aber auch nicht.

                  Auf dein Beispiel übertragen könnte man das Dilemma aber umgehen, indem man die Semantik ändert. Ich würde

                  int function(char *, char **)

                  ändern in

                  char **function(char *, int *)

                  und dann den Zeiger auf den Speicherbereich, den die Funktion reserviert hat, als Funktionsergebnis zurückgeben (im Fehlerfall NULL), die Anzahl der Teilstrings dann über den int-Zeiger[*].

                  [viel Code]

                  ich habe mir jetzt nicht die Mühe gemacht, den Code im Einzelnen zu sichten, denn ich habe den Eindruck, du hast das Problem an sich erkannt. Aber bevor du da von Hölzchen auf Stöckchen kommst: Vielleicht geht's mit einem etwas anderen Ansatz einfacher.

                  //free allocated space
                  printf("free:\n");
                  free(mystrings);

                  Damit gibst du allerdings nur die Zeiger auf die Strings frei, nicht die Strings selbst.

                  So long,
                   Martin

                  [*] Wenn man es richtig machen will, prüft man innerhalb der Funktion noch, ob der int* gültig (nicht null) ist, und schreibt den Rückgabewert nur in diesem Fall. Not a bug, but a feature: Wenn die caller-Funktion die Anzahl nicht braucht, übergibt sie einfach NULL anstelle eines int*.

                  --
                  Paradox ist, wenn jemand eingefleischter Vegetarier ist.
                  1. Hellihello Martin,

                    brauch ich nicht einen char*** um den Parameter zu manipulieren?

                    uffffff ...

                    Ich habe mir das Konzept nochmal genau durch den Kopf gehen lassen. Ja, im Prinzip hast du Recht. Denn du gibst einen uninitialisierten char** in die Funktion hinein, die tut irgendwas damit, aber davon bekommt die übergeordnete Funktion nix mit. Wow.

                    Gut, dass ich die Logik scheinbar annähernd anfange zu begreifen. Im Zend-Framework arbeiten übrigens die einzelnen Funktionen (in dem Fall Klassen) auch unabhängig an einem Objekt, das dann nicht mal übergeben werden muss (ein Objekt übergeben heißt ja, die Referenz zu übergeben seit PHP5) sondern sich auch noch selber holt über die statische Methode (Singelton) einer Klasse, die das Objekt dann rausrückt. So "weiß" die aufrufende Funktion also im Grunde nicht, was die aufgerufen veranstaltet.

                    Ich habe solche Algorithmen bisher immer so gelöst, dass die aufrufende Funktion (caller) die Speicherreservierung vornimmt und der aufgerufenen Funktion (callee) dann einen Zeiger auf diesen reservierten Speicherbereich übergibt. Das ist einfacher zu überblicken und zu debuggen, und man kann die Funktion dann sowohl mit statisch als auch dynamisch reservierten Puffern verwenden.

                    Jau, das dachte ich erst auch. Drei Sterne Pointer sind für den Kopf schon relativ verschachtelt finde ich. Aber Vinzenz schlug ja vor, nur die Anzahl der Teilstrings zurückzugeben.

                    Nachteil: Der Speicherbedarf muss vorher bekannt sein (oder eine definierte Begrenzung haben),

                    Genau an dem Punkt war ich dann auch. Aber man könnte ja die Funktionen auch in Reihe schalten. Erst eine, die die Anzahl der Strings ermittelt, dann den Speicher reservieren und übergeben (oder so).

                    oder man muss Strukturen nutzen, die von sich aus eine dynamische Größe haben, etwa verkettete Listen. Einfacher ist das aber auch nicht.

                    Fein das. DoublyLinkedList gibt es als Klasse/Interface auch in der SPL von PHP https://forum.selfhtml.org/?t=175998&m=1168283. Diese Struktur ist ja nicht uninteressant.

                    Auf dein Beispiel übertragen könnte man das Dilemma aber umgehen, indem man die Semantik ändert. Ich würde

                    int function(char *, char **)

                    ändern in

                    char **function(char *, int *)

                    und dann den Zeiger auf den Speicherbereich, den die Funktion reserviert hat, als Funktionsergebnis zurückgeben (im Fehlerfall NULL), die Anzahl der Teilstrings dann über den int-Zeiger[*].

                    Jau, so hatte ich ja im Grunde versucht anzufangen.

                    "folgende funktion wird damit aufgerufen
                    char **
                    csvlinesplitting(char *line)"
                    https://forum.selfhtml.org/?t=177894&m=1172273

                    [viel Code]

                    ich habe mir jetzt nicht die Mühe gemacht, den Code im Einzelnen zu sichten, denn ich habe den Eindruck, du hast das Problem an sich erkannt.

                    Dank für die Rückmeldung. Das Gefühl bekomme ich langsam auch.

                    Aber bevor du da von Hölzchen auf Stöckchen kommst: Vielleicht geht's mit einem etwas anderen Ansatz einfacher.

                    //free allocated space
                    printf("free:\n");
                    free(mystrings);

                    Damit gibst du allerdings nur die Zeiger auf die Strings frei, nicht die Strings selbst.

                    D.h. free(mystrings[0]) und free(mystring[1]), also die Pointer auf den reservierten Speicher für die Strings. free bekommt im Grunde die Adresse übermittelt, bzw. hat sich C in dem Zusammenhang mit der Adresse gemerkt, welcher Datentyp und welche Größe damit verunden ist?

                    Könnte ich, dem Verständnis halber, "int a = (int)&mystring[1]" (weise dem integer a die adresse von mystring[1] zu) und dann free((char*)a); (befreie den Speicher der aus dem integer zurückverwandelten Adresse(Pointer))?

                    [*] Wenn man es richtig machen will, prüft man innerhalb der Funktion noch, ob der int* gültig (nicht null) ist, und schreibt den Rückgabewert nur in diesem Fall. Not a bug, but a feature: Wenn die caller-Funktion die Anzahl nicht braucht, übergibt sie einfach NULL anstelle eines int*.

                    Aha, drüber nachdenken.

                    Dank und Gruß,

                    frankx

                    --
                    tryin to multitain  - Globus = Planet != Welt
                    1. Hallo,

                      D.h. free(mystrings[0]) und free(mystring[1]), also die Pointer auf den reservierten Speicher für die Strings. free bekommt im Grunde die Adresse übermittelt, bzw. hat sich C in dem Zusammenhang mit der Adresse gemerkt, welcher Datentyp und welche Größe damit verunden ist?

                      Datentyp ist ja für das Allozieren und Freigeben von Speicher egal - malloc übergibst Du ja auch nur die Größe, der Cast davor dient nur der Klarheit.

                      Und in der Regel werden im Speicher unmittelbar *vor* dem von malloc zurückgegebenen Speicherbereich "Buchhaltungsinformationen" gespeichert (wie genau das aussieht ist Implementierungsabängig), d.h. malloc(n) macht folgendes:

                      vorher:

                      ----------------------------------------------------------------------
                                        ..............
                       ----------------------------------------------------------------------

                      nachher:

                      -----------+--------+------------------------------------------+------
                          ...     | intern | Speicherbereich                          | ...
                       -----------+--------+------------------------------------------+------
                                   ^        ^
                                   |        |
                                   |        Zeiger, der von malloc() zurückgegeben wird
                                   Intern verwendeter Speicher mit "Buchhaltungsinfos"

                      Damit kann free() wissen, wie viel Speicher wieder frei wird.

                      Bedenke: free() ist auch nur eine Funktion, die einen void*-Zeiger bekommt, d.h. irgendwo in Deiner C-Standardlib ist sowas enthalten wie:

                      void free (void *ptr) {
                       // tu was, damit der Bereich, auf den ptr zeigt, wieder frei ist
                      }

                      Könnte ich, dem Verständnis halber, "int a = (int)&mystring[1]" (weise dem integer a die adresse von mystring[1] zu) und dann free((char*)a); (befreie den Speicher der aus dem integer zurückverwandelten Adresse(Pointer))?

                      In x86: Ja.
                      In x86_64: Nein.

                      In x86 sind sowohl int als auch Speicheradressen 32bit - kein Problem.
                      In x86_64 ist in 32 bit und Speicheradressen sind 63bit - Informationen gehen also verloren.

                      Es gibt einen speziellen Zahlendatentyp, der dafür vorgesehen ist, Speicheradressen zu halten, nennt sich size_t, ist in sys/types.h definiert.

                      size_t a = (size_t)&mystring[1]; free((char*)a); würde also überall so wie von Dir gewünscht funktionieren (wäre nur reichlich sinnlos).

                      Viele Grüße,
                      Christian

                      1. Hello,

                        Datentyp ist ja für das Allozieren und Freigeben von Speicher egal - malloc übergibst Du ja auch nur die Größe, der Cast davor dient nur der Klarheit.

                        Das ist mMn genau die Stelle, an der der Übergang von klassischer Programmierung, die dann zur Not auch noch in Assembler stattfinden könnte zur OOP stattdfindet. Die Hochstprache schaltet als Zwischenstufe eine Typdefinition (Record, Struct) dazwischen.

                        Assembler      Speicher wird blockweise allokiert, Du musst selber wissen,
                                       wie der Block eingeteilt ist. Der Offset ist hier das Zauberwort

                        Hochsprache    Speicher wird pro Typelement allokiert
                                       Du hast bereits eine fertige Dereferenzierung für jedes
                                       Element der Struktur als Name in der Hochsprache
                                       Der Offset wird durch den Namen symbolisiert, beim Einfügen von Elementen
                                       kümmert sich die IDE/Compiler um die Aktualisierung des Offsets
                                       Die Struktur liegt aber immer noch als geschlossener Speicherblock vor

                        ?              dazwischen ist noch was...

                        OOP            Speicher wird für ein Objekt (Instanz einer Klasse) alloziiert
                                       Die Umsetzung der Allokation für die Elemente (Member-Variablen)
                                       kann durch das Objekt selber geschehen.
                                       Es ist also nicht mehr notwendig, dass alle Elemente des Objektes im Speicher
                                       in einer geschlossenen Form vorliegen.

                        Liebe Grüße aus Syburg bei Dortmund

                        Tom vom Berg

                        --
                        Nur selber lernen macht schlau
                        http://bergpost.annerschbarrich.de
                        1. Hellihello Tom,

                          Das ist mMn genau die Stelle, an der der Übergang von klassischer Programmierung, die dann zur Not auch noch in Assembler stattfinden könnte zur OOP stattdfindet. Die Hochstprache schaltet als Zwischenstufe eine Typdefinition (Record, Struct) dazwischen.

                          Wollte ich zum dem letzten Threaddrift zu OOP und Lanzenbrechnung verlinken, auf eine von Svens oder Vinzenz' Antworten, aber findichnicht. Vll. in der Lücke zwischen aktuellen und geschlossenen Threads?

                          Dank und Gruß,

                          frankx

                          --
                          tryin to multitain  - Globus = Planet != Welt
                          1. Hellihello

                            schlussendlich für mich die Frage, ob sich hier nicht genau wie bei PHP die Frage stellt, wie sich das am gescheitesten strukturieren lässt. Dabei meine ich nicht nur (oder vielleicht doch?) die effektive Speicherverwaltung. Sondern wie kann ein Objekt "rumgereicht werden" (am Besten als Pointer wohl, oder ja sowieso nur, oder?). Bzw. vielleicht auch, dass sie am Besten genau das tun, an einem bzw. zwei "objekten" rumzuwerkeln. Dem Input (bei PHPs Zend Framework ist das das Request-Objekt, als im Grunde ja ein Pointer auf eine Struktur) und dem Output (Respons-Objekt). Was neu für mich war, dass ein Front-Conroller zwar den Ablauf vorgibt, die bearbeitenden Klassen aber alle ganz selbständig an den beiden Singleton-Instanzen der beiden Objekte rumfummeln.

                            Dank und Gruß,

                            frankx

                            --
                            tryin to multitain  - Globus = Planet != Welt
                            1. Hello,

                              ich hoffe, dass ich Dich jetzt richt verstehe.

                              In C++ geht man davon aus, dass der gesamte Ping-Pong-Prozess zwischen Server und Client auf (einem) zustandsbehafteten, verbindungsorientierten System(en) stattfindet, also insgesamt in einem gemeinsamen zustandsorientierten System. Die Teilprozesse (wenn es mehrere sind für Client/Bedienprogrammteil und Server/Kernprogrammteil) laufen also immer weiter und können ihre Speicherstrukturen also bis zur nächtsten Inanspruchnahme beibehalten.

                              Wenn Du nun PHP im Server-Client-Umfeld benutzt, gehe ich davon aus, dass die Verbindung zwischendurch abreißt, auch wennn Du den Zustand in einer Session speicherst. Benutzht Du für die Session einen Shared Memory Block, dann dürfte es auch möglihc sein, die Speicherstrukturen weiterleben zu lassen, benutzt Du aber die klassiesche Session-Datei, dann müssen die Strukturen (Instanzen Deiner Klassen) vor dem Abriss der Verbindung immer serialisiert und abgelegt werden, bei Wiederaufnahme der Verbindung in den alten Zustand zurückversetzt werden, also deserialisiert und im Speicher plaziert werden. Da passt dann im ersten Moment natürlich kein Zeiger mehr, denn Dein vorhin genutzter Speicher wird dir jetzt nicht mehr zur Verfügung stehen, da er inzwischen für andere Prozesse benutzt wird.

                              Liebe Grüße aus Syburg bei Dortmund

                              Tom vom Berg

                              --
                              Nur selber lernen macht schlau
                              http://bergpost.annerschbarrich.de
                          2. Hellihello

                            Wollte ich zum dem letzten Threaddrift zu OOP und Lanzenbrechnung verlinken, auf eine von Svens oder Vinzenz' Antworten, aber findichnicht. Vll. in der Lücke zwischen aktuellen und geschlossenen Threads?

                            http://forum.de.selfhtml.org/archiv/2008/8/t175998/#m1172122

                            Dank und Gruß,

                            frankx

                            --
                            tryin to multitain  - Globus = Planet != Welt
                        2. Hallo Tom,

                          Das ist mMn genau die Stelle, an der der Übergang von klassischer Programmierung, die dann zur Not auch noch in Assembler stattfinden könnte zur OOP stattdfindet. Die Hochstprache schaltet als Zwischenstufe eine Typdefinition (Record, Struct) dazwischen.

                          Nein, das stimmt so nicht. Rein von der Speicherverwaltung her unterscheidet sich eine C++-Klasse und eine C-struct kein bisschen - eine C++-Klassehat halt zusätzlich noch interne Daten, die mitgespeichert werden (Referenzen auf die vtables, etc.), aber ob ich ein class Foo { public: int a; int b; }; order ein struct foo { int a; int b; }; habe ist das so gut wie das gleiche. Dagegen hat ein assoziatives Array in PHP (kommt rein semantisch einer C-Struct noch am nächsten) speicherverwaltungsmäßig überhaupt nichts mehr mit einer geschlossenen Datenstruktur zu tun. Genauso wie ein PHP-Objekt. In Java ist's dann noch mal etwas anderes.

                          Sprich: Speicherverwaltung hat *NICHTS*, aber auch gar *NICHTS* mit dem Programmierparadigma zu tun, wenn man jetzt mal von Assembler selbst absieht (das kann nämlich wirklich nur Offsets).

                          Viele Grüße,
                          Christian

                          1. Hallo nochmal,

                            aber ob ich ein class Foo { public: int a; int b; }; order ein struct foo { int a; int b; }; habe ist das so gut wie das gleiche.

                            Gerade nochmal kontrolliert: Der GNU C++ Compiler liefert sogar exakt die gleiche Speicherstruktur für die beiden Objekte. Erst, wenn ich der Klasse Foo noch zusätzliche virtuelle Funktionen hinzufüge, kommt am Anfang noch ein Zeiger auf die vtable der Klasse hinzu, das war's dann aber auch.

                            Viele Grüße,
                            Christian

                          2. Hello,

                            Nein, das stimmt so nicht. Rein von der Speicherverwaltung her unterscheidet sich eine C++-Klasse und eine C-struct kein bisschen

                            Das verstehe ich jetzt nicht oder ich habe C nicht mehr ausreichend im Kopf...

                            Darf denn eine C-Struktur auch Zeiger auf weitere, nicht initialisierte Speicherstrukturen enthalten?

                            Ich hatte das so verstanden, dass durch die Verwendung der Struktur nichts instantiiert wird, was außerhalb der Struklur liegt, sondern nur sie Speicherbereiche, die zur Struktur gehören und innerhalb eines geschlossenen Speicherbereiches untergebracht werden.

                            Kann schon sein, dass ich das noch nicht richtig einordne. Komme ich aber heute leider nichrt mehr zum Ausprobieren... Du könntest also vielleicht schon mal vorab ein Resumée ziehen?

                            Liebe Grüße aus Syburg bei Dortmund

                            Tom vom Berg

                            --
                            Nur selber lernen macht schlau
                            http://bergpost.annerschbarrich.de
                            1. Hallo,

                              Rein von der Speicherverwaltung her unterscheidet sich eine C++-Klasse und eine C-struct kein bisschen
                              Das verstehe ich jetzt nicht oder ich habe C nicht mehr ausreichend im Kopf...

                              beides möglich ... ;-)

                              Darf denn eine C-Struktur auch Zeiger auf weitere, nicht initialisierte Speicherstrukturen enthalten?

                              Aber natürlich!

                              Ich hatte das so verstanden, dass durch die Verwendung der Struktur nichts instantiiert wird, was außerhalb der Struklur liegt, sondern nur sie Speicherbereiche, die zur Struktur gehören und innerhalb eines geschlossenen Speicherbereiches untergebracht werden.

                              Äh, wie bitte? Das habe *ich* jetzt nicht verstanden.
                              Eine Struktur in C (und auch C++) ist nichts anderes als eine "geordnete" Zusammenfassung mehrerer Variablen, die dadurch automatisch zu Feldern ("members") der Struktur werden und ihrerseits beliebige komplexe Gebilde sein können - auch Arrays, weitere verschachtelte Strukturen, Zeiger auf weitere Strukturen, Zeiger auf Funktionen usw.

                              Und nichts anderes ist auch eine Klasse in C++. Nur dass bei einer Klasse implizit das erste Feld in der Struktur die vtable ist, eine Liste von Zeigern auf Funktionen, hier: auf die Methoden der Klasse (entfällt anscheinend, wenn die Klasse keine Methoden hat, wie Christian festgestellt hat).

                              So long,
                               Martin

                              --
                              F: Was macht ein Offizier, der in der Nase bohrt?
                              A: Er holt das Letzte aus sich heraus.
                      2. Hallo,

                        size_t a = (size_t)&mystring[1]; free((char*)a); würde also überall so wie von Dir gewünscht funktionieren (wäre nur reichlich sinnlos).

                        Ok, gerade nochmal gelesen und Fehler entdeckt: Das würde *nicht* funktionieren, das wäre Blödsinn. Folgendes würde gehen:

                        size_t a = (size_t)mystring[1]; free((char*)a);

                        &mystring[1] liefert Dir nämlich vom Datentyp her ein char ** (wenn mystring[1] ein char *) ist - aber keines, das durch malloc() alloziert wurde (das war ja mystring bzw. &mystring[0], das halt mit einer gewissen Größe, damit man den Bereich als Array nutzen kann) - wenn Du das durch free() jagst, gibt's schöne Speicherallokationsbugs, die dann irgendwann wenn das in einer ungünstigen Konstellation passiert zu lustigen Sicherheitslücken führen.

                        Viele Grüße,
                        Christian

          3. Hellihello

              
            #include <stdio.h>  
            #include <stdlib.h>  
            #include <string.h>  
              
              
            #ifndef LINELEN  
            #define LINELEN 80  
            #endif  
              
            #ifndef SEPARATOR  
            #define SEPARATOR ','  
            #endif  
              
            #ifndef LINEEND  
            #define LINEEND '\n'  
            #endif  
              
            /***  
             * getCommaCount  
             * zählt die Anzahl der Kommata  
             *  
             * @param line   Zeiger auf eine Zeichenkette  
             * @return       Anzahl der Kommata  
             */  
            int getCommaCount(char* line) {  
             int commaCount = 0;  
             while(*line++) {  
              if(*line== SEPARATOR) {  
               commaCount++;  
              }  
             }  
             return commaCount;  
            }  
              
            /***  
             * setCells  
             * zählt die Anzahl der Kommata  
             *  
             * @param line   Zeiger auf eine Zeichenkette  
             */  
            int setCells(char** result, char* line) {  
             int charCount = 0;  
             int cellCount = 0;  
             while(*line) {  
              if(*line!= SEPARATOR && *line!= LINEEND) {  
               result[cellCount][charCount] = *line;  
               charCount++;  
              } else {  
               result[cellCount][charCount] = 0;  
               cellCount++;  
               result[cellCount] = result[cellCount-1] + (charCount+1)*sizeof(char*);  
               charCount=0;  
               }  
              *line++;  
             }  
            }  
              
              
            /***  
             * SplitLinesCsv  
             * splittet eine Textzeile am Komma auf,  
             * füllt die Teilzeichenketten in result  
             *  
             * @param line   Zeiger auf eine Zeichenkette  
             * @param result Adresse eines Zeigers auf ein Array von Zeichenketten  
             * @return       Anzahl der Teilzeichenketten  
             *  
             * Seiteneffekt: Speicher auf dem Heap wird allokiert  
             */  
            int SplitLinesCsv(char* line, void* result) {  
             int cellCount = 0;  
             int lineLength = strlen(line);  
             char*** pointerToResult = result;  
              
             //~ printf("[SplitLinesCsv] result as int: '%d'\n", result);  
             // Ermittle, wieviele Teilzeichenketten es gibt  
             cellCount = getCommaCount(line) + 1; // one more than commas  
              
             // Allokiere den Speicher für das notwendige Array von Zeigern auf char  
             *pointerToResult = malloc(cellCount * sizeof(char*));  
              
             // Allokiere für jede Teilzeichenkette den notwendigen Speicher  
              *pointerToResult[0]=malloc(lineLength * sizeof(char)); //allocated memory will not be longer than orginial line;  
              
             // Ermittle die Teilzeichenketten  
                // ...  
             setCells(*pointerToResult, line);  
             return cellCount;  
            }  
              
            int  
            main()  
            {  
             char* line = "nickname,vorname,nachname,1970\n";  
             char** result;  
             int cellCount;  
             printf("[main] &result as int: '%d'\n", &result);  
             //~ printf("[main] result as int: '%d'\n", result);  
             cellCount = SplitLinesCsv(line, &result);  
             while (cellCount > 0) {  
              printf("[main] result[%d] as string: '%s'\n", cellCount-1, result[cellCount-1]);  
              cellCount--;  
             }  
             return 0;  
            }  
              
            
            

            wäre ein Veruch hierzu.

            komisch nur, dass ich

            "printf("[main] &result as int: '%d'\n", &result);" in main starten muss, oder aber
            ohne den "&" oder aber in SplitLineCSV "printf("[SplitLinesCsv] result as int: '%d'\n", result);".

            Sonst kommt am Ende murx raus:

            [main] result[3] as string: '1970'
            [main] result[2] as string: ''1970'
            '
            [main] result[1] as string: 'kreismitpfeil'
            [main] result[0] as string: 'nickname'

            statt wie eigentlich erwartet:

            [main] &result as int: '2280672'
            [main] result[3] as string: '1970'
            [main] result[2] as string: 'nachname'
            [main] result[1] as string: 'vorname'
            [main] result[0] as string: 'nickname'

            und der Unterschied nur wegen eines "printf()", allerdings mit result als Parameter. Wird das dadurch irgendwie "initialisiert"? Komisch, dass das dicke Ende dann doch immer nochmal kommt.

            Dank und Gruß,

            frankx

            --
            tryin to multitain  - Globus = Planet != Welt
      2. Hellihello

        char **     // das verstehe ich nicht? Wozu steht das da?
                      // Soll das der Typ (Rückgabewert) der Funktion sein?

        Syntaktisch gesehen ja. Darüber habe ich mich auch gewundert. Also möchte frankx eigentlich einen Zeiger auf Zeiger auf char zurückgeben - im Prinzip ein Array aus Strings.
        Das passt aber nicht so ganz mit der Arbeitsweise der Funktion zusammen, außerdem hat er ohnehin versäumt, ein Ergegbnis zurückzuliefern.

          
        char **  
        csvlinesplitting(char *line)  
        {  
                int elemcounter;  
                elemcounter = 0;  
                int counter;  
                counter = 0;  
                char elements[5][120];  
                printf("Elements am Anfang: %s\n\n", elements);  
                while(*line != '\n') {  
                        if (*line != ',') {  
                                elements[elemcounter][counter] = *line;  
                                elements[elemcounter][counter+1]=0;  
                                counter++;  
                        }  
                        else {  
                                printf("\n\nes ist ein komma da: %c - elementsinhalt ist : %s\n", *line, elements[elemcounter]);  
                                printf("lineinhalt ist : %s\n", line);  
                                counter=0;  
                                elemcounter++;  
                                elements[elemcounter+1][0]=0;  
                        }  
                        line++;  
          
                }  
                printf("\n\nENDE: %c - elementsinhalt ist : %s\n", *line, elements[elemcounter]);  
                return elements;  
        }  
        
        

        mault er

        csv2.c:35: warning: return from incompatible pointer type
        csv2.c:35: warning: function returns address of local variable

        mach ich "return **elements";

        csv2.c:35: warning: return makes pointer from integer without a cast

        in main dann:

          
        //init **var  
        char **main_elements;  
        // sollte die rückgabe von der Funktion csvlinesplitting übernehmen  
        main_elements = csvlinesplitting(char *line);  
        
        

        geht wohl analog zu JS oder PHP nicht, oder?

        Dank und Gruß,

        frankx

        --
        tryin to multitain  - Globus = Planet != Welt
        1. Hello,

          mault er

          csv2.c:35: warning: return from incompatible pointer type
          csv2.c:35: warning: function returns address of local variable

          Da würde ich auch maulen.

          Du erzeugst innerhalb der Funktion eine Struktur auf dem Stack.
          Zu gibst den Zeiger auf diese Struktur als Ergebnis zurück. Für den Zeiger wurde auch mit dem Funktionsstart ein Platz auf dem Stack freigehalten, aber nicht für eine Struktur unbekannter Größe.

          Der Lebensbereich der Struktur endet mit der Funktion. Der Zeiger zeigt daher hinterher auf einen Speicherbereich, der ihm nicht mehr gehört.

          Baue die Struktur auf dem Heap auf. Dann lebt sie auch nach dem Ende der Funktion weiter. Allerdings musst Du dann später auch selber dafür sorgen, dass der Speicher wieder freigegeben wird.
          Du musst Dir also den Zeiger auf jedes dynamisch erzeugte Element merken.

          Oder baue Dir vorher eine Leerstruktur auf, die Du benutzen darfst. Dann musst Du aber auf die Größe achten, so wie Martin das vorhin gezeigt hat.

          Liebe Grüße aus Syburg bei Dortmund

          Tom vom Berg

          --
          Nur selber lernen macht schlau
          http://bergpost.annerschbarrich.de