dynamische mehrdimensionale Arrays [C]



  • Hallo cplusplus community,

    ich hoffe es ist nicht unhöflich, wenn ich mich nicht vorstelle, außer das ich schon die allgemeinen Basics von C beherrsche/eingeübt bin(mehr oder weniger 😃 ) und ich schildere euch gleich mein Problem.

    Es geht um zweidimensionale Arrays, welche ich in Structures mit Pointer darstellen will:

    struct Member
    {
    char *member;
    char **membername;
    };
    

    es geht mir jetzt darum, das member ein zweidimensionales Array des Datentyps char ist, ich stelle es mir so vor, das membername auf member zeigt und ich dadurch einen Pointer habe, wo ich die namen reinschreibe und einen pointer, welcher halt den pointer beinhaltet wo der name ist.

    int main()
    {
    struct Member *m;
    
    m->member = malloc(8 * sizeof(char*)); // für 8 Member
    m->membername = malloc(20 * sizeof(char)); //20 zeichen für name
    m = malloc(sizeof(struct Member));
    
    m->membername =& member;
    
    return 0;
    }
    

    //die Abfrage nach der Überprüfung der speicheranforderung spar ich mir hier 🙂

    und um das ganze jetzt etwas handlicher zu machen, habe ich mir eine Funktion dafür vorgesehen, welche den namen den Members zuweist und für mich somit ein pointer -> pointer -> pointer logisch erscheint, welcher auf den pointerpointer des namenspointers zeigt :), jedoch wenn schon ein member einen namen hat, somit wird der nächste member genommen

    void setName(struct Member *m, char ***member)
    {
    
    member =& m->membername;
    
    while(m->member)
    {
    m->member++;
    }
    
    return(member);
    }
    

    //abfragen zur überprüfung von m werden wieder gespart 🙂

    hier mein Problem: das Programm lässt sich kompilieren aber irgendwas scheint wohl mit den pointer schief gegangen zu sein oder vielleicht die speicherzuweisung

    Der ganze Quellcode:

    #include <stdio.h>
    
    struct Member
    {
    char *member;
    char **membername;
    };
    
    void setName(struct Member *m, char ***member)
    {
    
    member =& m->membername;
    
    while(m->member)
    {
    m->member++;
    }
    
    return(member);
    }
    
    int main()
    {
    struct Member *m;
    
    m->member = malloc(8 * sizeof(char*)); // für 8 Member
    m->membername = malloc(20 * sizeof(char)); //20 zeichen für name
    m = malloc(sizeof(struct Member));
    
    m->membername =& member;
    
    setName(m, "Name");
    printf("%s", m->member);
    
    return 0;
    }
    


  • Ohje, lass es lieber.
    Nimm ein normales Array:

    enum{ANZAHL_MEMBER=8,MAX_NAME=20};
    
    int main()
    {
      char m[ANZAHL_MEMBER][MAX_NAME+1]={0};
    
      ...
    
      strcpy(m[0],"bla");
      strcpy(m[1],"fasel");
    
      ...
    
      for(int i=0;i<ANZAHL_MEMBER;++i) puts(m[i]);
    
      ...
    }
    


  • Du kommst mit den * durcheinander.

    struct Member
    {
    char *member;
    char **membername;
    };
    
    struct Member *m;
    
    m->member = malloc(8 * sizeof(char*)); // Dann sollte member aber ein char** sein
    m->membername = malloc(20 * sizeof(char)); // dafür reicht für membername ein char* aus
    m = malloc(sizeof(struct Member)); // Das kommt zu spät, da du ja schon in den Zeilen vorher auf die Elemente zugreifst.
    
    m->membername =& member;  // Hiermit geht der Speicher aus Zeile 10 verloren. Den bekommst du nicht wieder.
    
    return 0;
    }
    


  • Wutz ich denke da hast du Recht, mit normalen Arrays hätte man diese Probleme nicht 🙂 trozdem möchte ich dieses Problem irgendwie lösen 😃

    DirkB danke für den Hinweis, ich habe es irgendwie ganz verschlafen, das ich durch die Funktion einen pointer dazurechnen sollte

    ist jedoch bei richtiger speicherzuweisung die Funktion richtig?

    void setName(sturct Member *m, char ***name)
    {
    
    name =& m->membername;
    
    while(m->member)
    {
    m->member++; // ist diese vorgehensweise richtig?
    }
    return(name);
    }
    
    int main()
    {
    
    m = malloc(sizeof(struct Member));
    m->member= malloc(sizeof(char**));
    m-membername = malloc(sizeof(char*));
    
    m-membername =& m->member;
    
    setName(m, "Name");
    printf("%s", m->member);
    
    return 0;
    }
    


  • Deine struct ist schon falsch. Zumindest zu dem Vorhaben, wie du es erklärt hast.

    struct Member
    {
    char **member;
    char *membername;
    };
    

    Und beachte auch unbedingt die Compiler-Warnungen nicht nur die Fehler.

    void setName(sturct Member *m, char ***name)
    {
    ...
    return(name);  // Dann merkst du auch so was. return mit Wert bei einer void-Funktion
    }
    
    m->member++; // ist diese vorgehensweise richtig?
    

    Nein, denn meber zeigt auf einen mit malloc besorgten Speicherbereich.
    Die Information wo dieser liegt geht dadurch verloren.
    Dann funktioniert das free() nicht mehr und du bekommst Speicherlecks. Dein Programm/Computer stürzt ab.



  • dimi_C schrieb:

    trozdem möchte ich dieses Problem irgendwie lösen 😃

    das ist auch gut so, denn es gibt fälle, in denen ein 'nomrales' array
    nicht ausreicht.
    prinzipiell kannst du das mit einem char** machen.
    hier ohne prüfung der rückgabewerte

    char** names = malloc ( 10 * sizeof(char* ); // für 10 namen
    for ( i=0; i < 10; i++ )
        names[i] = malloc(16); // für einen namen mit maximal 15 zeichen.
    

    verwendung

    strcpy(names[0], "sinnvollernick");
    strcpy(names[1], "gustav gans");
    puts(names[0]);
    

    sicherlich musst du die anzahl irgendwo speichern, das alles lässt sich gut in einer struktur unterbringen.
    sieht dann auch nicht viel anders aus

    meine_instanz.names = malloc ...
    

    weiterhin happy coding.
    mfg



  • Mit "normalen Arrays" hätte man nicht nur diese Probleme nicht, das Ganze wäre auch noch schneller... 😉



  • alles klar ich habe mein Problem verstanden

    ich werde es mit einer matrix machen

    int i = 7;
    
    char **member = malloc(8 * sizeof(char*));
    
    while(i--)
    {
    member[i] = malloc(20 * sizeof(char));
    }
    

    somit wird ein speicherblock erstellt mit einem member mit 20 char

    somit ist das hauptproblem eigentlich erledigt,

    viel dank DirkB



  • vielen dank für euere Hilfe 😃



  • dot schrieb:

    Mit "normalen Arrays" hätte man nicht nur diese Probleme nicht, das Ganze wäre auch noch schneller... 😉

    die 'normalen arrays' kann man aber nicht immer verwenden, z.b.
    bei großen datenmengen.



  • Ich meinte damit natürlich einen einfachen, linearen Speicherbereich anstatt sowas fragmentiertes...



  • Für chars ist das ziemlich einfach in einem Stück zu haben:

    char **two_dimensional_char_array(size_t x, size_t y) {
      size_t i;
      char **index;
    
      index = malloc(sizeof(char*) * y + y * x);
    
      if(index != NULL) {
        for(i = 0; i < y; ++i) {
          index[i] = (char*) (index + y) + i * x;
        }
      }
    
      return index;
    }
    


  • Jepp nen linearen, zusammenhängenden Speicherbereich bekommt man ziemlich einfach.
    Der Speicherbedarf für die Zeiger typs char** kann eingespart werden.
    Die Indizierung geht in diesem Fall ein wenig anders als bei der 'zweidimensionalen' Allokation, hmm... umständlicher.
    Dafür hat man weniger mallocs und frees.

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define NAMELEN_MAX 63
    
    int main(void)
    {
    	const size_t iterator_distance = NAMELEN_MAX + 1;
    	size_t names_count = 8, i;
    	char* linear_block = calloc ( names_count * ( NAMELEN_MAX + 1 ), 1 );
    
    	if ( linear_block == NULL )
    		return 0;
    
    	for ( i = 0; i < names_count; i++ )
    	{
    		char buf [ NAMELEN_MAX+1 ];
    		sprintf_s ( buf, sizeof ( buf ), "%u %s", i, "Johnny Flitzpiepe" );
    		strncpy ( linear_block + i * iterator_distance, buf, NAMELEN_MAX );
    	}
    
    	for ( i = 0; i < names_count; i++ )
    	{
    		puts ( linear_block + i * iterator_distance );
    	}
    
    	free ( linear_block );
    	system ( "pause" );
    
    	return 0;
    }
    


  • CJosef schrieb:

    const size_t iterator_distance = NAMELEN_MAX + 1;
        size_t names_count = 8, i;
        char* linear_block = calloc ( names_count * ( NAMELEN_MAX + 1 ), 1 );
     
    ...
       
        for ( i = 0; i < names_count; i++ )
        {
    ...
            strncpy ( linear_block + i * iterator_distance, buf, NAMELEN_MAX );
        }
    

    Furchtbar.

    sinnvollernick schrieb:

    char** names = malloc ( 10 * sizeof(char* ); // für 10 namen
    for ( i=0; i < 10; i++ )
        names[i] = malloc(16); // für einen namen mit maximal 15 zeichen.
    

    Furchtbar.

    dimi_C schrieb:

    int i = 7;
    
    char **member = malloc(8 * sizeof(char*));
    
    while(i--)
    {
    member[i] = malloc(20 * sizeof(char));
    }
    

    somit wird ein speicherblock erstellt mit einem member mit 20 char

    1 Speicherblock ja, aber noch nicht mal zusammenhängend und mit 8x malloc realisiert.
    Wenn man wie in deinem Fall eine konstante (max.) Stringlänge voraussetzen kann, nutzt man das aus und kommt mit 1x calloc UND einfacher Array-Schreibweise aus:

    char (*a)[21]=calloc(8,sizeof*a);
    int i;
    for(i=0;i<8;++i) *a[i]='1'+i;
    for(i=0;i<8;++i) puts(a[i]);
    ...
    free(a); /* auch nur 1x free notwendig */
    


  • Wutz schrieb:

    Furchtbar.

    Kommt da noch ne Begründung, oder willst du einfach nur wieder mal deinen Frust abladen?



  • Wutz schrieb:

    char (*a)[21]=calloc(8,sizeof*a);
    int i;
    for(i=0;i<8;++i) *a[i]='1'+i;
    for(i=0;i<8;++i) puts(a[i]);
    ...
    free(a); /* auch nur 1x free notwendig */
    

    Woher weiß free(a) denn eigentlich, dass das num-Argument des calloc eine 8 war, sprich, dass da 8 sizeof(*a) hintereinanderliegen und nicht eins oder 29786345?


  • Mod

    Schock schrieb:

    Woher weiß free(a) denn eigentlich, dass das num-Argument des calloc eine 8 war, sprich, dass da 8 sizeof(*a) hintereinanderliegen und nicht eins oder 29786345?

    Weil das malloc sich das gemerkt hat.



  • Merkt sich malloc "beliebig viel" oder nur seinen letzten Aufruf, sprich wäre Folgendes ok?

    int * a = Calloc(1,5);
    int * b = Calloc(2,5);
    int * c = Calloc(3,5);
    
    free(a);
    free(b);
    free(c);
    

    Wenn ja - wie macht es das, legt es sich eine dynamische Liste an, hat es einen vorgegebenen Buffer?





  • Vielen Dank. Ich hatte nur nach "wie funktioniert free()" gegoogelt; aber jetzt weiß ich ja, wer mir freundlich und kompetent zur Seite steht. 🤡


Log in to reply