dynamische mehrdimensionale Arrays [C]



  • 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;
    }
    

  • Mod

    Wenn du das Problem neu definierst, dann ist auch eine neue Lösung gefragt. Bei der Problemstellung "X strings mit Länge Y" ist jedoch eine nicht-kontinuierliche Allokation einfach dumm. Doppelte Indirektion und Nichtlokalität sind tödlich für Programmperformance. Speichereffizienz ist hingegen fast immer vollkommen irrelevant. Außerdem ist dein Argument bzgl. der Speichemenge naiv: Der Doppelzeiger verbrät schließlich schon einmal 8 Byte pro extra Zeiger + malloc Verwaltungsdaten für jede Allokation (geschätzt auch noch mal ~16 Byte). Das ist schon alleine länger als die Beispielstrings hier im Thread.



  • Wobei mit "max. bekannte Stringlänge" jetzt nicht nur die Compilezeit gemeint ist, sondern man mit C99 (und optional C11) und VLA auch zur Laufzeit quasigenerische char[] Typen basteln kann und dann einfach die Subskript-Schreibweise weiterverwenden kann.
    Hauptsache, die Arraygröße ist gleich für alle Elemente der Stringliste.

    void liste(void *p,int i)
    {
      char (*s)[i]=p;
      for(int x=0;x<3;++x)
        puts(s[x]); // <-- das geht jetzt hier ohne Kenntnis der Arraygröße zur Compilezeit
    }
    
    int main()
    {
        char a[][11]={"bla","x","fasel"};
        char b[][99]={"bla","y","fasel"};
        liste(a,sizeof*a);
        liste(b,sizeof*b);
    }
    

    Wobei das hier höchstwahrscheinlich der einzige "Vorteil" von VLA sein dürfte.


Anmelden zum Antworten