Untypischer Laufzeitfehler durch malloc()



  • @EL-europ sagte in Untypischer Laufzeitfehler durch malloc():

    for(cnt=0; cnt <= width * height; cnt++)
    

    Das <= sollte wohl besser < sein.
    So wie die Schleife da steht läuft die bis inklusive width * height, sollte aber bis exklusive width * height laufen weil ja bei 0 angefangen wird.
    Also bei beispielsweise width = height = 2 würde die Schleife von 0 bis inklusive 4 laufen, also 5 mal. Sollte aber nur 4 mal laufen.
    Rest hab ich mir nicht angesehen.

    Um solche Fehler zu finden eignen sich tools wie Valgrind oder Address Sanitizer. Anleitungen wie man diese verwendet sollten sich leicht ergoogeln lassen.



  • @hustbaer sagte in Untypischer Laufzeitfehler durch malloc():

    So wie die Schleife da steht läuft die bis inklusive width * height, sollte aber bis exklusive width * height laufen weil ja bei 0 angefangen wird.
    Also bei beispielsweise width = height = 4 würde die Schleife von 0 bis inklusive 4 laufen, also 5 mal. Sollte aber nur 4 mal laufen.

    Du meinst "würde die Schleife von 0 bis inklusive 16 laufen, also 17 mal. Sollte aber nur 16 mal laufen."!?!



  • Danke @hustbaer 🙏🏻 das war der Fehler. Beim Ändern auf dynamischen Speicher hab ich vergessen das ich +1 Feld brauche 😵

    Danke nochmal das du dir das angesehen hast!



  • Ich will dich ja nicht nerven, aber

    @EL-europ sagte in Untypischer Laufzeitfehler durch malloc():

    		feld = malloc(width * sizeof(int));
    		for(cnt=0; cnt<width; cnt++)feld[cnt]=malloc(height * sizeof(int));
    

    ist immer noch Mist.

    Erstens funktioniert das nur zufällig wenn sizeof int und sizeof int* gleich sind und zweitens hast Du damit keinen zusammenhängenden Speicherbereich (außer evtl. Zufällig) auf den Du mit

    @EL-europ sagte in Untypischer Laufzeitfehler durch malloc():

    	for(cnt=0; cnt<count; cnt++){
    		*(feld+cnt) = wert;
    	}
    

    in initalFeld() zugreifen könntest.



  • @Swordfish
    Danke dir das du das auch angesehen hast. Und ich bin mit dir das dieses 2d-feld falsch iniziiert wurde, aber bei der Ausführung ist es trotzdem richtig iniziiert, auch wenn ich von 0 verschiedene Werte nehme wird das Feld richtig dargestellt.
    Ich vermute der Fehler viel nicht auf weil keine verschiedenen Werte iniziiere sondern nur gleiche,

    Danke dir auch nochmal



  • @Swordfish
    Die Speicheralloziierung ist doch Richtig oder? Die hab ich ausm Grundkurs von Jürgen Wolf. Die iniziierung des Feldes ist aber Falsch gewesen! Hab ich dich da Richtig verstanden?


  • Mod

    @EL-europ sagte in Untypischer Laufzeitfehler durch malloc():

    @Swordfish
    Die Speicheralloziierung ist doch Richtig oder? Die hab ich ausm Grundkurs von Jürgen Wolf. Die iniziierung des Feldes ist aber Falsch gewesen! Hab ich dich da Richtig verstanden?

    Argh! Da bist du leider reingefallen:
    https://www.c-plusplus.net/forum/topic/272350/warnung-bücher-von-jürgen-wolf-zu-c-und-c-zum-lernen-ungeeignet-weil



  • Du greifst auf Speicher zu der Dir nicht gehört.

    valgrind output:

    ==296== Memcheck, a memory error detector
    ==296== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
    ==296== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
    ==296== Command: ./a.out 60 20
    ==296== Parent PID: 13
    ==296== 
    ==296== Invalid write of size 8
    ==296==    at 0x109B2E: main (in /mnt/c/Users/Swordfish/a.out)
    ==296==  Address 0x4a48130 is 0 bytes after a block of size 240 alloc'd
    ==296==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
    ==296==    by 0x109AF6: main (in /mnt/c/Users/Swordfish/a.out)
    ==296== 
    ==296== Invalid write of size 4
    ==296==    at 0x1093C5: initalFeld (in /mnt/c/Users/Swordfish/a.out)
    ==296==    by 0x1099F9: resetGame (in /mnt/c/Users/Swordfish/a.out)
    ==296==    by 0x109B76: main (in /mnt/c/Users/Swordfish/a.out)
    ==296==  Address 0x4a481c0 is 0 bytes after a block of size 80 alloc'd
    ==296==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
    ==296==    by 0x109B2D: main (in /mnt/c/Users/Swordfish/a.out)
    ==296== 
    ==296== Invalid write of size 8
    ==296==    at 0x109419: initalTail (in /mnt/c/Users/Swordfish/a.out)
    ==296==    by 0x109A05: resetGame (in /mnt/c/Users/Swordfish/a.out)
    ==296==    by 0x109B76: main (in /mnt/c/Users/Swordfish/a.out)
    ==296==  Address 0x4a4c8b0 is 0 bytes after a block of size 9,600 alloc'd
    ==296==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
    ==296==    by 0x109B5C: main (in /mnt/c/Users/Swordfish/a.out)
    ==296== 
    ==296== Invalid write of size 4
    ==296==    at 0x109A58: resetGame (in /mnt/c/Users/Swordfish/a.out)
    ==296==    by 0x109B76: main (in /mnt/c/Users/Swordfish/a.out)
    ==296==  Address 0x4a48af8 is 8 bytes before a block of size 80 alloc'd
    ==296==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
    ==296==    by 0x109B2D: main (in /mnt/c/Users/Swordfish/a.out)
    ==296== 
    ==296== Invalid write of size 4
    ==296==    at 0x109A86: resetGame (in /mnt/c/Users/Swordfish/a.out)
    ==296==    by 0x109B76: main (in /mnt/c/Users/Swordfish/a.out)
    ==296==  Address 0x4a48af0 is 16 bytes before a block of size 80 alloc'd
    ==296==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
    ==296==    by 0x109B2D: main (in /mnt/c/Users/Swordfish/a.out)
    ==296== 
    ==296== Invalid read of size 4
    ==296==    at 0x109620: printFeld (in /mnt/c/Users/Swordfish/a.out)
    ==296==    by 0x109BBB: main (in /mnt/c/Users/Swordfish/a.out)
    ==296==  Address 0x4a481c0 is 0 bytes after a block of size 80 alloc'd
    ==296==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
    ==296==    by 0x109B2D: main (in /mnt/c/Users/Swordfish/a.out)
    ==296== 
    ==296== Invalid write of size 4
    ==296==    at 0x10972E: setFeld (in /mnt/c/Users/Swordfish/a.out)
    ==296==    by 0x109BD6: main (in /mnt/c/Users/Swordfish/a.out)
    ==296==  Address 0x4a48af8 is 8 bytes before a block of size 80 alloc'd
    ==296==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
    ==296==    by 0x109B2D: main (in /mnt/c/Users/Swordfish/a.out)
    ==296== 
    ==296== Invalid read of size 4
    ==296==    at 0x1098A2: setFeld (in /mnt/c/Users/Swordfish/a.out)
    ==296==    by 0x109C04: main (in /mnt/c/Users/Swordfish/a.out)
    ==296==  Address 0x4a48af4 is 12 bytes before a block of size 80 alloc'd
    ==296==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
    ==296==    by 0x109B2D: main (in /mnt/c/Users/Swordfish/a.out)
    ==296== 
    ==296== Invalid write of size 4
    ==296==    at 0x1099A3: setFeld (in /mnt/c/Users/Swordfish/a.out)
    ==296==    by 0x109C04: main (in /mnt/c/Users/Swordfish/a.out)
    ==296==  Address 0x4a48af4 is 12 bytes before a block of size 80 alloc'd
    ==296==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
    ==296==    by 0x109B2D: main (in /mnt/c/Users/Swordfish/a.out)
    ==296== 
    
    valgrind: m_mallocfree.c:305 (get_bszB_as_is): Assertion 'bszB_lo == bszB_hi' failed.
    valgrind: Heap block lo/hi size mismatch: lo = 304, hi = 77894656.
    This is probably caused by your program erroneously writing past the
    end of a heap block and corrupting heap metadata.  If you fix any
    invalid writes reported by Memcheck, this assertion failure will
    probably go away.  Please try that before reporting this as a bug.
    
    
    host stacktrace:
    ==296==    at 0x58046FFA: ??? (in /usr/lib/x86_64-linux-gnu/valgrind/memcheck-amd64-linux)
    ==296==    by 0x58047127: ??? (in /usr/lib/x86_64-linux-gnu/valgrind/memcheck-amd64-linux)
    ==296==    by 0x580472CB: ??? (in /usr/lib/x86_64-linux-gnu/valgrind/memcheck-amd64-linux)
    ==296==    by 0x580514B4: ??? (in /usr/lib/x86_64-linux-gnu/valgrind/memcheck-amd64-linux)
    ==296==    by 0x5803DE9A: ??? (in /usr/lib/x86_64-linux-gnu/valgrind/memcheck-amd64-linux)
    ==296==    by 0x5803CD9F: ??? (in /usr/lib/x86_64-linux-gnu/valgrind/memcheck-amd64-linux)
    ==296==    by 0x58041F04: ??? (in /usr/lib/x86_64-linux-gnu/valgrind/memcheck-amd64-linux)
    ==296==    by 0x5803C1D8: ??? (in /usr/lib/x86_64-linux-gnu/valgrind/memcheck-amd64-linux)
    ==296==    by 0x58017AF4: ??? (in /usr/lib/x86_64-linux-gnu/valgrind/memcheck-amd64-linux)
    ==296==    by 0x1002D6BFB2: ???
    ==296==    by 0x1002CA9F2F: ???
    ==296==    by 0x1002CA9F17: ???
    ==296==    by 0x1002CA9F2F: ???
    ==296==    by 0x1002CA9F3F: ???
    
    sched status:
      running_tid=1
    
    Thread 1: status = VgTs_Runnable (lwpid 296)
    ==296==    at 0x10993E: setFeld (in /mnt/c/Users/Swordfish/a.out)
    ==296==    by 0x109C04: main (in /mnt/c/Users/Swordfish/a.out)
    client stack range: [0x1FFEFFE000 0x1FFF000FFF] client SP: 0x1FFF000030
    valgrind stack range: [0x1002BAA000 0x1002CA9FFF] top usage: 9480 of 1048576
    


  • Wenn Du sowas machst wie:

    int *foo = malloc(ROWS * sizeof(int*));
    

    hast Du einen zusammenhängenden Speicherbereich für ROWS Zeiger auf int.
    foo[0] ist der erste dieser Zeiger, foo[ROWS - 1] ist der letzte.

    Dann alloziierst Du mit malloc() die "zweite Dimension" und lässt diese Zeiger foo[0 ... ROWS-1] darauf zeigen:

    for (size_t row = 0; row < ROWS; ++row)
        foo[row] = malloc(COLUMNS * sizeof(int));
    

    Darauf zugreifen kannst Du nun mit foo[row][column].
    Was Du aber nicht machen kannst ist den ersten Zeiger aus foo (foo[0]) nehmen und darauf lustig hinzuaddieren. Die einzelnen Speicherbereiche auf die die Zeiger foo[0 ... ROWS-1] zeigen HÄNGEN NICHT ZUSAMMEN!



  • @Swordfish
    Mit

    int **feld = malloc(width * sizeof(int));
    for(cnt=0; cnt<width; cnt++) feld[cnt]=malloc(height * sizeof(int));
    

    alloziiere ich doch ein Feld von Zeigern das auf int-typen zeigt?
    wenn ich

    *feld = malloc(width * sizeof(int *));
    

    asführe warnt mich der Compiler mit
    warning: assignment to ‘int’ from ‘void *’ makes integer from pointer without a cast [-Wint-conversion]
    174 | *feld = malloc(width * sizeof(int *));
    wenn ich

    *feld = (int)malloc(width * sizeof(int *));
    

    ausführe mit warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
    174 | *feld = (int)malloc(width * sizeof(int *));

    Ich denke die Breite der Typen kann auf unterschiedlichen Platformen zu Problemen führen?
    Auch kann ich dann mit

    &feld[0][0]
    

    nicht mehr die Adresse an eine Funktion übergeben



  • T'schuldige, das da:

    @Swordfish sagte in Untypischer Laufzeitfehler durch malloc():

    Wenn Du sowas machst wie:

    int *foo = malloc(ROWS * sizeof(int*));
    

    sollte natürlich

    int **foo = malloc(ROWS * sizeof(int*));
    //   ^
    

    heißen.



  • @EL-europ sagte in Untypischer Laufzeitfehler durch malloc():

    alloziiere ich doch ein Feld von Zeigern das auf int-typen zeigt?

    Ja, damit bekommst Du ein "Array" von Zeigern auf int.
    feld zeigt dann auf einen zusammenhängenden Speicherbereich für diese Zeiger.

    Dann alloziierst Du für jeden dieser Zeiger ein "Array" von ints. Diese einzelnen Bereiche sind auch jeweils zusammenhängend, alle Werte direkt hintereinander im Speicher.

    ABER:
    Das "Array" auf das feld[1] zeigt kann ganz wo anders sein als das "Array" auf das feld[0] zeigt. Diese einzelnen "Arrays" können im Speicher überall verstreut sein.

    Deshalb kannst Du nicht einfach den ersten Zeiger feld[0] nehmen und von dem ausgehend bis feld[width * height - 1] durchgehen wie Du es zB in initalFeld() machst.



  • @Swordfish
    Ah, ok. Eben 🙂
    Um nochmal klar zu stellen:
    mit

    int **foo =malloc(cnt * sizeof(int))
    for(cnt=0; cnt<width; cnt++)feld[cnt]=malloc(height * sizeof(int));
    

    erstelle ich ein Feld in dem width*height int-typen gespeichert werden
    und mit

    feld = malloc(width * sizeof(int *));
    for(cnt=0; cnt<width; cnt++) feld[cnt]=malloc(height * sizeof(int));
    

    erstelle ich ein Feld in dem width * Zeiger auf height * int-typen zeigen? Welchen Vorteil hat das? Vielleicht Portabilität? Wenn das so ist muss ich den Zugriff auf die Zeiger und deren Werte im Programm bei Änderung auch noch über- prüfen/denken.

    Das tu jetzt mal



  • @EL-europ
    das tu ICH jetzt mal will ich sagen 🙂 sry



  • @Swordfish sagte in Untypischer Laufzeitfehler durch malloc():

    @EL-europ sagte in Untypischer Laufzeitfehler durch malloc():

    alloziiere ich doch ein Feld von Zeigern das auf int-typen zeigt?

    Ja, damit bekommst Du ein "Array" von Zeigern auf int.
    feld zeigt dann auf einen zusammenhängenden Speicherbereich für diese Zeiger.

    Dann alloziierst Du für jeden dieser Zeiger ein "Array" von ints. Diese einzelnen Bereiche sind auch jeweils zusammenhängend, alle Werte direkt hintereinander im Speicher.

    ABER:
    Das "Array" auf das feld[1] zeigt kann ganz wo anders sein als das "Array" auf das feld[0] zeigt. Diese einzelnen "Arrays" können im Speicher überall verstreut sein.

    Deshalb kannst Du nicht einfach den ersten Zeiger feld[0] nehmen und von dem ausgehend bis feld[width * height - 1] durchgehen wie Du es zB in initalFeld() machst.

    Eben hab ich dich vielleicht verstanden und was dazu gelernt?
    In beiden Fällen sind es Zeiger auf int-typen, in erster Version sind die int-arrays nicht hintereinanader im Speicher und mit

    int **feld=malloc(size * sizeof(int *));
    

    werden auch die int-arrays hintereinander im Speicher abgelegt?



  • @EL-europ sagte in Untypischer Laufzeitfehler durch malloc():

    werden auch die int-arrays hintereinander im Speicher abgelegt?

    Nein.

    Ein Doppelzeiger **feld ist kein 2D-Array - es sieht nur so aus.



  • @DirkB
    ja stimmt. Durch den wiederholten Aufruf von malloc() in der Schleife für jedes einzelne array kann nicht gewährleisten sein das sie hintereinander liegen. oder?



  • @SeppJ
    es ist tatsächlich so das ich ein c-Grundlagen und ein c++ umfassendes Handbuch von J.W. habe die beide, zumindest Didaktisch, nicht an andere Autoren heranreichen. Seine Beispielcodes werfen oft Fragen auf die nicht beantwortet werden. Frag jetzt bitte nicht nach einem Beispiel, hab die Bücher grade hinter mir und mag nicht grundlos darin lesen und schlechte Beispiele suchen



  • Ich weiß nicht, wie man es Dir erzählen muss damit der Groschen fällt.

    int *foo;
    foo = malloc(42 * sizeof(int));
    

    Da ist foo ein Zeiger auf einen Integer dem das Ergebnis von malloc() zugewiesen wird. Diese von malloc() zurückgegebene Adresse steht nun in foo.

    Oben sagte ich "Zeiger auf einen" Integer. Du weißt aber, daß malloc() einen zusammenhängenden Speicherbereich besorgt hat, in dem 42 Integer alle direkt hintereinander liegen. Also kannst Du *(foo + 0), *(foo + 1), *(foo + 2), *(foo + 3), ... bis *(foo + 42 - 1) machen um auf die einzelnen Elemente dieses Speicherbereichs zuzugreifen. (Oder, dasselbe, nur hübscher: foo[0], foo[1], foo[2], ... bis foo[42 - 1].)

    Zu Deinem 2d-Dings:

    int **bar;  // ein Zeiger auf einen Zeiger auf Integer
    bar = malloc(42 * sizeof(int*));
    

    Da ist foo ein Zeiger auf einen [Zeiger auf Integer] dem das Ergebnis von malloc() zugewiesen wird. Diese von malloc() zurückgegebene Adresse steht nun in bar.

    Wieder dasselbe wie oben: in bar steht nun die von malloc() zurückgegebene Adresse und wir wissen, daß malloc() einen zusammenhängenden Speicherbereich besorgt hat in dem 42 [Zeiger auf Integer] alle direkt hintereinander liegen. Also können wir *(bar + 0), *(bar + 1), *(bar + 2), *(bar + 3), ... bis *(bar + 42 - 1) machen um auf die einzelnen Elemente dieses Speicherbereichs zuzugreifen. (Oder, dasselbe, nur hübscher: bar[0], bar[1], bar[2], ... bis bar[42 - 1].)

    Schön. Nun besorgen wir uns die 2. Dimension:

    int **bar;                           // dasselbe wie
    bar = malloc(42 * sizeof(int*));    // schon im letzten codeschnippsel
    
    for (size_t i = 0; i < 42; ++i)
        bar[i] = malloc(13 * sizeof(int));  // DIESE ZEILE
    

    besorgt nun jeweils mittels malloc() einen zusammenhängenden Speicherbereich für 13 Integer. Das Ergebnis von malloc() (=die Adresse wo dieser Speicherbereich beginnt) speichern wir jeweils in bar[0], bar[1], bar[2], ... bar[42 - 1].

    Das schaut nun im Speicher so aus:

    
    
     Irgendwo am Stack           Irgendwo am Heap                           Irgendwo am Heap
      |                           |                                           |
    +-------+    zeigt auf    +-------------------------+   zeigt auf      +-----------+
    | bar   |---------------> |  einen Zeiger auf int   |----------------->| einen int |
    +-------+                 |-------------------------|                  | einen int |
                              | malloc garantiert uns   |                  | einen int |
                  bar +  1 -->| daß einen Zeiger weiter |-----------+      | ...       |
                              | noch ein Zeiger ist der |           |      | einen int |
                              | uns gehört              |           |      +-----------+
                              |-------------------------|           |                                    Irgendwo am Heap
                  bar +  2 -->| und noch einer          |----+      |                                       |
                              |-------------------------|    |      |                                    +-----------+
                  bar +  3 -->| und noch einer          |    |      +----- *(*(bar + 1) +  0) ---------->| einen int |
                              |-------------------------|    |             *(*(bar + 1) +  1) ---------->| einen int |
                  ...               wird langweilig          |             *(*(bar + 1) +  2) ---------->| einen int |
                              |-------------------------|    |                   ...                     | ...       |
                  bar + 40 -->| und noch einer          |    |             *(*(bar + 1) + 12) ---------->| einen int |
                              |-------------------------|    |                                           +-----------+
                  bar + 41 -->| und der letzte          |    |
                              +-------------------------+    |                                           IRGENDWO am Heap
                                                             |                                            +-----------+
                                                             +-------------*(*(bar + 2) +  0) ----------->| einen int |
                                                                           *(*(bar + 2) +  1) ----------->| einen int |
                                                                           *(*(bar + 2) +  2) ----------->| einen int |
                                                                                 ...                      | ...       |
                                                                           *(*(bar + 2) + 12) ----------->| einen int |
                                                                                                          +-----------+
    

    Du kannst die Pointer bar[0], bar[1], bar[2], ... bar[42 - 1] nehmen und die dereferenzieren um zu den jeweiligen Anfängen der 2. Dimension zu kommen. bar[y] ist bloß eine hübsche Schreibweise für *(bar + y). Also ist bar[y][x] nur eine hübsche Schreibweise für *(*(bar + y) + x).

    Du addierst zu bar aber einfach immer weiter hinzu bis du irgendwann bei bar + y * x bist. Das funktioniert so nicht weil die Dingstis der "2. Dimension" überall im Speicher verstreut sein können und nicht hintereinander liegen.



  • @Th69 sagte in Untypischer Laufzeitfehler durch malloc():

    @hustbaer sagte in Untypischer Laufzeitfehler durch malloc():

    So wie die Schleife da steht läuft die bis inklusive width * height, sollte aber bis exklusive width * height laufen weil ja bei 0 angefangen wird.
    Also bei beispielsweise width = height = 4 würde die Schleife von 0 bis inklusive 4 laufen, also 5 mal. Sollte aber nur 4 mal laufen.

    Du meinst "würde die Schleife von 0 bis inklusive 16 laufen, also 17 mal. Sollte aber nur 16 mal laufen."!?!

    Fast. Ich wollte width = height = 2 und 4/5 schreiben, und hab dann fälschlicherweise 4/4/5 statt 2/4/5 geschrieben. Läuft aber auf's gleiche raus 🙂