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?
-
@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 aufint
.
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 Zeigerfoo[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 ausfoo
(foo[0]
) nehmen und darauf lustig hinzuaddieren. Die einzelnen Speicherbereiche auf die die Zeigerfoo[0 ... ROWS-1]
zeigen HÄNGEN NICHT ZUSAMMEN!
-
@Swordfish
Mitint **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
int
s. Diese einzelnen Bereiche sind auch jeweils zusammenhängend, alle Werte direkt hintereinander im Speicher.ABER:
Das "Array" auf dasfeld[1]
zeigt kann ganz wo anders sein als das "Array" auf dasfeld[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 bisfeld[width * height - 1]
durchgehen wie Du es zB ininitalFeld()
machst.
-
@Swordfish
Ah, ok. Eben
Um nochmal klar zu stellen:
mitint **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 mitfeld = 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
int
s. Diese einzelnen Bereiche sind auch jeweils zusammenhängend, alle Werte direkt hintereinander im Speicher.ABER:
Das "Array" auf dasfeld[1]
zeigt kann ganz wo anders sein als das "Array" auf dasfeld[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 bisfeld[width * height - 1]
durchgehen wie Du es zB ininitalFeld()
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 mitint **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 vonmalloc()
zugewiesen wird. Diese vonmalloc()
zurückgegebene Adresse steht nun infoo
.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]
, ... bisfoo[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 vonmalloc()
zugewiesen wird. Diese vonmalloc()
zurückgegebene Adresse steht nun inbar
.Wieder dasselbe wie oben: in
bar
steht nun die vonmalloc()
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]
, ... bisbar[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 vonmalloc()
(=die Adresse wo dieser Speicherbereich beginnt) speichern wir jeweils inbar[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 istbar[y][x]
nur eine hübsche Schreibweise für*(*(bar + y) + x)
.Du addierst zu
bar
aber einfach immer weiter hinzu bis du irgendwann beibar + 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 exklusivewidth * height
laufen weil ja bei 0 angefangen wird.
Also bei beispielsweisewidth = 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
-
Danke der Mühe @Swordfish
vor allem die Zeichnung hat mir geholfen zu verstehen. Eins hab ich nun auch noch gelernt: Der Zufall lässt auch Programme laufen die falsch geschrieben sind
-
Danke nochmal allen für ihre Beiträge, das Thema "Zeiger" verstehe ich dank @Swordfish nun besser.
Jetzt beschäftige ich mich mit Threads in c um die Schlange alleine laufen zu lassen wenn keine Taste gedrückt wird.
-
Dazu brauchst Du keine Threads sondern nur etwas das Tastatureingaben ohne zu blocken erkennt. Schau nach ncurses, der funktion
getch()
undnodelay
.
-
@Swordfish
Danke für den Hinweis, werd jetzt ncurses Wiki studieren damit ich da rein komme. Hast du vielleicht noch ein paar Tips, oder zusammenfassende Worte für mich bezüglich der ncurses? So wie ich das jetzt verstehe muss neben dem einbinden der ncurses.h beim compilen dem Compiler über "-Incurses" mitgeteilt werden das er die ncurses "benutzt"
Ist der Umgang anderes als mit den üblichen stdlib.h und stdio.h
-
@EL-europ Die Headerdateien .h sind nicht die Library
Dort stehen (nur) die Informationen über die Funktionen, aber kein Code.
Der Code dazu steht (meist) in .lib-Dateien und werden vom Linker dazu gebunden.
Bei der Standardbibliothek ist das klar, bei Erweiterungen müssen die mit angegeben werden.In der Standardbibliothek sind eigentlich alle Funktionen der Header enthalten, die du mit den <> beim #include angibst.
(wenn man da nicht selber rumgepfuscht hat)Eine Ausnahme ist <math.h>. Wenn man die Fließkommafunktionen nutzen möchte, muss man auf manchen Systemen die math-Libraray mit -lm dazu binden.
(Das gilt nicht für + - * /, sondern für Funktionen wie sin(), sqrt(), ... )