Untypischer Laufzeitfehler durch malloc()
-
Hallo miteinander ich hoffe hier tieferen Einblick in C zu bekommen mit folgendem Problem:
#include <stdio.h> #include <stdlib.h> #include <termio.h> #include <math.h> #include <time.h> struct position{ int x; int y; } pos; void initalTail(struct position *tail){ int cnt=0; struct position myPos; myPos.x=-1; myPos.y=-1; // printf("Fehler"); hier endet das Problem for(cnt=0; cnt <= width * height; cnt++){ *(tail + cnt) = myPos; } } void resetGame(int *feld, struct position *tail){ initalFeld(feld, width * height, BLANK); initalTail(tail); } int main(int argc, char *argv[]){ .. int **feld; struct position *tail;/*[width * height + 1];*/ if(argc > 2){ width=atoi(argv[1]); height=atoi(argv[2]); feld = malloc(width * sizeof(int)); for(cnt=0; cnt<width; cnt++)feld[cnt]=malloc(height * sizeof(int)); //hier beginnt das Problen tail = (struct position *)malloc((width * height) *(sizeof(struct position)));
mit gcc - Wall -pedantic ... //(Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
und arm-linux-gnueabihf-gcc ... //crosstoolchain
lässt sich das Programm meldungsfrei compilieren.
Bei der Ausführung auf x86_64 mit argv[1]= 3und argv[2]=3
und auf arm unabhängig davon immer, bricht das Programm beim Start mit der Fehlermeldung "malloc(): corrupted top size Abgebrochen (Speicherabzug geschrieben)" ab.Wenn ich aber in der Funktion initalTail(.. die printf("Fehler") Anweisung ausführen lasse, läuft das Programm auf beiden Platformen einwandfrei!
Wie kann das sein?
Ich bin neu in C, und glaube Zeiger und dynamischen Speicher allozieren verstanden zu haben. Aber hier komme ich nicht weiter... und weis auch nicht wie das speziell googlen kann. (Oder hab ich tatsächlich einen Fehler mit dem Zeiger und Speicher im Programm und es läuft rein zufällig auf ZWEI Maschinen mit der printf?)
Wenn ich den speicher nicht über malloc() alloziere sondern das Feld mit tail[width * height] definiere läuft es AUCH auf beiden Plattformen.Ich selbst komme aus Python zu C und danke schon mal im Voraus für Antworten
-
Mit "untypischer" Fehler hab ich mich zu weit aus dem Fenster gelehnt! Ich hab zu wenig Wissen in C um das behaupten zu können... sry
-
Ein vollständiges kompilierbares Programm wäre schon super.
-
@Swordfish sagte in Untypischer Laufzeitfehler durch malloc():
Ein vollständiges kompilierbares Programm wäre schon super.
Moin moin
#include <stdio.h> #include <stdlib.h> #include <termio.h> #include <math.h> #include <time.h> #define KEY_UP 'w' #define KEY_DOWN 's' #define KEY_LEFT 'd' #define KEY_RIGHT 'a' #define BLANK 0 #define HEAD 1 #define TAIL 2 #define FOOD 3 int gameover = 0; int points = 0; int width = 100; int height = 100; int tailcount = 0; struct position{ int x; int y; } pos; static struct termio savemodes; static int havemodes = 0; int tty_break() { struct termio modmodes; if(ioctl(fileno(stdin), TCGETA, &savemodes) < 0) return -1; havemodes = 1; modmodes = savemodes; modmodes.c_lflag &= ~ICANON; modmodes.c_cc[VMIN] = 1; modmodes.c_cc[VTIME] = 0; return ioctl(fileno(stdin), TCSETAW, &modmodes); } void initalFeld(int *feld, int count, int wert){ int cnt; for(cnt=0; cnt<count; cnt++){ *(feld+cnt) = wert; } } void initalTail(struct position *tail){ int cnt=0; struct position myPos; myPos.x=-1; myPos.y=-1; // printf("Fehler"); for(cnt=0; cnt <= width * height; cnt++){ *(tail + cnt) = myPos; } } void move(int key){ switch(key){ case KEY_DOWN: if(pos.y >= height -1) pos.y = 0; else ++pos.y; break; case KEY_UP: if(pos.y <= 0) pos.y = height -1; else --pos.y; break; case KEY_LEFT: if(pos.x >= width -1) pos.x = 0; else ++pos.x; break; case KEY_RIGHT: if(pos.x <= 0) pos.x = width -1; else --pos.x; } } void printFeld(int *feld){ int x; int y; int figur; fprintf(stdout, "\033[2J"); fprintf(stdout, "\033[1;1H"); printf("\nsnake v%d.%d\n(%d x %d)\n", \ snake_VERSION_MAJOR, snake_VERSION_MINOR, width, height); for(y=0; y < height + 2; y++){ printf("\n|"); for(x=0; x < width; x++){ if(y==0 || y==height +1)printf("-"); else{ figur = *(feld + (x*height +y-1)); switch(figur){ case BLANK: printf(" "); break; case HEAD : printf("@"); break; case TAIL : printf("*"); break; case FOOD : printf("X"); break; } } } printf("|"); } printf("\n\n%d Punke\n",points); } int setFeld(int *feld, struct position *tail, int befor){ int ret = 1; int cnt = 0; int randx; int randy; static int hasFood = 0; struct position mypos; if(befor){ if(! hasFood){ *(feld + (pos.x * height + pos.y)) = BLANK; if(tailcount > 0){ mypos = *tail; *(feld + (mypos.x * height + mypos.y)) = BLANK; *(feld + (pos.x * height + pos.y)) = TAIL; if(tailcount > 1){ while(cnt < tailcount -1){ *(&tail[cnt]) = *(&tail[cnt+1]); ++cnt; } *(tail + tailcount -1) = pos; }else if(tailcount == 1) *tail = pos; } }else{ *(feld + (pos.x * height + pos.y)) = TAIL; hasFood = 0; } } else{ int figur = *(feld + (pos.x * height + pos.y)); if(figur == FOOD){ *(tail + tailcount) = pos; ++tailcount; hasFood = 1; points += 10; do{ randx=rand() % width; randy=rand() % height; }while(*(feld + (randx * height + randy)) != BLANK); *(feld + (randx * height + randy)) = FOOD; }else if(figur == TAIL) ret = 0; *(feld + (pos.x * height + pos.y)) = HEAD; } return ret; } void resetGame(int *feld, struct position *tail){ points = 0; tailcount = 0; initalFeld(feld, width * height, BLANK); initalTail(tail); pos.x = width / 2; pos.y = height / 2; *(feld + (pos.x * height + pos.y)) = HEAD; *(feld + (pos.x * height + pos.y - 2)) = FOOD; } int main(int argc, char *argv[]){ char key = 'w'; char gemeOverkey; int cnt; int **feld; struct position *tail;/*[width * height + 1];*/ if(argc > 2){ width=atoi(argv[1]); height=atoi(argv[2]); feld = malloc(width * sizeof(int)); for(cnt=0; cnt<width; cnt++)feld[cnt]=malloc(height * sizeof(int)); tail = (struct position *)malloc((width * height) *(sizeof(struct position))); }else{ printf("Aufruf mit 'snake breite hoehe' max: 'snake 100 100'\n"); return 0; } resetGame(&feld[0][0],tail); srand( (unsigned int) time(NULL) ); tty_break(); while(key != 'x'){ printFeld(&feld[0][0]); setFeld(&feld[0][0],tail,1); key = getchar(); move(key); if(! setFeld(&feld[0][0],tail,0) || points == width * height * 10 - 20){ if(points == width * height * 10 - 20) printf("\nSie haben gewonnen !"); else printf("\nSie haben sich in den Schwanz gebissen !"); printf("\nnochmal j/n ?\n"); gemeOverkey = getchar(); if(gemeOverkey == 'j') resetGame(&feld[0][0],tail); else if(gemeOverkey == 'n')break; } } printf("\ndanke fuers spielen\n"); return 0;
ist eine weiterentwicklung von
einem c-tutorial Link Text
-
@EL-europ sagte in Untypischer Laufzeitfehler durch malloc():
void initalFeld(int *feld, int count, int wert){ int cnt; for(cnt=0; cnt<count; cnt++){ *(feld+cnt) = wert; } }
Du hast hier für
feld
kein zweidimensionales Array in dem alle Elemente direkt hintereinander im Speicher liegen.2D-Array
int foo[][]{ { 1, 2, 3 }, { 4, 5, 6 } }
starting at &foo[0]:1, 2, 3, 4, 5, 6
Visualized:+------+ | foo | +------+ | 1 | | 2 | | 3 | | 4 | | 5 | | 6 | +------+
Dein Zeiger auf Zeiger auf
int
:int foo = malloc(2 * sizeof(int*)); // beachte das sternchen for (size_t i = 0; i < 2; ++i) foo[i] = malloc(3 * sizeof(int)); int value = 1; for (size_t y = 0; y < 2; ++y) for (size_t x = 0; x < 3 ++x) foo[y][x] = value++;
+------+ +--> | int | | +------| | | 1 | | | 2 | | | 3 | +---------+ | +------+ | foo[0] -|-------+ | foo[1] -|-------+ +------+ +---------+ +--> | int | +------+ | 4 | | 5 | | 6 | +------+
-
@Swordfish
Super, danke für die schnelle Antwort. Ich werd nach dem Schlafen versuchen sie zu Verstehen und dann zu implementieren. Dann geb ich hier noch nen Kommentar dazu ab. Aller Vorraussicht noch heute am mittag.
-
@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 inklusivewidth * height
, sollte aber bis exklusivewidth * height
laufen weil ja bei 0 angefangen wird.
Also bei beispielsweisewidth = 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 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."!?!
-
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
undsizeof 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?
-
@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