Untypischer Laufzeitfehler durch malloc()



  • @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



  • @firefly Ja, genau. Danke



  • @Swordfish
    mit "#if defined var" fragt der Präprozessor wo genau nach ob die Variable var gesetzt ist?
    mit "#pragma com(lib, 'str')" werden Compileroptionen gesetzt. Welche Bedeutung haben com, lib und str
    mit "#define Sleep..." wird Sleep(x) durch usleep((x) * 10000) ersetzt. Gibt es in win eine Funktion namens "Sleep(int x)"? oder wie funktioniert das delay bei compilierung auf win?

    Bei der Analyse hab ich curses ausgelassen, das ist ein eigenes Thema denke ich, und komme zu folgendem Ergebnis.
    Dein Code scheint Objektorientiert zu sein?
    ich hab vergessen den Speicher wieder frei zu geben☠

    Bezüglich enum:
    Der mir ersichtliche Vorteil ist das ich sie im Editor "zusammenklappen" kann.
    Jürgen Wolf schreibt das Enumerationen mit Bezeichner, im Gegensatz zu #define vom Debugger symbolisch dargestellt werden!
    Was meint er damit?

    bezüglich size_t in snake_t.lenght und snake_t.new_lenght
    was ist hier der Unterschied zu int, oder warum benutzt du size_t statt int?

    Was mir hier unbedeutend ist (curses) aber dich vielleicht interessiert
    Das Resize des Terminal funktioniert bei mir nicht
    Wenn ich curses nicht initialisiere und dem restlichen code nach "if(resizeterm.." auskommentiere,
    funktioniert die Ausgabe das das Terminal nicht resized werden konnte.
    Wenn ich das komplette Programm mit werten die zu resize führen aufrufe, wird nicht resized,
    das Spiel startet, aber was über rechts hinausgeht zeichnet er dann am linken Rand weiter.



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

    mit "#if defined var" fragt der Präprozessor wo genau nach ob die Variable var gesetzt ist?

    Der Präprozessor kennt keine in C deklarierten Variablen. defined _MSC_VER gibt 1 wenn das Präprozessorsymbol _MSC_VER definiert wurde. Das ist der Fall wenn mit dem Microsoft C++-Compiler übersetzt wird.

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

    mit "#pragma com(lib, 'str')" werden Compileroptionen gesetzt. Welche Bedeutung haben com, lib und str

    https://docs.microsoft.com/en-us/cpp/preprocessor/comment-c-cpp?view=msvc-160

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

    mit "#define Sleep..." wird Sleep(x) durch usleep((x) * 10000) ersetzt. Gibt es in win eine Funktion namens "Sleep(int x)"?

    Genau. Und ja.

    https://docs.microsoft.com/de-de/windows/win32/api/synchapi/nf-synchapi-sleep

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

    Bezüglich enum:
    Der mir ersichtliche Vorteil ist das ich sie im Editor "zusammenklappen" kann.
    Jürgen Wolf schreibt das Enumerationen mit Bezeichner, im Gegensatz zu #define vom Debugger symbolisch dargestellt werden!
    Was meint er damit?

    Erster Beweggrund ist Namen zu haben anstatt im ganzen Code Magic Numbers zu verteilen. Zweitens hat man dadurch auch einen Typ der dabei hilft zu dokumentieren welche Werte (bzw. symbolische Konstanten) für welche Variablen sinnvoll sind. Was Wolf meint? Wahrscheinlich daß man eben statt den Zahlen einen Bezeichner sieht.

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

    bezüglich size_t in snake_t.lenght und snake_t.new_lenght
    was ist hier der Unterschied zu int, oder warum benutzt du size_t statt int?

    Für size_t ist garantiert daß der Typ groß genug für jedes mögliche Objekt im Speicher ist.

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

    Was mir hier unbedeutend ist (curses) aber dich vielleicht interessiert
    Das Resize des Terminal funktioniert bei mir nicht

    Ja, bei mir auch nicht. Hab' es mit (u)rxvt und xterm probiert.

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

    Dein Code scheint Objektorientiert zu sein?

    Bingo. Mir hätte zwar die Erkenntnis gereicht daß ich ohne globale Variablen auskomme und jede Funktion eine klare Aufgabe hat, aber ... 🙂
    Siehst Du die Vorteile?



  • @Swordfish
    Also kann ich mit "if defined" nur Eigenschaften des eingesetzten Compilers abfragen?
    Ja, ich glaube du hast alles was geht in Funktionen ausgelagert
    Für mich als Anfänger wird der Code dadurch schwerer lesbar, aber ich will ja was lernen und habe den Autor an der Seite. Ich bin den Code Zeile für Zeile durch gegangen, aber weshalb der komplette body gezeichnet wird kann ich noch nicht nachvollziehen. Da ist mir dann die Luft ausgegangen nach dem ich alle anderen zurückgestellten Fragen (ausser curses) geklärt habe 🙂
    Danke das du dir die Mühe gemacht hast und einen kompletten Code zum Nachvollziehen geschrieben hast. Das hilft mir sehr



  • @Swordfish
    Ich muss mich korrigieren
    Dein Code ist viel lesbarer als meiner. Ohne die Kommentare würde mein Code völlig unleserlich sein, man bräuchte Notizen und müsste sich viel mehr im Gedächtnis behalten; deiner ist auch ohne Kommentare lesbar. Vielleicht schwierig als Anfänger wegen den vielen Funktionen, aber ohne Kommentare, Notizen usw... lesbar



  • @Swordfish
    Ach, eine Frage hab ich vergessen:

    for (size_t i = snake->length - 1; i; --i)
    

    Weshalb funktioniert diese Schleife obgleich die Abbruchbedingung lediglich die Zählvariable 'i' ist?