【问题标题】:Calling SDL "IMG_Load" a second time results in a "EXC_BAD_ACCESS (code=EXC_I386_GPFLT)"第二次调用 SDL "IMG_Load" 会导致 "EXC_BAD_ACCESS (code=EXC_I386_GPFLT)"
【发布时间】:2017-08-17 11:30:31
【问题描述】:

我在 MacOS 10.12 上使用 Xcode 8.3.3 并通过 Homebrew 作为 Dylib 安装了 SDL2

以下是稍作修改的sample code from lazy foo

我刚刚添加了第二个纹理 gTexture2 和函数 loadMedia2 以便能够重现该问题。第二次执行 IMG_Load 时,它会崩溃并显示以下消息:

EXC_BAD_ACCESS (code=EXC_I386_GPFLT)

搜索如何解决“一般保护错误”问题也没有让我进一步了解,崩溃似乎发生在 SDL 内部。我可能在这里真的误解了导致这个问题的东西,并且真的欢迎任何帮助。

真正令人困惑的是,它并不总是崩溃,只有大约 3 次中的 2 次。

崩溃似乎发生在SDL_AllocFormat_REAL ()内部:

这是代码示例。

/*This source code copyrighted by Lazy Foo' Productions (2004-2015)
 and may not be redistributed without written permission.*/

//Using SDL, SDL_image, standard IO, and strings
#include <SDL.h>
#include <SDL_image.h>
#include <stdio.h>
#include <string>

//Screen dimension constants
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;

//Starts up SDL and creates window
bool init();

//Loads media
bool loadMedia();

//Frees media and shuts down SDL
void close();

//Loads individual image as texture
SDL_Texture* loadTexture( std::string path );

//The window we'll be rendering to
SDL_Window* gWindow = NULL;

//The window renderer
SDL_Renderer* gRenderer = NULL;

//Current displayed texture
SDL_Texture* gTexture = NULL;
SDL_Texture* gTexture2 = NULL;


bool init()
{
    //Initialization flag
    bool success = true;

    //Initialize SDL
    if( SDL_Init( SDL_INIT_VIDEO ) < 0 )
    {
        printf( "SDL could not initialize! SDL Error: %s\n", SDL_GetError() );
        success = false;
    }
    else
    {
        //Set texture filtering to linear
        if( !SDL_SetHint( SDL_HINT_RENDER_SCALE_QUALITY, "1" ) )
        {
            printf( "Warning: Linear texture filtering not enabled!" );
        }

        //Create window
        gWindow = SDL_CreateWindow( "SDL Tutorial", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN );
        if( gWindow == NULL )
        {
            printf( "Window could not be created! SDL Error: %s\n", SDL_GetError() );
            success = false;
        }
        else
        {
            //Create renderer for window
            gRenderer = SDL_CreateRenderer( gWindow, -1, SDL_RENDERER_ACCELERATED );
            if( gRenderer == NULL )
            {
                printf( "Renderer could not be created! SDL Error: %s\n", SDL_GetError() );
                success = false;
            }
            else
            {
                //Initialize renderer color
                SDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0xFF, 0xFF );

                //Initialize PNG loading
                int imgFlags = IMG_INIT_PNG;
                if( !( IMG_Init( imgFlags ) & imgFlags ) )
                {
                    printf( "SDL_image could not initialize! SDL_image Error: %s\n", IMG_GetError() );
                    success = false;
                }
            }
        }
    }

    return success;
}

bool loadMedia()
{
    //Loading success flag
    bool success = true;

    //Load PNG texture
    gTexture = loadTexture( "../assets/player.png" );
    if( gTexture == NULL )
    {
        printf( "Failed to load texture image!\n" );
        success = false;
    }

    return success;
}

bool loadMedia2()
{
    //Loading success flag
    bool success = true;

    //Load PNG texture
    gTexture2 = loadTexture( "../assets/scene_main/background.png" );
    if( gTexture == NULL )
    {
        printf( "Failed to load texture image!\n" );
        success = false;
    }

    return success;
}

void close()
{
    //Free loaded image
    SDL_DestroyTexture( gTexture );
    SDL_DestroyTexture( gTexture2 );
    gTexture = NULL;
    gTexture2 = NULL;        

    //Destroy window
    SDL_DestroyRenderer( gRenderer );
    SDL_DestroyWindow( gWindow );
    gWindow = NULL;
    gRenderer = NULL;

    //Quit SDL subsystems
    IMG_Quit();
    SDL_Quit();
}

SDL_Texture* loadTexture( std::string path )
{
    //The final texture
    SDL_Texture* newTexture = NULL;

    //Load image at specified path
    SDL_Surface* loadedSurface = IMG_Load( path.c_str() );
    if( loadedSurface == NULL )
    {
        printf( "Unable to load image %s! SDL_image Error: %s\n", path.c_str(), IMG_GetError() );
    }
    else
    {
        //Create texture from surface pixels
        newTexture = SDL_CreateTextureFromSurface( gRenderer, loadedSurface );
        if( newTexture == NULL )
        {
            printf( "Unable to create texture from %s! SDL Error: %s\n", path.c_str(), SDL_GetError() );
        }

        //Get rid of old loaded surface
        SDL_FreeSurface( loadedSurface );
    }

    return newTexture;
}

int main( int argc, char* args[] )
{
    //Start up SDL and create window
    if( !init() )
    {
        printf( "Failed to initialize!\n" );
    }
    else
    {
        //Load media
        if( !loadMedia() || !loadMedia2() )
        {
            printf( "Failed to load media!\n" );
        }
        else
        {   
            //Main loop flag
            bool quit = false;

            //Event handler
            SDL_Event e;

            //While application is running
            while( !quit )
            {
                //Handle events on queue
                while( SDL_PollEvent( &e ) != 0 )
                {
                    //User requests quit
                    if( e.type == SDL_QUIT )
                    {
                        quit = true;
                    }
                }

                //Clear screen
                SDL_RenderClear( gRenderer );

                //Render texture to screen
                SDL_RenderCopy( gRenderer, gTexture, NULL, NULL );

                //Update screen
                SDL_RenderPresent( gRenderer );
            }
        }
    }

    //Free resources and close SDL
    close();

    return 0;
}   

小更新:

  • 我已经在 windows 上试过了,它运行得很好。所以我猜这个问题与MacOs有关。

  • 我已经尝试重新安装所有库。

  • 我使用的是 C++14。

解决方案

好吧,它只是解决方案的一半,它更像是一个解决方法

感谢@Sahib Yar,他指出尝试将图像放在同一目录中。这解决了这个问题。

但我觉得这真的很奇怪,你应该可以从不同的目录或至少子目录加载资源。

最后一个问题

现在我真的很想解释为什么我们不能在 MacOS 上使用 SDL 从多个目录加载图像。这只是一个错误,已知的事情还是我犯了一个大错误?

【问题讨论】:

  • SDL_image 在 MacOS 上使用ImageIO 来加载图像,因此这可能解释了与 Windows 的不同行为。
  • loadImage2 检查有误。除此之外,这段代码似乎还不错。可能是 SDL 编译错误或 SDL 编译与 ImageIO / OSX 库之间不兼容?

标签: c++ sdl-2


【解决方案1】:

看来您并没有破坏不需要的纹理2。

SDL_DestroyTexture( gTexture );
SDL_DestroyTexture( gTexture2 );

gTexture = NULL;
gTexture2 = NULL;

this lazyfoo教程中提到

在我们的清理函数中,我们必须记住释放我们的 纹理使用SDL_DestroyTexture

编辑 1:

尝试将所有图片放在同一个目录中。

编辑 2:

它与 MacOS 中的目录无关 从this tutorial,似乎编译器正在对std::string path 进行一些优化,因为std::stringmutable。 尝试在函数结束时清除 std::string path 对象,以清除其对象保留的所有内存。

添加这一行。

std::string().swap(path);

您的问题是一个悬空指针。 EXC_BAD_ACCESS 是 CPU 抱怨您正在处理不存在的内存或访问权限区域之外的内存。原因是缺乏对对象的保留,导致提前释放,然后被覆盖。此时(可能会延迟),指针将指向垃圾,其取消引用会导致抛出 EXC_BAD_ACCESS

编辑 3:

这与 SDL2 无关。谷歌搜索后,我发现在 Xcode 中,所有内容最终都打包到 1 个单独的目录中。我找到了multiple questions 关于这个。它可能与folder reference and groups 有关。我猜这可能与blue folders 有关。如果是这种情况,您可以咨询 this answer 并相应地使用 SDL。

【讨论】:

  • 我忘记破坏纹理是完全正确的,但这与这个问题无关。我已经更新了我的示例代码,但这不是解决方案。
  • 你试过把两张图片放在同一个目录吗?
  • 这到底是什么鬼。这似乎是问题所在,介意解释一下这种行为吗?
  • @Mario 你试过更新答案中的代码了吗?
  • 对不起,我还不能,但我明天给你一个更新。
猜你喜欢
  • 2014-08-10
  • 2011-07-22
  • 1970-01-01
  • 2015-12-24
  • 1970-01-01
  • 1970-01-01
  • 2014-10-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多