Untypischer Laufzeitfehler durch malloc()
-
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(), ... )
-
@EL-europ
ncurses ist kein C-Sprachstandard, ein standardkonformer Compiler muss diese Bibliothek nicht anbieten.
-
@Wutz Danke für die Info
-
Danke @DirkB für die ausführliche antwort. Aber mir kommen weitere Fragen dazu auf. Wenn du schon Antworten hast würde mir das einen Haufen "Forschungsarbeit" abnehmen.
Library bedeuted: Das der Quellcode nicht verfügbar sondern nur ein Binary auf dem Entwicklungsrechner ist?
Musss die Library auch auf dem Zielsystem vorhanden sein?
Wenn ja, und ich den Quellcode habe und mit Einbinde muss dann trotzdem die Library auf Zielsystem vorhanden sein?
Wenn ich cross-compile, brauche ich dann die Library im "cross-Format"?
-
@Wutz sagte in Untypischer Laufzeitfehler durch malloc():
@EL-europ
ncurses ist kein C-Sprachstandard, ein standardkonformer Compiler muss diese Bibliothek nicht anbieten.Das heisst es kann auch compiler geben bei denen "-lncurses" nicht funtioniert obgleich ich die Library habe?
-
@Swordfish sagte in Untypischer Laufzeitfehler durch malloc():
Dazu brauchst Du keine Threads sondern nur etwas das Tastatureingaben ohne zu blocken erkennt. Schau nach ncurses, der funktion
getch()
undnodelay
.Der "Automove" macht erst mit der curses richtig Sinn. Ansonsten ist Geflacker durch "clearscreen" zu stark.
Aber Ich glaube die curses kommt mit schnellen Tastenanschlägen nicht so klar. wenn ich im "Automove-modus" eine Zeit (ca. 3-5min) lang spiele, stürzt das Programm einfach ab. Kannst du mir nochmal Helfen? Du hast doch den Speicher analysieren können. Vielleicht findest du wieder einen Programmfehler. Das wäre toll/* Klassikspiel snake v1.4 * Eine Schlange zum Essen steuern * ohne das diese sich nach dem zeiter Mahlzeit in den Schwanz beisst * * unter Mithilfe von https://www.c-plusplus.net/forum/ * nach einer Vorlage von https://www.c-howto.de/ * * @author: El Europ alias momefilo * Lizenz: GPL */ #include <stdio.h> #include <stdlib.h> #include <curses.h> #include <math.h> #include <time.h> #include <string.h> #include <unistd.h> /*Tasten zur Steuerung */ #define MYKEY_UP 'w' #define MYKEY_DOWN 's' #define MYKEY_LEFT 'd' #define MYKEY_RIGHT 'a' /* Figuren auf dem Spielfeld */ #define BLANK 0 /* Leeres Feld */ #define HEAD 1 /* Kopf der Schlange */ #define TAIL 2 /* Schwanz der Schlange */ #define FOOD 3 /* Essen fuer die Schlange damit der Schwanz waechst */ /* Spielevariablen */ int autoMove = 0; /* Beweggung ohne Tastendruck*/ int xoffset = 0; /* Zur Spielfeldzentrierung */ int yoffset = 0; /* Dito */ int tailcount = 0; /* Laenge des Schwanzes */ int points = 0; /* Erreichte Punkte */ int width; /* Spielfelddimension */ int height; /* Dito */ char richtung; /* Die aktuelle Richtung der Schlange */ /* Position des HEAD */ struct position{ int x; int y; } pos; /* Position des HEAD */ /* Initialisiert das Spielfeldarray * Dank an @swordfish */ void initalFeld(int **feld, int wert){ int x; int y; for(x=0; x<width; x++){ for(y=0; y<height; y++){ *(*(feld + x) + y) = wert; } } } /* Initialisiert das Schwanzarray */ void initalTail(struct position *tail){ int cnt=0; struct position myPos; myPos.x=-1; myPos.y=-1; for(cnt=0; cnt <= width * height; cnt++){ *(tail + cnt) = myPos; } } /* Setzt pos.x und pos.y Abhängig von der richtung */ void myMove(){ switch(richtung){ case MYKEY_DOWN: if(pos.y >= height -1) pos.y = 0; else ++pos.y; break; case MYKEY_UP: if(pos.y <= 0) pos.y = height -1; else --pos.y; break; case MYKEY_LEFT: if(pos.x >= width -1) pos.x = 0; else ++pos.x; break; case MYKEY_RIGHT: if(pos.x <= 0) pos.x = width -1; else --pos.x; } } /* Zeichnet den Spielfeldrand und Ueberschrift*/ void printFeld(){ int x; /* Schleifenzaehler */ /* Schriftzug mittig ueber Spielfeld */ int xoff = (width - 10) / 2; if(xoff < 0) xoff = 0; /* Schriftstil fett */ attrset(A_BOLD); /* Ueberschrift zeichnen */ mvprintw(0+yoffset, xoff+xoffset, "Snake v%d.%d", 1, 4); mvprintw(1+yoffset, xoff+xoffset, "(%d x %d)", width, height); /* Ecken Zeichnen */ mvaddch(yoffset + 3, xoffset, ACS_ULCORNER); mvaddch(yoffset + 3, xoffset + width+1, ACS_URCORNER); mvaddch(yoffset+height + 4, xoffset, ACS_LLCORNER); mvaddch(yoffset+height + 4, xoffset+width + 1, ACS_LRCORNER); /* Linke und rechte Kante zeichnen */ for(x=4; x<=height +3; x++){ mvaddch(yoffset+x, xoffset, ACS_VLINE); mvaddch(yoffset+x, xoffset+width + 1, ACS_VLINE); } /* Obere und untere Kante zeichnen */ for(x=1; x<=width; x++){ mvaddch(yoffset+3, xoffset+x, ACS_HLINE); mvaddch(yoffset+height + 4, xoffset+x, ACS_HLINE); } } /* Setzt FOOD zufaellig aufs Spielfeld */ void setFood(int **feld){ int x; int y; do{ x=rand() % width; y=rand() % height; }while(*(*(feld + x) + y) != BLANK); feld[x][y] = FOOD; /* Spielfeld aktualisieren */ attrset(A_BOLD|COLOR_PAIR(2)); /* Farbe blau fuer FOOD */ mvaddch(yoffset+y + 4, xoffset+x + 1, ACS_DIAMOND); /*Figur FOOD zeichnen */ attrset(A_BOLD|COLOR_PAIR(1)); /* Farbe wieder gelb */ } /* Zum Start des Spiels. Zeichnet spielfeld neu und setzt HEAD/FOOD/points */ void resetGame(int **feld, struct position *tail){ clear(); /* Bildschirm leeren */ printFeld(); /*Ueberschrift und Rand zeichnen */ initalFeld(feld, BLANK); /* Spielfeldarray initialisieren */ initalTail(tail); /* Schwanz initialisieren */ /* HEAD platzieren */ pos.x = width / 2; pos.y = height / 2; feld[pos.x][pos.y] = HEAD; /* Feldarray aktualisieren */ mvaddch(yoffset+pos.y + 4, xoffset+pos.x + 1, '@'); /* Figur zeichnen */ /* FOOD Platzieren */ setFood(feld); /* Spielvariablen ruecksetzten */ tailcount = 0; points = 0; /* Punkte anzeigen */ mvprintw(yoffset+height+5,xoffset,"Punkte %d",points); /* Wenn Option Automove aktiv */ if(autoMove)nodelay(stdscr, TRUE); } /* Fuehrt Spielzug auf feldarray aus aktualisiert das tailarray * und zeichnet die Figuren und Punktzahl * Beisst sich die Schlange in den Schwanz return 0 (Game over) sonst 1*/ int setFeld(int **feld, struct position *tail, int bevor){ int ret = 1; /* Wenn die Schlange in den Schwanz beisst return 0 */ int figur; /* Die Figur auf der neuen Position im Feld (BLANK,TAIL,FOOD) */ static int hasFood = 0; /* Wenn die Schlange grad was zu Essen fand */ int cnt; struct position tailPos; /* Position für tail-Elemente */ /* Vor dem Zug. Der HEAD verlaesst die Position pos * Den Schwanz verschieben und zeichnen */ if(bevor){ /* Die von HEAD verlassende position pos leeren */ feld[pos.x][pos.y] = BLANK; mvaddch(yoffset+pos.y+4, xoffset+pos.x + 1, ' '); /* Hat die Schlange ueberhaupt einen Schwanz */ if(tailcount > 0){ /* Hat die Schlange zuvor kein FOOD gefunden verschiebe Schwanz */ if(! hasFood){ /*Schwanzende loeschen */ tailPos = tail[0]; feld[ tailPos.x ][ tailPos.y ] = BLANK; mvaddch(yoffset+tailPos.y+4, xoffset+tailPos.x + 1, ' '); /* Wenn Schwanzlaenge > 1 verschieben */ if(tailcount > 1){ for(cnt=0; cnt<tailcount-1; cnt++) tail[cnt] = tail[cnt +1]; tail[tailcount - 1] = pos; /* Neue Position vor dem HEAD */ } /* Sonst einfach neuen Anfang setzten */ else tail[0] = pos; /* oder FOOD wurde zuvor gefunden und wird jetzt verdaut */ }else hasFood = 0; /* Schwanzanfang im Spielfeld setzen und zeichnen */ feld[pos.x][pos.y] = TAIL; mvaddch(yoffset+pos.y + 4, xoffset+pos.x + 1, '*'); } /* Oder nach dem Zug. Der HEAD besetzt die Position pos * Figuren auswerten und neu Zeichnen*/ }else{ figur = feld[pos.x][pos.y]; /* was ist aktuell auf der neuen Position */ if(figur==TAIL) ret = 0; /* TAIL: In den Schwanz gebissen - Gameover */ else if(figur==FOOD){ /* FOOD: Was zu Essen gefunden */ tail[tailcount] = pos; /* Neues tail-Element einfügen */ setFood(feld); /* Frischen Essen servieren :) */ hasFood = 1; /* Fuer nachsten Zug vormerken */ ++tailcount; points += 10; } /* HEAD zeichnen und im Spielfeldarray setzten */ feld[pos.x][pos.y] = HEAD; mvaddch(yoffset+pos.y + 4,xoffset+pos.x+1, '@'); /* Punke anzeigen */ mvprintw(yoffset+height+5,xoffset,"Punkte %d",points); } return ret; } /* Beendet curses */ void quit(){endwin();} int main(int argc, char *argv[]){ int **feld; /* Spielfeld */ struct position *tail; /* Schwanz */ int cnt; /* Schleifenvariable */ int myx; /* Zur Spielfeldzentrierung */ int myy; /* Dito */ srand( (unsigned int) time(NULL) ); /* Zufall initialisieren */ /* Bei unkorrekten Komandozeilenargumenten erfolgt Abbruch */ if(argc > 2){ width=atoi(argv[1]); height=atoi(argv[2]); if(width > 1 && height > 1){ feld = malloc(width * sizeof(int *)); for(cnt=0; cnt<width; cnt++)feld[cnt]=malloc(height * sizeof(int)); tail = (struct position *)malloc((width * height +1) *(sizeof(struct position))); }else{ printf("Aufruf mit 'snake breite hoehe [option a=Automove]' min: 'snake 2 2'\n"); printf("Steuerung mit 'w' 'a' 's' 'd' Ende 'x'\n"); return 0; } }else{ printf("Aufruf mit 'snake breite hoehe [option a=Automove]' min: 'snake 2 2'\n"); printf("Steuerung mit 'w' 'a' 's' 'd' Ende 'x'\n"); return 0; } /* Ist Automove aktiviert ?*/ if(argc>3)autoMove = 1; /* curses initialisieren */ initscr(); atexit(quit); curs_set(0); noecho(); start_color(); init_pair(1, COLOR_YELLOW, COLOR_BLACK); init_pair(2, COLOR_CYAN, COLOR_BLACK); bkgd(COLOR_PAIR(1)); cbreak(); keypad(stdscr, TRUE); /*Spielfeld zentrieren */ getmaxyx(stdscr ,myy, myx); if(width>myx-4 || height>myy-16){ /* Spielfeld zu gross fuer Screen */ attrset(A_BOLD); mvprintw(LINES/2,0,"Das Spielfeld ist zu gross max:%d x %d",myx-4,myy-16); mvprintw(LINES/2+1,0,"mit taste beenden und mit max neu aufrufen"); getch(); return 0; } xoffset = myx/2 - width/2 -1; yoffset = myy/2 - height/2; /* Spiel starten */ resetGame(feld, tail); while(richtung != 'x'){ /* Wenn Automove aktiv sleep() aktivieren*/ if(autoMove){ if(richtung == 'a' || richtung == 'd')usleep(100000); else if(richtung == 'w' || richtung == 's')usleep(150000); } /* Neue Richtung einlesen */ char key = getch(); if(key=='w' || key=='a' || key=='s' || key=='d' || key=='x') richtung = key; /* Spielfeld vor dem Zug setzten. HEAD verlaesst die pos */ setFeld(feld, tail, 1); /* Zug Ausführen */ myMove(); /* Spielfeld nach dem Zug setzten * gewonnen und verloren auswerten */ if(! setFeld(feld, tail, 0) || points > width*height*10 -30){ /* damit auf getch() gewartet wird */ nodelay(stdscr, FALSE); /* Schriftfarbe aendern */ attrset(A_BOLD|COLOR_PAIR(2)); /* Gewonnen ?*/ if(points > width*height*10 -30) mvprintw(yoffset+height/2,xoffset+2,"Sie haben gewonnen! Nochmal j"); /* oder verloren */ else mvprintw(yoffset+height/2,xoffset+2,"Sie haben verloren! Nochmal j"); attrset(A_BOLD|COLOR_PAIR(1)); key = getch(); if(key != 'j') break; /*Spiel verlassen */ else resetGame(feld,tail); /*Spiel neu starten */ } /* Naechster/neuer zug beginnt */ } quit(); /* curses beenden */ }