Speicherzugriffsfehler nachdem Programm durchgelaufen ist



  • Hab ich auch schon. Funktioniert nicht, er gibt ja die Namen aus und danach steht in der Konsole "Speicherzugriffsfehler"

    Das Problem scheint zu sein, dass nAnzahl den Wert 200 hat, ich aber die Anzahl der eingegeben strings als Grenze setzen will.

    Also muss ich die Anzahl der Schleifendurchläufe in der Eingabefunktion mitzählen. Das mache ich indem ich die Zählervariable n nach jedem Durchlauf inkrementiere.

    Aber wie kann ich den Wert von n (aus der Eingabefunktion) in die Ausgabefunktion übergeben ?
    Habe auch versucht return n bei der Eingabefunktion und dann in der main-Funktion nAnzahl=n zu setzen.
    Funktioniert aber auch nicht weil die main-Funktion die Variable n auch nicht kennt.



  • probier dies:

    //Eingabe der Namen
    int StrLstEingabe (char* acListe[], int nAnzahl)
    {
       int s;
       for (s=0; s<nAnzahl; s++)
       {
          if (0 == (acListe[s] = malloc(256)))
             break;  // malloc failed
          fgets (acListe[s], 256, stdin);
          if (strcmp (acListe[s], "#\n")==0) 
             break;
       } 
       return s;
    }
    
    //Ausgabe der Namen
    void StrLstAusgabe (char* acListe[], int nAnzahl)
    {
       int s;
       for (s=0; s<=nAnzahl; s++)
       {
          fputs(acListe[s], stdout);
       }
    }
    
    int main()
    {
      char* acListe[100];
    
      StrLstAusgabe (acListe, StrLstEingabe(acListe, 100));
    
      getchar();
      return 0;
    }
    

    🙂



  • Hi, danke für den Vorschlag. Funktioniert wunderbar, leider wollte unser Dozent gewisse Dinge vorgeben, werde also den Code noch überarbeiten müssen.

    Ich versteh nicht so ganz was es hiermit auf sich hat:

    if (0 == (acListe[s] = malloc(256)))
             break;
    

    Du scheinst 256 Byte Speicher zu reservieren. Aber was soll das 0==(...) ?

    Desweiteren muss ich den Rückgabewert aus der Eingabefunktion in einer anderen Funktion nochmal verwenden. Dort will ich aber nicht nochmal die Eingabefunktion aufrufen (sonst muss ich die Eingabe der Namen auch wiederholen=

    Gibt es irgendeine Möglichkeit das ich den Rückgabewert von der EIngabefunktion irgendwie so ablegen kann, dass jede Funktion darauf zugreifen kann ?



  • Aber was soll das 0==(...)

    Er hat NULL gemeint, um etwaige Fehler von malloc abzufangen.

    Gibt es irgendeine Möglichkeit das ich den Rückgabewert von der EIngabefunktion irgendwie so ablegen kann, dass jede Funktion darauf zugreifen kann ?

    Eine? Globale Variablen. Statische Member von Funktionen. Blocks auf dem Heap (von malloc). Zeiger auf lokale Variablen.



  • Danke für die Antwort.

    Also pointer dachte ich mir schon, globale Variable ist mir auch klar....aber:

    Globale Variable ist für unseren Dozenten tabu 🙂

    Vorgabe von unserem Dozenten für diese Aufgabe:
    Das ganze mit insgesamt 5 Funktionen realisieren, nämlich:
    main-Funktion
    Eingabe-Funktion
    Eingabe-Funktion, welche die strings in einen zusammenhängenden Speicherbereich direkt hintereinander speichert
    Ausgabe-Funktion
    Ausgabe-Funktion, welche die SPeicheradresse der einzelnen strings mit ausgibt.

    Dabei MUSS die Eingabe-Funktion folgendermassen aufgerufen werden:
    int StrLstEingabe (char* acListe[], nAnzahl)

    und die Ausgabe:
    void StrLstAusgabe (char* acIndex[], int nAnzahl)

    Somit ist ein pointer auf nAnzahl bzw. das Erweitern auf eine dritte Variable nicht erlaubt. Global ist generell nicht gewünscht.
    Das bedeutet die Variable welche die Anzahl der Schleifendurchläufe in der Eingabe-Funktion mitzählt verliert ausserhalb dieser Funktion Ihren Wert.

    Ich weiss also nicht so recht wie ich die Zähler-Variable in mehr als einer Funktion verwenden kann. Mit einer klappt das ja (wie in dem obigen Beispiel) indem ich die Eingabe-Funktion mit der Ausgabe-Funktion aufrufe. Somit landet der Rückgabewert direkt bei der Ausgabe-Funktion.

    Aber wie bekomme ich den Rückgabewert nun in die anderen Ausgabe-Funktionen ? Ein erneutes Aufrufen der Eingabe-Funktion entfällt, da sonst die Eingabe erneut abgefragt wird.

    Mir fällt einfach keine Variante ein wie ich das mit den Vorgaben erledigen könnte.



  • lalas schrieb:

    Gibt es irgendeine Möglichkeit das ich den Rückgabewert von der EIngabefunktion irgendwie so ablegen kann, dass jede Funktion darauf zugreifen kann?

    aber ja. z.b so:

    int count_of_elements_including_number_sign = StrLstEingabe(acListe, 100);
    

    und dann einfach die entsprechenden funktionen mit dieser variablen als argument aufrufen.
    und vergiss nicht, am schluss alles wieder zu free'n.
    🙂



  • Super das probier ich mal....aber tut mir leid wenn ich nochmal nachhaken muss:

    Was meinst Du mit "free'n" ?



  • lalas schrieb:

    Was meinst Du mit "free'n" ?

    die elemente von 'acListe[]' sind allesamt adressen, die vom heap-manager durch 'malloc' angefordert wurden. weil C keinen automatischen 'garbage collector' hat (allah hat es so bestimmt), musst du diese elemente manuell (in einer schleife, mit 'free') dem heap wieder zurückgeben.
    🙂



  • hmmm...okay verstehe ich, wie ich es mache keine ahnung 😉

    ich muss auch in einer zweiten funktion mit "realloc" arbeiten und da bekomme ich ne compiler-warnung "ignoring return value of 'realloc', declared with attribute warn_unused_result"

    vielleicht hat das was damit zu tun ?

    das lustige daran ist, dass uns der dozent die funktion in der vorlesung an die tafel gekritzelt hat und (wie so oft) sich anscheinend ein fehler eingeschlichen hat.
    schau mal, vielleicht hilft das, die funktion soll im gegensatz zur ersten eingabe-funktion, die strings hintereinander in einem zusammenhängenden speicherbereich schreiben:

    int StrLstEingabeKompakt (char* acListe[], int nAnzahl)
    {
      void* vMem = NULL;
      char szPuffer[200];
      int s, nByteNeu, nByteSum;
    
      for (s=0; s<nAnzahl; s++)
      {
        fgets (szPuffer, 200, stdin);
        if (strcmp (szPuffer, "#\n")==0) break;
        nByteNeu=strlen(szPuffer)+1;
    
        realloc (vMem, nByteSum+nByteNeu);
        memcpy (vMem+nByteSum, szPuffer, nByteNeu);
        nByteSum+=nByteNeu;
        strcpy(acListe[s], szPuffer);
      }
      return s;
    }
    

    der quellcode ist exakt von unserem dozenten so vorgegeben worden. der erste fehler scheint zu sein, dass realloc garnix zurückgibt wenn der übegebene pointer den wert NULL hat.
    irgendein tipp ?



  • "ignoring return value of 'realloc', declared with attribute warn_unused_result"

    Da meckert der Compiler, und das ist auch gut so. realloc() könnte auch schiefgehen, und in dem Fall fliegt deinem Dozenten das Ding um die Ohren.

    Die Warnungen kann man bei üblichen Compilern abschalten, rein sprachlich könnte man sie auch durch einen void-Cast übergehen. Bringt aber nix, weil man halt schon prüfen sollte, ob realloc() durchgegangen ist.

    der erste fehler scheint zu sein, dass realloc garnix zurückgibt wenn der übegebene pointer den wert NULL hat.

    Stimmt nicht, realloc() gibt was zurück, wenn der Zeiger NULL war, nämlich das gleiche, was malloc() zurückgeben würde.

    irgendein tipp ?

    Immer die Rückgaben von Funktionen prüfen, in denen ein "alloc" vorkommt. Den Dozenten wüst beschimpfen, wenn er das nicht einsehen will.

    okay verstehe ich, wie ich es mache keine ahnung

    Einfach für jeden Zeiger, den malloc/realloc/calloc dir gegeben haben, irgendwann free() aufrufen.



  • lalas schrieb:

    hmmm...okay verstehe ich, wie ich es mache keine ahnung 😉

    ich muss auch in einer zweiten funktion mit "realloc" arbeiten und da bekomme ich ne compiler-warnung "ignoring return value of 'realloc', declared with attribute warn_unused_result"

    vielleicht hat das was damit zu tun ?

    nicht vielleicht, du *musst*! Was ist wenn ralloc fehlschlägt und NULL zurückliefert? Dann überschreibst du Speicher, der dir möglicherweise gar nicht gehört.



  • lalas schrieb:

    int StrLstEingabeKompakt (char* acListe[], int nAnzahl)
    {
      void* vMem = NULL;
      char szPuffer[200];
      int s, nByteNeu, nByteSum;
    
      for (s=0; s<nAnzahl; s++)
      {
        fgets (szPuffer, 200, stdin);
        if (strcmp (szPuffer, "#\n")==0) break;
        nByteNeu=strlen(szPuffer)+1;
        
        realloc (vMem, nByteSum+nByteNeu);
        memcpy (vMem+nByteSum, szPuffer, nByteNeu);
        nByteSum+=nByteNeu;
        strcpy(acListe[s], szPuffer);
      }
      return s;
    }
    

    die funktion ist ziemlich verbuggt. was mir auf den ersten blick auffällt:
    - vMem ist ein void*, d.h. die addition vMem+nByteSum wird kein compiler fressen wollen.
    - nByteSum wird nicht mit 0 initialisiert, hat also unvorhersehbaren inhalt
    dozenten machen sowas gern (code aus dem kopf ganz schnell auf die tafel kritzeln) damit ihr euch damit auseinandersetzt wenn's kracht. ihr sollt ja aus fehlern lernen und selbständig wissen erarbeiten, nicht alles vorgekaut bekommen.
    🙂



  • - vMem ist ein void*, d.h. die addition vMem+nByteSum wird kein compiler fressen wollen.

    Darüber bin ich auch schon gestolpert. warn_unused_result deutet akut auf den gcc hin, und der kann void-Arithmetik als Erweiterung (die aber in vielen Pakteten default-mäßig aufgedreht ist). Führt dazu, dass die Addition anstandslos durchgeht.



  • gnu-flamer schrieb:

    - vMem ist ein void*, d.h. die addition vMem+nByteSum wird kein compiler fressen wollen.

    Darüber bin ich auch schon gestolpert. warn_unused_result deutet akut auf den gcc hin, und der kann void-Arithmetik als Erweiterung (die aber in vielen Pakteten default-mäßig aufgedreht ist). Führt dazu, dass die Addition anstandslos durchgeht.

    wie bitte??? nimmt er das als 'char*' an?
    🙂



  • das void* vMem meinte unser dozent müsse so sein, weil realloc ein void* übergeben habe möchte als ersten parameter.
    wie ich dazu einen int addieren kann ist mir auch schleierhaft, aber laut dozent sollen wir das so schreiben.

    ich würde das ganze ja gerne umschreiben, nur ist mir nicht so ganz klar wie ich das anders stricken kann.

    der erste teil entspricht ja der "normalen" eingabefunktion.

    danach soll einfach der speicherbereich des eingegeben string berechnet werden und danach der speicherbereich des zweiten strings dazu addiert werden.

    so soll bewirkt werden das die strings alle in einem aufeinander folgenden speicherbereich gespeichert werden.

    nByteNeu=strlen(szPuffer)+1 müsste doch den speicherbedarf als int in nByteNeu schreiben, oder ?



  • lalas schrieb:

    das void* vMem meinte unser dozent müsse so sein, weil realloc ein void* übergeben habe möchte als ersten parameter.
    wie ich dazu einen int addieren kann ist mir auch schleierhaft, aber laut dozent sollen wir das so schreiben.

    Wenn ich diesen "Dozenten" erwisch... 😡

    In C kann jeder Pointertyp implizit nach void* umgewandelt werden. Ein void -Pointer ist ein Pointer, von dem der Compiler keine Ahnung hat, welche Art Element (Typ) dahintersteckt. Somit ist Pointerarithmetik mit void -Pointern ein Ratespiel.

    lalas schrieb:

    ich würde das ganze ja gerne umschreiben, nur ist mir nicht so ganz klar wie ich das anders stricken kann.

    Mir auch nicht. Deine (seine) StrLstEingabeKompakt( ) ist mir auch spanisch, da 1. (wie schon erwähnt) das realloc( ) herzlich wenig bringt, wenn man die neue Adresse wegwirft und 2. die Adressen der jeweiligen Tokens nicht im Array acListe landen.

    lalas schrieb:

    nByteNeu=strlen(szPuffer)+1 müsste doch den speicherbedarf als int in nByteNeu schreiben, oder ?

    ? Was meinst Du damit? Für Speichergrößen sollte man eigentlich den Typ size_t verwenden ( - deine Funktionen sollten statt Integern auch size_t verwenden und zurückgeben).

    Vielleicht hilft dir das:

    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    #define MAX_ITEMS 100
    
    size_t StrLstEingabeKompakt( char *list[ ], size_t max_items )
    {
    	char input[ 200 ];
    	char *data = 0;
    	size_t data_size = 0;
    	size_t input_length = 0;
    	size_t i = 0;
    
    	for( ; i < max_items; ++i ) {
    
    		fgets( input, 200, stdin );
    
    		if( !strcmp( input, "#\n" ) ) {
    
    			break;
    		}
    
    		data = realloc( data, data_size + ( input_length = strlen( input ) + 1 ) );
    		strcpy( data + data_size, input );
    		list[ i ] = data + data_size;
    		data_size += input_length;
    	}
    
    	return i;
    }
    
    void StrLstAusgabe( char* list[], size_t num_items )
    {
    	size_t i = 0;
    
    	for( ; i < num_items; ++i ) {
    
            // fgets( ) liest das newline mit ein. Ausgabe mit puts( ) wär' hässlich ;)
    		printf( list[ i ] );
    	}
    }
    
    int main( )
    {
    	char *list[ MAX_ITEMS ];
    	size_t num_items = StrLstEingabeKompakt( list, MAX_ITEMS );
    	StrLstAusgabe( list, num_items );
    	free( list[ 0 ] );
    }
    

    cheers, Swordfish

    PS: void* castet man nicht.



  • spitze, das hat mir echt geholfen...danke euch allen.

    habe den code jetzt soweit (fast) fertig und es funktioniert alles. muss nun nur noch sortieralgorithmen einbauen und fertig.

    was ich allerdings noch immer nicht begriffen habe ist das mit dem free()

    was muss ich genau nach dem aufruf von malloc, realloc, etc. genau machen ? einfach am ende der funktion free() schreiben oder noch irgendwelche parameter mit angeben ?



  • free nimmt void-Zeiger als einzigen Parameter (siehe Swordfish -- auch der hat hin und wieder recht).



  • gccsucks-freak schrieb:

    wie bitte??? nimmt er das als 'char*' an?

    Bin leider ein wenig betrunken, trotzdem folgendes:

    int main(int ac, char **av)
    {
        char buf[32] = {'0', '1', '2', '3', '4', 0};
        void *v = buf;
    
        v += 1;
    
        printf("%d %d %d\n", v, v+1, v+2);
    
        return 0;
    }
    

    Kompiliert mit dem GCC ohne Probleme und gibt drei aufeinanderfolgende Ganzzahlen aus. Ist das standard-konform?



  • #include <stdio.h>
    
    int main(int ac, char **av)
    {
        char buf[32] = {'0', '1', '2', '3', '4', 0};
        void *v = buf;
    
        v += 1;
    
        printf("%c\n", *(char*)v);
    
        return 0;
    }
    

    Gibt aus: 1


Anmelden zum Antworten