SDL_Renderer 用作全局变量,但如果在 main 中声明则不会,而是传递给需要它的函数

SDL_Renderer works as global variable, but not if declared in main, and passed to the functions that require it instead

我一直在回顾一些 SDL 教程,我为了练习做了一段时间 pointers/references,但我遇到了一些问题。

如果我全局声明 SDL_Renderer 一切正常,但如果我尝试在 main 中声明它,并将渲染器传递给需要它的函数,我最终会得到一个 SDL_Error 说“渲染器无效”。

什么有效:

...
// Global vars
SDL_Window* gWindow = NULL;

SDL_Renderer* gRenderer = NULL;

SDL_Rect gSpriteClips[ 4 ];
LTexture gSpriteSheetTexture;
...

// called from main after SDL initialization
bool loadMedia()
{
    bool success = true;

    //Load sprite sheet texture
    if( !gSpriteSheetTexture.loadFromFile( gRenderer, "img/dots.png" ) )
    {
        printf( "Failed to load sprite sheet texture!\n" );
        success = false;
    }
...

什么不起作用:

...
bool init( SDL_Window* window, SDL_Renderer* renderer, const int SCREEN_WIDTH, const int SCREEN_HEIGHT )
{
    ...
}
...
bool load_media( SDL_Renderer* renderer, LTexture& sprite_sheet_texture, SDL_Rect* sprite_clips )
{
    bool success = true;

    //Load sprite sheet texture
    if( !sprite_sheet_texture.loadFromFile( renderer, "img/dots.png" ) )
    {
        printf( "Failed to load sprite sheet texture!\n" );
        success = false;
    }
...

int main(int argc, char* args[])
{
    const int SCREEN_WIDTH = 640;
    const int SCREEN_HEIGHT = 480;

    SDL_Window* window = NULL;

    SDL_Renderer* renderer = NULL;
    
    SDL_Rect sprite_clips[ 4 ];
    LTexture sprite_sheet_texture;
    
    if( !init( window, renderer, SCREEN_WIDTH, SCREEN_HEIGHT ) )
    {
        std::cout << "Failed to initialize!\n";
    }
    else
    {
        if( !load_media( renderer, sprite_sheet_texture, sprite_clips ) )
        {
            std::cout << "Failed to load media!\n";
        }
...

纹理class:(两个版本的文件相同)

bool LTexture::loadFromFile( SDL_Renderer* gRenderer, const std::string &path )
{
    // Get rid of preexisting texture
    free();

    // The final texture
    SDL_Texture* newTexture = NULL;

    // Load image at specified path
    SDL_Surface* loadedSurface = IMG_Load( path.c_str() );

    if( loadedSurface == NULL )
    {
        std::cout << "Unable to load image! SDL_Image error: " << IMG_GetError() << std::endl;
    }
    else
    {
        // color key image
        SDL_SetColorKey( loadedSurface, SDL_TRUE, SDL_MapRGB( loadedSurface->format, 0, 0xFF, 0xFF ) );

        // Create texture from surface pixels
        newTexture = SDL_CreateTextureFromSurface( gRenderer, loadedSurface ); <-- POINT OF FAILURE
        
        if( newTexture == NULL )
        {
            std::cout << "Unable to create texture! SDL error: " << SDL_GetError() << std::endl; <-- THE ERROR I GET
        }
...

在我看来,SDL_CreateTextureFromSurface 需要一个指向 SDL_Renderer 的指针,这正是我要传递的内容。我不明白为什么它在全球范围内声明或在 main 中声明很重要。它应该仍然只是我指向某处的一大块内存。 我知道我一定是把它传递错了,但我不知道如何正确地做,而且我尝试过的一切都产生了同样的错误(将指针作为引用传递,作为指向指针的指针等。 ).

旁注:我知道我可以全局声明它或创建一个单例并完成它,但这对我来说更像是一个学习练习 - 所以重点是理解 pointer/reference 方面的问题。

在第二个代码段中,如果 init 更改变量 renderer,它只会更改变量的本地副本。它不会改变函数main中的原始变量renderer。这是因为你传递的是变量by value. If you instead pass it by pointer or by reference,你也会修改原来变量的值。这样,您的程序的行为方式将与您的全局变量版本相同。

请注意,为了通过指针将指针传递给另一个函数,您将需要一个指向指针的指针。在您的情况下,您将需要 SDL_Renderer **.

根据 Andreas Wenzel 的回答和建议,我现在有了一个可行的解决方案。我以为我是通过指针传递的,而实际上我是通过值传递的。通过将指针传递给指针,它会按预期工作。

我在下面包含了一些片段,以显示与原始问题中代码的不同之处。

int main(int argc, char* args[])
{
    const int SCREEN_WIDTH = 640;
    const int SCREEN_HEIGHT = 480;

    SDL_Window* window = NULL;

    SDL_Renderer* renderer = NULL;
    
    SDL_Rect sprite_clips[ 4 ];
    LTexture sprite_sheet_texture;
    
    if( !init( &window, &renderer, SCREEN_WIDTH, SCREEN_HEIGHT ) )
    {
        std::cout << "Failed to initialize!\n";
    }
    else
    {
        if( !load_media( &renderer, sprite_sheet_texture, sprite_clips ) )
        {
            std::cout << "Failed to load media!\n";
        }

...

bool init( SDL_Window** window, SDL_Renderer** renderer, const int SCREEN_WIDTH, const int SCREEN_HEIGHT )
{
    bool success = true;

    // initialize SDL
    if( SDL_Init( SDL_INIT_VIDEO) < 0 )
    {
        std::cout << "SDL could not initialize! SDL error: " << SDL_GetError() << std::endl;
        success = false;
    }
    else
    {
        if( !SDL_SetHint( SDL_HINT_RENDER_SCALE_QUALITY, "1" ) )
        {
                std::cout << "Warning: Linear texture filtering not enabled!\n";
        }

        // Create window
        *window = SDL_CreateWindow( 
                "SDL Tutorial",
                SDL_WINDOWPOS_UNDEFINED,
                SDL_WINDOWPOS_UNDEFINED,
                SCREEN_WIDTH,
                SCREEN_HEIGHT,
                SDL_WINDOW_SHOWN
                );

        if( window == NULL )
        {
            std::cout << "Window could not be created! SDL error: " << SDL_GetError() << std::endl;
            success = false;
        }
        else
        {
            // Create renderer for window
            *renderer = SDL_CreateRenderer( *window, -1, SDL_RENDERER_ACCELERATED );
            if( renderer == NULL )
            {
                std::cout << "Renderer could not be created! SDL error: " << SDL_GetError() << std::endl;
                success = false;
            }

...

bool load_media( SDL_Renderer** renderer, LTexture& sprite_sheet_texture, SDL_Rect* sprite_clips )
{
    bool success = true;

    //Load sprite sheet texture
    if( !sprite_sheet_texture.loadFromFile( *renderer, "img/dots.png" ) )
    {
        printf( "Failed to load sprite sheet texture!\n" );
        success = false;
    }

...

void close( SDL_Window** window, SDL_Renderer** renderer )
{
    //Destroy Window
    SDL_DestroyRenderer( *renderer );
    SDL_DestroyWindow( *window );
    *window = NULL;
    *renderer = NULL;

    IMG_Quit();
    SDL_Quit();
}