Untypischer Laufzeitfehler durch malloc()



  • Dazu brauchst Du keine Threads sondern nur etwas das Tastatureingaben ohne zu blocken erkennt. Schau nach ncurses, der funktion getch() und nodelay.



  • @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() und nodelay.

    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 */
    }
    


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

    Library bedeuted: Das der Quellcode nicht verfügbar sondern nur ein Binary auf dem Entwicklungsrechner ist?

    Jain.
    Die Library ist compiliert, du kannst dennoch den Quellcode dafür haben.

    Musss die Library auch auf dem Zielsystem vorhanden sein?

    Das kommt auf das Format an.
    Es gibt statische Librarys - die werden mit deinem Code zu einer Exe gelinkt und sind somit Teil des Programms.
    Und es gibt dynamischel Librarays (DLL "Dynamic Linked Library") - die werden erst zur Laufzeit eingebunden und sind extra Dateien.
    Da DLL extra Dateien sind, kann man sie leicht durch neuere ersetzen, bzw können sich mehrere Programme eine DLL teilen.
    (In C gehört zu einer DLL noch eine lib, die die Aufrufe in die DLL kapselt)

    Wenn ja, und ich den Quellcode habe und mit Einbinde muss dann trotzdem die Library auf Zielsystem vorhanden sein?

    s.o.

    Wenn ich cross-compile, brauche ich dann die Library im "cross-Format"?

    Die Library muss - wie auch alle anderen Codebestandteile - für das Zielsystem geeignet sein.
    Wenn du den Quellcode hast, kannst du die selber erstellen, wenn nicht downloaden oder kaufen oder fluchen.



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

    Das heisst es kann auch compiler geben bei denen "-lncurses" nicht funtioniert obgleich ich die Library habe?

    -lncurses ist eine Linkeroption (der wird vom Compiler aufgerufen). Du musst dann noch mit einer anderen Option weitere Pfade angeben, wo er nach Librarys suchen soll.
    Du kannst auch angeben, wo der Compiler bei #include "keinsystemheader.h" suchen soll.



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

    Es gibt statische Librarys - die werden mit deinem Code zu einer Exe gelinkt und sind somit Teil des Programms.
    Und es gibt dynamischel Librarays (DLL "Dynamic Linked Library") - die werden erst zur Laufzeit eingebunden und sind extra Dateien.
    Da DLL extra Dateien sind, kann man sie leicht durch neuere ersetzen, bzw können sich mehrere Programme eine DLL teilen.
    (In C gehört zu einer DLL noch eine lib, die die Aufrufe in die DLL kapselt)

    Da er ein Ubuntu nutzt, gibt es dort natürlich keinerlei DLLs. Unter UNIX/Linux wird das Thema Libraries etwas anders gehandhabt als unter Windows.

    Bei Systemen mit dem Programmformat ELF (es gibt auch noch andere wie XCOFF, Mach-O, …) verhält es sich wie folgt.

    • Es gibt Archives mit der Endung .a, das ist nichts anderes als eine Sammlung an übersetzten Dateien, die in einer Datei archiviert werden. Sie werden als statische Libraries genutzt.
    • Es gibt Shared Libraries mit der Endung .so. Diese werden erst beim Programmstart geladen, und das System nutzt sie um die Symbole in Programm aufzulösen. Danach kann das Programm genutzt werden.
    • *.so Dateien können auch während der Laufzeit über dlopen in den Andressraum des Programms geladen werden.

    Alle *.so Libraries müssen auf dem Zielsystem vorhanden sein. Das kann man bei Ubuntu entweder mit Hilfe eines *.deb Packets erreichen, dann installiert das System ggf. die notwendigen Library Pakete nach, oder erstelle ein Flatpak, Snap, … und lieferte die Libraries mit aus.



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

    -lncurses ist eine Linkeroption (der wird vom Compiler aufgerufen). Du musst dann noch mit einer anderen Option weitere Pfade angeben, wo er nach Librarys suchen soll.

    Das braucht man bei Ubuntu nicht, wenn die Library in einem der üblichen Standardverzeichnisse des Systems liegt.



  • @john-0 sagte in Untypischer Laufzeitfehler durch malloc():

    Unter IX wird das Thema Libraries etwas anders gehandhabt als unter Windows.

    Ja. Totaaaaal anders. Statische Libraries und dynamisch geladene. Gaaaanz anders.



  • Danke @DirkB @john-0 @Swordfish für die Antworten. Das Thema Librarys ist etwas komplex und ich finde mich nicht zurecht darin. Wenn ich @john-0 richtig verstanden habe gibt es dynamische *.so und statische *.a librarys auf meinem System. Wenn ich dynamische libs einbinde, müssen die auf dem Zielsystem zur Laufzeit vorhanden sein und bei statischen nicht? da ich sie "fest eincompiliert" habe?
    Wenn ich jetzt für ein Win-Ziel crosscompilieren will, was muss ich da bezüglich der Librarys beachten? Welche Hürden gibt es da?
    Die von @DirkB erwähnten DLL's sind das Win-gegenstück zu den *.so dateien? Wenn ja, wie heiseßen die statischen .*a Dateien auf Win?

    @Swordfish Ist dein Beitrag ironisch, oder stimmst du @DirkB energisch 🙂 zu, bezüglich Win/IX-librarys



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

    Ja. Totaaaaal anders. Statische Libraries und dynamisch geladene. Gaaaanz anders.

    Wie ich immer diese geistreichen Anmerkungen liebe. Dir ist hoffentlich bewusst, dass es unter UNIX auch statisch shared Libraries gab bzw. von den Toolchains zum Teil noch unterstützt werden? Dir ist hoffentlich bewusst, dass man nicht jede UNIX Plattform das Laden von dynamic shared Libraries in das laufende Programm unterstützt? Man muss auf diesen Plattformen ein anderes DSO Format erzeugen, damit man das machen kann. macOS ist wohl der bekannteste Vertreter. Aber ja doch, es gibt keine Unterschiede.



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

    Wenn ich jetzt für ein Win-Ziel crosscompilieren will, was muss ich da bezüglich der Librarys beachten? Welche Hürden gibt es da?

    Daß die benötigten Libraries für die Zielplatform verfügbar sind oder gebaut werden können oder etwas halbwegs kompatibles verfügbar ist. (Beispiel mit PDCurses unten.)

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

    Die von @DirkB erwähnten DLL's sind das Win-gegenstück zu den *.so dateien? Wenn ja, wie heiseßen die statischen .*a Dateien auf Win?

    Unter Windows enden Statische- und Importlibraries auf .lib.


    Was fällt Dir auf wenn Du Deinen und meinen Code vergleichst?

    #include <errno.h>
    #include <stdbool.h>
    #include <stddef.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <time.h>
    
    #if defined _MSC_VER
    #    include <windows.h>
    #    undef MOUSE_MOVED
    #    include "../PDCurses/curses.h"
    #    define resizeterm resize_term
    #    if defined(_WIN64)
    #        error "PDCurses missing for x64 :("
    #    elif defined(_WIN32)
    #        ifdef _DEBUG
    #            pragma comment(lib, "../pdcurses/wincon/pdcursesd.lib")
    #        else
    #            pragma comment(lib, "../pdcurses/wincon/pdcurses.lib")
    #        endif
    #    endif
    #elif defined __linux__
    #    include <unistd.h>
    #    include <curses.h>
    #    include <string.h>
    #    define Sleep(x) usleep((x) * 1000)
    #endif
    
    void print_usage(char const *program_name)
    {
        printf("Usage: %s [WIDTH] [HEIGHT]\n\n", program_name);
    }
    
    bool str2int(char const *str, int *number)
    {
        errno = 0;
        char *endptr;
        *number = strtol(str, &endptr, 10);
        return errno != ERANGE && *endptr == '\0';
    }
    
    void curses_init()
    {
        initscr();
        refresh();
        noecho();
        leaveok(stdscr, true);
        nodelay(stdscr, true);
        keypad(stdscr, true);
        curs_set(0);
    }
    
    typedef struct coord_tag {
        int x;
        int y;
    } coord_t;
    
    typedef enum direction_tag {
        DIRECTION_RIGHT  = KEY_RIGHT,  // +x
        DIRECTION_LEFT   = KEY_LEFT,   // -x
        DIRECTION_DOWN   = KEY_DOWN,   // +y
        DIRECTION_UP     = KEY_UP      // -y
    } direction_t;
    
    enum {
        INITIAL_LENGTH  = 3,
        GROWTH_DELTA    = 8,
        DELAY           = 100  // ms
    };
    
    typedef struct snake_tag {
        coord_t     *body;
        size_t       length;
        direction_t  direction;
        bool         is_alive;
        size_t       new_length;
        int          delay;
    } snake_t;
    
    coord_t coord_create(int x, int y)
    {
        coord_t coord = { x, y };
        return coord;
    }
    
    bool snake_create(snake_t *snake, coord_t initial_position, direction_t direction)
    {
        coord_t *body = malloc(sizeof *body);
        if (!body)
            return false;
    
        snake->body        = body;
        snake->body[0]     = initial_position;
        snake->length      = 1;
        snake->direction   = direction;
        snake->is_alive    = true;
        snake->new_length  = INITIAL_LENGTH;
        snake->delay       = DELAY;
        return true;
    }
    
    bool snake_in_body(snake_t const *snake, coord_t position, bool include_head)
    {
        for (size_t i = include_head ? 0 : 1; i < snake->length; ++i)
            if (snake->body[i].x == position.x && snake->body[i].y == position.y)
                return true;    
        return false;
    }
    
    bool snake_is_alive(snake_t *snake)
    {
        coord_t head = snake->body[0];    
        if (!head.x || !head.y || head.x == COLS - 1 || head.y == LINES - 1)
            snake->is_alive = false;
        else if (snake_in_body(snake, head, false))
            snake->is_alive = false;
        return snake->is_alive;
    }
    
    void snake_update_direction(snake_t *snake, direction_t new_direction)
    {
        if (new_direction != DIRECTION_RIGHT && new_direction != DIRECTION_LEFT &&
            new_direction != DIRECTION_DOWN  && new_direction != DIRECTION_UP)
            return;
    
        if (snake->direction == new_direction)
            return;
    
        if (snake->direction == DIRECTION_RIGHT && new_direction == DIRECTION_LEFT  ||
            snake->direction == DIRECTION_LEFT  && new_direction == DIRECTION_RIGHT ||
            snake->direction == DIRECTION_UP    && new_direction == DIRECTION_DOWN  ||
            snake->direction == DIRECTION_DOWN  && new_direction == DIRECTION_UP)
        {
            snake->is_alive = false;
            return;
        }
    
        snake->direction = new_direction;
    }
    
    bool snake_move(snake_t *snake)
    {
        coord_t new_head_position = snake->body[0];
        coord_t old_tail_position = snake->body[snake->length - 1];
    
        switch (snake->direction) {
            case DIRECTION_RIGHT:
                ++new_head_position.x;
                break;
            case DIRECTION_LEFT:
                --new_head_position.x;
                break;
            case DIRECTION_DOWN:
                ++new_head_position.y;
                break;
            case DIRECTION_UP:
                --new_head_position.y;
                break;
        }
        mvaddch(new_head_position.y, new_head_position.x, '*');
    
        for (size_t i = snake->length - 1; i; --i)
            snake->body[i] = snake->body[i - 1];
        snake->body[0] = new_head_position;
    
        if (snake->length < snake->new_length) {
            coord_t *new_body = realloc(snake->body, (snake->length + 1) * sizeof(*new_body));
            if (!new_body)
                return false;
            snake->body = new_body;
            snake->body[snake->length++] = old_tail_position;
        }
        else mvaddch(old_tail_position.y, old_tail_position.x, ' ');
    
        refresh();
        return true;
    }
    
    void snake_grow(snake_t *snake)
    {
        snake->new_length = snake->length + GROWTH_DELTA;
    }
    
    bool snake_try_eat(snake_t const *snake, coord_t food)
    {
        return snake->body[0].x == food.x && snake->body[0].y == food.y;
    }
    
    void snake_delay(snake_t const *snake)
    {
        int delay = snake->delay;
        if (snake->direction == DIRECTION_DOWN || snake->direction == DIRECTION_UP)
            delay = (int)(delay * 1.4);
        Sleep(delay);
    }
    
    void snake_delete(snake_t *snake)
    {
        free(snake->body);
    }
    
    coord_t food_create(snake_t const *snake, int max_x, int max_y)
    {
        coord_t food;
        do food = coord_create(rand() % (max_x - 2) + 1, rand() % (max_y - 2) + 1);
        while (snake_in_body(snake, food, true));
        return food;
    }
    
    int main(int argc, char **argv)
    {
        int desired_width = 80;
        int desired_height = 25;
    
        if (argc > 3 ||
            argc > 1 && !str2int(argv[1], &desired_width) ||
            argc > 2 && !str2int(argv[2], &desired_height))
        {
            print_usage(argv[0]);
            return EXIT_FAILURE;
        }
    
        curses_init();
    
    #ifdef __linux__
        {
            char buffer[80];
            sprintf(buffer, "resize %3d %3d > /dev/null  2>&1", desired_height, desired_width);
            system(buffer);
        }
    #endif
    
        if (resizeterm(desired_height, desired_width) == ERR) {
            endwin();
            fprintf(stderr, "Couldn't resize Terminal to %d x %d :(\n\n", desired_width, desired_height);
            return EXIT_FAILURE;
        }
    
        snake_t snake;
        if (!snake_create(&snake, coord_create(COLS / 2, LINES / 2), DIRECTION_RIGHT)) {
            endwin();
            fputs("Not enough memory :(\n\n", stderr);
            return EXIT_FAILURE;
        }
        
        box(stdscr, 0, 0);
        srand((unsigned) time(NULL));
        coord_t food = food_create(&snake, COLS, LINES);
        mvaddch(food.y, food.x, 'O');
    
        do {
            snake_update_direction(&snake, getch());
            
            if (!snake_move(&snake)) {
                snake_delete(&snake);
                endwin();
                fputs("Not enough memory :(\n\n", stderr);
                return EXIT_FAILURE;
            }
    
            if (snake_try_eat(&snake, food)) {
                snake_grow(&snake);
                food = food_create(&snake, COLS, LINES);
                mvaddch(food.y, food.x, 'O');
            }
    
            snake_delay(&snake);
        } while (snake_is_alive(&snake));
    
        snake_delete(&snake);
        endwin();
    }
    
    


  • @Swordfish
    super👍🏻 Danke für die Arbeit, ich schau mir morgen /also heute am Tage an den code an



  • @Swordfish Ich fange grade an deinen code zu analysieren. Der erste unterschied der mir auffällt ist die bedingte Compilierung. Bei mir lässt es sich nicht compilieren

    gcc -o test test.c
    /usr/bin/ld: /tmp/ccHS05xB.o: in function `curses_init':
    test.c:(.text+0xbe): undefined reference to `initscr'
    /usr/bin/ld: test.c:(.text+0xc5): undefined reference to `stdscr'
    /usr/bin/ld: test.c:(.text+0xcd): undefined reference to `wrefresh'
    /usr/bin/ld: test.c:(.text+0xd2): undefined reference to `noecho'
    /usr/bin/ld: test.c:(.text+0xd9): undefined reference to `stdscr'
    /usr/bin/ld: test.c:(.text+0xe6): undefined reference to `leaveok'
    /usr/bin/ld: test.c:(.text+0xed): undefined reference to `stdscr'
    /usr/bin/ld: test.c:(.text+0xfa): undefined reference to `nodelay'
    /usr/bin/ld: test.c:(.text+0x101): undefined reference to `stdscr'
    /usr/bin/ld: test.c:(.text+0x10e): undefined reference to `keypad'
    /usr/bin/ld: test.c:(.text+0x118): undefined reference to `curs_set'
    /usr/bin/ld: /tmp/ccHS05xB.o: in function `snake_is_alive':
    test.c:(.text+0x275): undefined reference to `COLS'
    /usr/bin/ld: test.c:(.text+0x285): undefined reference to `LINES'
    /usr/bin/ld: /tmp/ccHS05xB.o: in function `snake_move':
    test.c:(.text+0x425): undefined reference to `stdscr'
    /usr/bin/ld: test.c:(.text+0x42f): undefined reference to `wmove'
    /usr/bin/ld: test.c:(.text+0x43b): undefined reference to `stdscr'
    /usr/bin/ld: test.c:(.text+0x448): undefined reference to `waddch'
    /usr/bin/ld: test.c:(.text+0x534): undefined reference to `stdscr'
    /usr/bin/ld: test.c:(.text+0x53e): undefined reference to `wmove'
    /usr/bin/ld: test.c:(.text+0x54a): undefined reference to `stdscr'
    /usr/bin/ld: test.c:(.text+0x557): undefined reference to `waddch'
    /usr/bin/ld: test.c:(.text+0x55e): undefined reference to `stdscr'
    /usr/bin/ld: test.c:(.text+0x566): undefined reference to `wrefresh'
    /usr/bin/ld: /tmp/ccHS05xB.o: in function `main':
    test.c:(.text+0x7d9): undefined reference to `resizeterm'
    /usr/bin/ld: test.c:(.text+0x7e3): undefined reference to `endwin'
    /usr/bin/ld: test.c:(.text+0x81a): undefined reference to `LINES'
    /usr/bin/ld: test.c:(.text+0x82b): undefined reference to `COLS'
    /usr/bin/ld: test.c:(.text+0x863): undefined reference to `endwin'
    /usr/bin/ld: test.c:(.text+0x894): undefined reference to `stdscr'
    /usr/bin/ld: test.c:(.text+0x8c1): undefined reference to `wborder'
    /usr/bin/ld: test.c:(.text+0x8dc): undefined reference to `LINES'
    /usr/bin/ld: test.c:(.text+0x8e2): undefined reference to `COLS'
    /usr/bin/ld: test.c:(.text+0x90d): undefined reference to `stdscr'
    /usr/bin/ld: test.c:(.text+0x917): undefined reference to `wmove'
    /usr/bin/ld: test.c:(.text+0x923): undefined reference to `stdscr'
    /usr/bin/ld: test.c:(.text+0x930): undefined reference to `waddch'
    /usr/bin/ld: test.c:(.text+0x937): undefined reference to `stdscr'
    /usr/bin/ld: test.c:(.text+0x93f): undefined reference to `wgetch'
    /usr/bin/ld: test.c:(.text+0x97c): undefined reference to `endwin'
    /usr/bin/ld: test.c:(.text+0x9d8): undefined reference to `LINES'
    /usr/bin/ld: test.c:(.text+0x9de): undefined reference to `COLS'
    /usr/bin/ld: test.c:(.text+0xa09): undefined reference to `stdscr'
    /usr/bin/ld: test.c:(.text+0xa13): undefined reference to `wmove'
    /usr/bin/ld: test.c:(.text+0xa1f): undefined reference to `stdscr'
    /usr/bin/ld: test.c:(.text+0xa2c): undefined reference to `waddch'
    /usr/bin/ld: test.c:(.text+0xa66): undefined reference to `endwin'
    collect2: error: ld returned 1 exit status
    


  • Das ist ein linker fehler und kein compiler fehler du musst die lib angeben (vermutlich -lncurses)



  • @EL-europ
    😵 Mein Fehler. hab -lncurses vergessen. programm lässt sich fehlerfrei compilieren