Verständnisfrage zu char*[]



  • Hallo Leute,

    ich habe vor kurzem angefangen C++ zu lernen (Bachelor Informatik Studium) und komme mit Pointern noch nicht ganz klar. Vom programmieren per se habe ich Ahnung (einige Jahre mit C#), es geht also nur um reines C++ Verständnis.

    Aber nun zu meiner eigentlichen Frage:
    Ich habe ein Übungsaufgabe, in der ich alle Wörter aus einem String erkennen und zählen soll.
    Die vorgegene Methodensignatur sieht so aus:

    int breakIntoWords(char *line, int maxwords, char *words[]);
    

    line ist der String, der untersucht werden soll.
    maxwords gibt die Arraylänge von words an.
    words ist das Array, in das die einzelnen Wörter geschrieben werden sollen.
    Der Rückgabewert ist die Anzahl der Wörter im String.

    Gehe ich recht in der Annahme dass *words[] ein Array von char-Pointern ist und kein Pointer auf ein char-Array?
    Wenn ja würde dass ja bedeuten, dass ich innerhalb des Arrays nur die Pointer ändern kann und nicht den zugehörigen String, oder?
    Mein Problem ist nun folgendes: wenn ich jetzt in diesem Array einfach einen Zeiger auf das zugehörige Wort im String habe, dann zeigen die ganzen Pointer ja auf Teilstrings, die dann zwar an dem entsprechenden Wort beginnen würden, aber halt den Rest des Satzes (oder Strings) auch, weil je kein Escapezeichen (\0) am Ende des Wortes steht.

    Ein Beispiel:
    Angenommen der zu untersuchende String ist folgender: "Das ist ein Satz"
    Würden die Wörter richtig erkannt, hätte ich 4 Pointer in dem Array. Beim ausgeben (z.B. printf) würde die Ausgabe doch so aussehen:
    p1 -> "Das ist ein Satz"
    p2 -> "ist ein Satz"
    p3 -> "ein Satz"
    p4 -> "Satz"
    weil ja nur ein Escapezeichen ganz am Ende steht.

    Ich müsste ja eigentlich von jedem gefundenen Wort eine Kopie anlegen und an diese dann noch ein '\0' anhängen, damit das Wort auch zu einem gültigen String wird. Und genau dass kann ich mit einem char*[] nicht machen.
    Ist das so korrekt?



  • Code4Fun schrieb:

    Gehe ich recht in der Annahme dass *words[] ein Array von char-Pointern ist und kein Pointer auf ein char-Array?

    Weder noch. Das ist ein Zeiger auf einen Zeiger auf char . Das ist das gleiche wie char **words .

    Code4Fun schrieb:

    Ich müsste ja eigentlich von jedem gefundenen Wort eine Kopie anlegen und an diese dann noch ein '\0' anhängen, damit das Wort auch zu einem gültigen String wird. Und genau dass kann ich mit einem char*[] nicht machen.
    Ist das so korrekt?

    Doch, das geht über den Indexoperator.

    words[0] = malloc(123);
    

    Vielleicht hilft es dir für den Anfang, wenn du ein typedef verwendest:

    typedef char *c_string;
    int breakIntoWords(c_string line, int maxwords, c_string *words);
    

  • Mod

    Warum ist line nicht char const* ?



  • Arcoth schrieb:

    Warum ist line nicht char const* ?

    So kann er in line Whitespace durch '\0' ersetzen und die Wortadressen in words sammeln - strtok -mäßig.



  • Alles klar hab's jetzt verstanden.
    Funktioniert wunderbar.
    Vielen Dank.

    /// Nachtrag
    Warum kann ich words dann nicht einfach so initialisieren:

    char words[maxwords][20]
    

    Wenn ich es so initialisiere und nach char** caste, bekomme ich einen Zugriffsfehler 😕



  • 1. Das ist keine Initialisierung sondern eine Definition.
    2. Du brauchst kein array of char[20] sondern ein array of char *



  • 1. Ja stimmt ist eine Definition
    2. Wenn char* [] das gleiche ist wie char** warum ist dann char** nicht das gleiche wie char[][] ??



  • // edit: Warning - 50% bullshit inside.

    char * [] ist nicht dasselbe wie ein char ** aber ein char * [] zerfällt in einen char ** . Ebenso ist ein char [][] kein char ** aber zerfällt in einen.

    Wenn du aber die Arrayelemente von words auf Wortanfänge in line zeigen lassen willst, dann brauchst du ein Array of Pointers to char und kein Array of Arrays of char .



  • char * [] ist nicht dasselbe wie ein char ** aber ein char * [] zerfällt in einen char **. Ebenso ist ein char [][] kein char ** aber zerfällt in einen.

    Ich weiß nicht, was zerfällt in diesem Zusammenhang bedeuten soll?

    char** und char[][] sind zwei verschiedene Typen, die nicht kompatible sind.

    char* und char[] sind zwei verschiedene Typen, die kompatible sind und implizit konvertiert werden können.



  • coder777 schrieb:

    char** und char[][] sind zwei verschiedene Typen, die nicht kompatible sind.

    Ouch. Ja 👍



  • Also jetzt bin ich verwirrt 😕

    TyRoXx schrieb:

    Code4Fun schrieb:

    Gehe ich recht in der Annahme dass *words[] ein Array von char-Pointern ist und kein Pointer auf ein char-Array?

    Weder noch. Das ist ein Zeiger auf einen Zeiger auf char . Das ist das gleiche wie char **words .

    das soll doch heißen, dass char*[] das gleiche ist wie char** , oder?

    coder777 schrieb:

    char** und char[][] sind zwei verschiedene Typen, die nicht kompatible sind.

    char* und char[] sind zwei verschiedene Typen, die kompatible sind und implizit konvertiert werden können.

    Wenn ich also ein string in Form von char* text; habe, und ich greife mit text[i] darauf zu, dann geht das, weil eine implizite Konvertierung statt findet?
    Und wenn das jetzt bei char** nicht funktioniert, wie greife ich dann auf die einzelnen Elemente zu?

    char** text;
    *(*(text + i) + j) = 'a'
    

    So? Jetzt bin ich vollkommen durcheinander 😃

    Worin unterscheiden sich denn char** und char[][] und warum sind diese nicht kompatiebel zueinander?


  • Mod

    char[][] gibt es nicht.



  • C(++) ist an der Stelle etwas verwirrend.

    #include <type_traits>
    
    //so sind das verschiedene Typen
    static_assert(not std::is_same<int *, int[]>::value, "");
    
    void f(int p[], int *q)
    {
      //und so ist das derselbe Typ!
      static_assert(std::is_same<decltype(q), decltype(p)>::value, "");
      static_assert(std::is_same<int *, decltype(p)>::value, "");
    
      int *p2 = p;
    
      //kompiliert nicht
      //int p3[] = p2;
    
      //geht aber mit einem echten Array
      int a[] = {1};
    }
    

    Es gibt übrigens gar kein char[][] .
    GCC sagt das dazu:

    void f(char c[][])
    
    error: declaration of 'c' as multidimensional array must have bounds for all dimensions except the first
    


  • Ja, char[][] gibt es nicht.

    Ich habe es einfach als Abkürzung für

    char Irgendwas[Irgendeinwert][Nocheinwert];

    genommen.

    Wenn man nun schreibt

    char **ZeigerZeiger = Irgendwas;

    wird sich der Compiler weigern. Der Punkt ist, dass die Daten eines Zeigers auf einen Zeiger eben anders organisiert sind als in einem zweidimensionalen Array.

    Wenn man folgendes macht:

    char **ZeigerZeiger = (char **) Irgendwas;

    wird man unweigerlich Schiffbruch erleiden.

    Man kann durchaus mittels ZeigerZeiger[i][j] auf den Inhalt zugreifen, aber die Berechnung um auf das einzelne Feld zu kommen ist eine andere als beim zweidimensionalen Array.


  • Mod

    In C++ können Objekttypen vollständig (definiert) oder unvollständig sein.
    Ein vollständiger Objekttyp verfügt u.a. über eine bekannte Größe und bekanntes Alignment, sizeof und align sind darauf anwendbar.
    Für unvollständige Typen gilt das nicht, und davon gibt es wiederum 2 Arten: solche die vervollständigt werden können, und solche die niemals vollständig sind.
    Typisches Beispiel für Ersteres ist eine Klasse, die deklariert, aber noch nicht definiert wurde.
    Typisches Beispiel für Leteres ist der Typ void.

    Kommen wir zu Arrays.
    Ein Array verfügt über einen Elementtyp und (optional) über eine feste Elementanzahl.
    Elementtyp kann jeder Typ sein, der vollständig ist oder vervollständigt werden kann.
    Ein Array mit bekannter Größe ist vollständig, wenn der Elementtyp vollständig ist; andernfalls unvollständig. Ein solcher unvollständiger Arraytyp wird vollständig, wenn der zugrundeliegende Elementtyp vollständig wird (also die betreffende Klasse definiert wird, bzw. rekursiv das Elementárray vollständig wird).

    Ein Arraytyp ohne bekannte Größe ist ein unvollständiger Typ, der nicht vervollständigt werden kann. Ein solcher Typ kann folglich niemals als Elementtyp eines anderen Arrays auftreten. Ausserdem ergeben sich ein paar interessante (z.T. ODR-relevante) Aspekte:

    extern char x[];
    using T = decltype(x);
    extern char x[1];
    using U = decltype(x);
    static_assert( !std::is_same<T,U>{} );
    

    Zu beachten ist, dass die Nichtvervollständigbarkeit dieser Typen kein Hindernis bei der Bildung von Zeigern ist.
    void* oder char(*)[] sind kein Problem.

    Sonderregel Funktionsdeklarationen:

    Wird in einer Funktionsdeklaration (und nur dort) der Typ eines Parameters als Array spezifiziert, so wird diese Spezifikation in die eines Zeigers auf den Elementtyp angepasst (die Arrayspezifikation muss immer noch legal sein: char[][] ist nicht erlaubt, obwohl char(*)[] möglich ist). Analog werden Paramter, die Funktionstyp haben, in Zeiger auf Funktionen angepasst. Eine vergleichbare Anpassung des Rückgabetypen erfolgt nicht, eine entsprechende Funktionsdeklaration, mit einem Array- oder Funktionsrückgabetyp ist schlicht illegal.
    Diese Anpassung bedeutet nicht, dass Arrays mit Zeigern gleichzusetzen wären. Sie ist historisch bedingt, da C mit Funktionen und Arrays als Parameter nicht umgehen kann, es aber bequem sein kann, deren Deklarationssyntax im entsprechenden Kontext zu verwenden. Der entsprechenden Funktionsdeklaration wird somit nur eine vernünftige Bedeutung gegeben, da sie andernfalls schlicht fehlerhaft wäre.


  • Mod

    coder777 schrieb:

    Ja, char[][] gibt es nicht.

    Ich habe es einfach als Abkürzung für

    char Irgendwas[Irgendeinwert][Nocheinwert];

    Und das ist keine gute Idee, eben weil [] bereits eine andere Bedeutung hat.



  • Okay ich denke jetzt habe ich es begriffen.

    Falls nicht werdet ihr in den kommenden Tagen nochmal von mir hören 😃

    Vielen Dank euch allen.


Log in to reply