problem beim rendern von bmp mit sdl



  • Hallo

    Ich habe mir mal ein paar kleine klassen für sdl geschrieben und komme nun leider nicht weiter. Ich habe eine Klasse für nicht animierte Sprites und diese möchte ich rendern leider bekoome ich dabei immer folgenden Fehlermeldung:

    Unhandled exception at 0x100226d9 in sdl_init.exe: 0xC0000005: Access violation reading location 0xcdcdcdf9.

    Der Code zum Aufruf sieht so aus:

    sdl::framework frame_work;
    	sdl::static_nonanimated_sprite pic;
    
    	frame_work.init(640, 480, 32, false);
    	pic.load("background.bmp");
    	pic.render();
    	frame_work.flip();
    
    	SDL_Delay(4000);
    	frame_work.quit();
    	frame_work.Del();
    	return 0;
    

    Wenn ich den Funktionsuafruf render weglassen, dann klappt alles. Es scheint sich also dort ein Fehler eingeschliechen zu haben. Also habe ich den Debugger angeworfen und folgenden Code bin ich durchgegangen:

    sdl::static_nonanimated_sprite::static_nonanimated_sprite(void)
    {
    
    }
    
    sdl::static_nonanimated_sprite::~static_nonanimated_sprite(void)
    {
    	SDL_FreeSurface(p_image);
    }
    
    void sdl::static_nonanimated_sprite::load(const std::string &file_name, const int pos_x, const int pos_y)
    {
    	p_screen = get_framework()->get_screen();
    	SDL_Surface* p_temp_image = SDL_LoadBMP(file_name.c_str());
    
    	if(!p_temp_image)
    	{
    		std::cout<<"Fehler beim Laden von: "<<file_name<<std::endl<<"Fehlermeldung: "<<SDL_GetError()<<std::endl;
    		get_framework()->quit();
    		exit (1);
    	} // if(!p_image)
    	else
    	{
    		p_image = SDL_DisplayFormat(p_temp_image);
    		SDL_FreeSurface(p_temp_image); //hier lande ich (siehe Text unten)
    	}
    	rect.x = 0;
    	rect.y = 0;
    	rect.w = p_image->w;
    	rect.h = p_image->h;
    }
    
    void sdl::static_nonanimated_sprite::set_colorkey(int r, int g, int b)
    {
    	SDL_SetColorKey(p_image, SDL_SRCCOLORKEY, SDL_MapRGB(p_image->format, r, b, g));
    }
    
    void sdl::static_nonanimated_sprite::render()
    {
    	SDL_BlitSurface(p_image, NULL, p_screen, &rect); //hier bin ich reingesprungen und wenn ich nun auf 
    //einen Schritt weiter geklickt habe, dann erschien die 
    //Fehlermeldung und der Pfeil des Debuggers springt in die 
    //Funktion load (ich habe die Stelle markiert)}
    

    Das verstehe ich einfach nicht.

    Vielen Dank für eure Hilfe.

    chrische



  • Da ist wohl was gröber daneben gegangen. 0xcdcdcdf9 ... also 0xcdcdcdcd ist der Wert den MSVC für den Speicher von neuen, uninitialisierten Objekten verwendet.

    Irgendwo wurde also entweder das 1. Byte eines nicht initialisierten Zeigers überschrieben, oder, wahrscheinlicher, es wurde auf (Zeiger + 44 Byte) zugegriffen, wobei der Zeiger eben nicht initialisiert war.

    Ich würde vorschlagen du steppst nochmal nach static_nonanimated_sprite::render() rein, und siehst dir alle Membervariablen der Klasse an. Dort wo noch 0xcdcdcdcd drinsteht ist vermutlich dein Problem zuhause.

    BTW: 0xcdcdcdcd ist 3452816845 (unsigned) oder -842150451 (signed)



  • Hallo

    Ich habe zwar nicht genau verstanden, was du geschrieben hast, aber du hattest natürlich recht: es gibt eine Variable mit dem angesprochenen Inhalt. Es handelt sich dabei um p_screen. Diese Variable wrd in dieser Methode mit Inhalt gefüllt:

    bool sdl::framework::init(const int screen_width, const int screen_height, const int color_depth, const bool will_be_fullscreen)
    {
    	if(SDL_Init(SDL_INIT_EVERYTHING) == -1)
    	{
    		std::cout<<"SDl konnt nicht initialisiert werden"<<std::endl<<"Fehlermeldung: "<<SDL_GetError()<<std::endl;
    		quit();
    		return false;
    	} // if(SDL_Init(SDL_INIT_EVERYTHING) == -1)
    
    	if(will_be_fullscreen)
    		p_screen = SDL_SetVideoMode(screen_width, screen_height, color_depth, SDL_HWSURFACE | SDL_DOUBLEBUF | SDL_FULLSCREEN);
    	else
    		p_screen = SDL_SetVideoMode(screen_width, screen_height, color_depth, SDL_HWSURFACE | SDL_DOUBLEBUF);
    
    	if(!p_screen)
    	{
    		std::cout<<"SDl konnt nicht gesetzt werden"<<std::endl<<"Fehlermeldung: "<<SDL_GetError()<<std::endl;
    		quit();
    		return false;
    	} // if(!p_screen)
    
    	p_keystate = SDL_GetKeyState(NULL);
    
    	return true;
    }
    

    Gibt es hier irgendeinen Fehler?

    chrische



  • Ich würde sagen das einfachste wäre du steppst mal Schritt für Schritt durch.
    Interessant sind alle Stellen wo p_screen vorkommt, also z.B. sdl::framework::init, sdl::static_nonanimated_sprite::load und sdl::framework::get_screen.

    BTW. ein paar Tips: du solltest Konstruktoren verwenden... nicht nur leere Konstruktoren schreiben. Und es hilft IMO ungemein Membervariablen als solche erkennbar zu machen, z.B. indem man ein m_ voranstellt. Wenn nicht dir, dann anderen Leuten die deinen Code lesen, und wahrscheinlich auch dir wenn du den Code selbst schon ein paar Monate nichtmehr gesehen hast, und wieder was dran ändern willst.

    Beispiel:

    sdl::static_nonanimated_sprite::static_nonanimated_sprite()
    {
        m_screen = get_framework()->get_screen();
        if (!m_screen)
            FEEEEEHLER();
    
        m_image = 0; // haben wir noch nicht
        m_rect.x = 0;
        m_rect.y = 0;
        m_rect.w = 0;
        m_rect.h = 0;
    }
    

    Wenn du das "schöner" (oder "besser") schreiben willst kannst du statt den Zuweisungen auch gleich eine "initializer-list" Schreiben, sieht dann so aus:

    sdl::static_nonanimated_sprite::static_nonanimated_sprite()
        :   m_screen(get_framework()->get_screen()),
            m_image(0) // haben wir noch nicht
    {
        if (!m_screen)
            FEEEEEHLER();
    
        m_rect.x = 0;
        m_rect.y = 0;
        m_rect.w = 0;
        m_rect.h = 0;
    }
    

    Eine Fausregel mit der man üblicherweise recht gut fährt ist: nachdem der Konstruktor gelaufen ist sollte ein Objekt in einem definierten und "gültigen" Zustand sein, und man sollte alle Memberfunktionen aufrufen können ohne dass das Programm abstürzt.
    In deinem Beispiel wäre z.B. noch eine Änderung der render Methode nötig:

    void sdl::static_nonanimated_sprite::render()
    {
        if (!m_image)
            return; // kein bild, nix rendern
    
        SDL_BlitSurface(m_image, NULL, m_screen, &m_rect); 
    }
    


  • Hallo

    Ich habe nun fast alle deiner Tipps befolgt (außer mit m_) und muss sagen, dass sich das Problem nun verlagert hat:

    In der Methode init der Klasse framework wird die Membervariable p_screen gefüllt:

    bool sdl::framework::init(const int screen_width, const int screen_height, const int color_depth, const bool will_be_fullscreen)
    {
    	if(SDL_Init(SDL_INIT_EVERYTHING) == -1)
    	{
    		std::cout<<"SDl konnt nicht initialisiert werden"<<std::endl<<"Fehlermeldung: "<<SDL_GetError()<<std::endl;
    		quit();
    		return false;
    	} // if(SDL_Init(SDL_INIT_EVERYTHING) == -1)
    
    	if(will_be_fullscreen)
    		p_screen = SDL_SetVideoMode(screen_width, screen_height, color_depth, SDL_HWSURFACE | SDL_DOUBLEBUF | SDL_FULLSCREEN);
    	else
    		p_screen = SDL_SetVideoMode(screen_width, screen_height, color_depth, SDL_HWSURFACE | SDL_DOUBLEBUF);
    
    	if(!p_screen)
    	{
    		std::cout<<"SDl konnt nicht gesetzt werden"<<std::endl<<"Fehlermeldung: "<<SDL_GetError()<<std::endl;
    		quit();
    		return false;
    	} // if(!p_screen)
    
    	p_keystate = SDL_GetKeyState(NULL);
    
    	return true;
    }
    

    Das klappt auch einwandfrei, aber wenn ich nun im c'tor von static_nonanimated_sprite die dortige Membervariable p_screen füllen will, wid das nichts:

    sdl::static_nonanimated_sprite::static_nonanimated_sprite(void)
    {
    	p_screen = get_framework()->get_screen();
    	p_image = NULL;
    	rect.x = 0;
    	rect.y = 0;
    	rect.w = 0;
    	rect.h = 0;
    }
    

    (Ich habe es nicht in Initialisierungsliste bekommen: eventuell, weil die Member geerbt sind?) p_screen ist auch danach immer noch NULL. get_framework sieht so aus:

    inline sdl::framework* get_framework()
    {
    	return sdl::framework::Get();
    }
    

    Der Zeiger auf das framework ist übrigens nicht NULL, sondern nur p_screen. Ich habe keine Ahnung warum.

    get_screen sieht so aus und sollte eigentlich auch nicht falsch sein:

    SDL_Surface* get_screen() const {return p_screen;}
    

    Ich hoffe, dass jemand hier weiter weiß.

    chrische



  • eventuell, weil die Member geerbt sind?

    Jo, rüchtüg, musst du in der klasse initialisieren wo sie "drin" sind, geerbte Member sollten ja schon von der Basisklassen initialisiert worden sein wenn der Konstruktor der abgeleiteten Klasse läuft.

    Ich kann nur nochmal darauf hinweisen: geht den Code Schritt für Schritt durch (im Debugger, dann siehst du genau was passiert) der p_screen vom "frameword" initialisieren sollte. Dieser wird nämlich wie's aussieht nicht ausgeführt.
    Vorher war p_screen uninitialisiert, jetzt, wo du's mit 0 initialisierst, ist er 0, d.h. er wird danach (nach dem Initialisieren) nichtmehr geschrieben. Sollte er aber.



  • Hallo

    Ich habe das Problem gefunden. framework ist von einen singleton abegleitet und nun habe ch einmal direkt ein framework erstellt und p_screen und einmal über get_framework(). Bei get_framework wird aber ein neues statisches framework ertsellt und nun habe ich versucht darauf zuzugreifen. Das ging natürlich nicht. Ich habe das nun so geändert:

    int _tmain(int argc, _TCHAR* argv[])
    {
    
    	sdl::static_nonanimated_sprite pic;
    
    	get_framework()->init(640, 480, 32, false);
    
    	pic.load("hello_world.bmp");
    	for(;;)
    	{
    		pic.render();
    		get_framework()->flip();
    	}
    
    	get_framework()->quit();
    	get_framework()->Del();
    
    	return 0;
    }
    

    Und alles passt. Member der Basisiklasse werden also in deren c'tor initialisiert. Dieser c'tor wird dementsprechend immer aufgerufen, wenn ich die geerebete Klasse konstruiere?

    chrische



  • chrische5 schrieb:

    Member der Basisiklasse werden also in deren c'tor initialisiert. Dieser c'tor wird dementsprechend immer aufgerufen, wenn ich die geerebete Klasse konstruiere?

    Äh.
    Wenn die Basisklasse einen Konstruktor hat, dann wird dieser automatisch auch ausgeführt wenn du ein Objekt einer abgeleiteten Klasse anlegst.
    Du kannst Konstruktoren für Basisklassen genauso wie Konstruktoren für Membervariablen über initializer-listen auch gezielt aufrufen, wobei du dann z.B. auch Parameter übergeben kannst. So kannst du Klassen als Basisklassen oder Member verwenden die garkeinen default Konstruktor haben.

    Variablen von eingebauten Typen (int, char, ..., Zeiger bzw. Arrays aus solchen Typen) werden dagegen NICHT automatisch initialisiert.



  • Hallo

    Da bin ch ja berihigt, dass sich mein Wissen mit deinem übereinstimmt und demnach stimmt. Vielen Dank für die sehr kompetente Hilfe.

    chrische


Anmelden zum Antworten