dynamische mehrdimensionale Arrays [C]



  • 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. 🤡



  • Wutz schrieb:

    [...]
    Furchtbar.

    Das ist gängige C-Praxis.
    Wenn es dir das Fürchten lehrt, dann ist C anscheinend nichts für dich.



  • Das ist gängige C-Pfuscherpraxis.
    Wie man es für den Fall vernünftig macht, habe ich gezeigt, hast du aber nicht verstanden.
    Lerne erstmal C.
    Lerne erstmal Deutsch. (Dativpronomen) Davon hast du nämlich auch keine Ahnung.
    Troll dich.



  • Wutz schrieb:

    Das ist gängige C-Pfuscherpraxis.
    Wie man es für den Fall vernünftig macht, habe ich gezeigt, hast du aber nicht verstanden.
    Lerne erstmal C.
    Lerne erstmal Deutsch. (Dativpronomen) Davon hast du nämlich auch keine Ahnung.
    Troll dich.

    im gegensatz zu dir, liebes wutz, bin ich nicht von einer im voraus bekannter, maximaler stringlänge ausgegangen.
    klar, habe ich konstanten, 16, 10 benutzt.
    das man diese aber durch variablen ersetzen kann, kapiert jeder anfänger nach wenigen minuten.
    tja, bloß du offensichtlich nicht.


  • Mod

    no-- schrieb:

    im gegensatz zu dir, liebes wutz, bin ich nicht von einer im voraus bekannter, maximaler stringlänge ausgegangen.
    klar, habe ich konstanten, 16, 10 benutzt.
    das man diese aber durch variablen ersetzen kann, kapiert jeder anfänger nach wenigen minuten.
    tja, bloß du offensichtlich nicht.

    Das ist jetzt aber ein Eigentor. Denn du hast wohl wirklich nicht verstanden, was daran furchtbar ist. Angenommen die Konstanten 10 und 16 wären Variablen:

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

    Was ändert sich dadurch daran, dass du hier (size_x + 1) mallocs benutzt, wo nur eines nötig wäre?



  • lieber herr moderator seppj,
    mein ausgangspunkt war:
    es sollen y namen eingegeben werden.
    die anzahl der namen ( hier: y ) sowie deren maximale stringlänge ( hier: x )
    sind zur compilezeit noch nicht bekannt.
    mein ansatz war dafür sinngemäß:

    // x, y sind irgendwo deklarierte variablen, deren inhalt der benutzer
    // _zur laufzeit_ bestimmt
    char** names = malloc ( y * sizeof( char* )); // für y namen
    for ( i = 0; i < y; i++ )
        names[i] = malloc ( x + 1 ); // für einen namen mit maximal x zeichen.
    

    meiner meinung nach geht das nicht mit einem einzigen aufruf von malloc
    (wenn man den speicher nicht so kompliziert wie in cjosefs beispiel adressieren möchte).


  • Mod

    no-- schrieb:

    mein ausgangspunkt war:
    es sollen y namen eingegeben werden.
    die anzahl der namen ( hier: y ) sowie deren maximale stringlänge ( hier: x )
    sind zur compilezeit noch nicht bekannt.

    [...]

    meiner meinung nach geht das nicht mit einem einzigen aufruf von malloc

    malloc (y * (x+1))
    


  • hahaha! 😃 🤡
    hab mir schon gedacht, dass das kommt.
    deshalb hattest du auch meinen kommentar in den klammer weggelassen.
    daraus folgt aber die viel zu umständliche adressierung, weshalb das so in der praxis so gut wie nie gemacht wird.

    und:
    man spart zwar ein paar char** zeiger okay.
    aber der speicherbedarf der einzelnen elemente lässt sich nicht an die tatsächliche stringlänge anpassen, im endeffekt verbrät man sehr wahrscheinlich
    viel mehr speicher als man einspart.

    dagegen lassen sich bei der variante

    char** names = malloc ( y * sizeof( char* ));
    ...
    

    die einzelnen elemente an die einzelnen stringlängen anpassen.
    daher reserviert man vorteilhafterweise den speicher aller elemente nicht
    auf einmal, sondern erst dann, wenn er tatsächlich benötigt wird.
    wenn also z.b. genau 10 namen per stdin eingelesen werden sollen:
    (in dem fall wird man sich nicht wegen ein paar bytes einpullern,
    hier geht es um größenordnungen von daten mehrerer mega-/gigabyte, die
    z.b. aus dateien eingelesen werden, das unten stehende dient nur zur demo ;))

    #include <stdio.h>
    #include <string.h>
    
    #define NAMELEN_MAX 62
    
    void clear_stdin ( void )
    {
    	while ('\n' != getchar())
    	{}
    }
    
    char* read_name_stdin ( void )
    {
    	char* p = NULL;
    	char buf[NAMELEN_MAX+2];
    	puts ( "Type your name: " );
    	fgets ( buf, sizeof( buf ), stdin );
    	p = strrchr ( buf, '\n' );
    	if ( p == NULL )
    	{ // eventuell stdin auf ferror prüfen
    		puts ( "Name too long! Input skipped!" );
    		clear_stdin();
    	}
    	else
    	{
    		*p = 0;
    		p = malloc ( p - buf );
    		// Todo: p == NULL ?
    		strcpy ( p, buf );
    	}
    	return p;
    }
    
    void display_namelist ( char** names_array, unsigned n )
    {
    	int i;
    	for ( i = 0; i < n; i++ )
    		puts ( names_array[i] );
    }
    
    int main ( void )
    {
    	unsigned names_count = 10;
    	unsigned i;
    	char** names_array = malloc ( names_count * sizeof( char* ));
    	// Todo: names_array == NULL ?
    	for ( i = 0; i < 10; i++ )
    	{
    		char* name = NULL;
    		while ( name == NULL )
    			name = read_name_stdin();
    		names_array[i] = name;
    	}
    
    	display_namelist ( names_array, names_count );
    	// Todo: free memory
    	system ( "pause" );
    	return 0;
    }
    

Anmelden zum Antworten