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

I’ve been going back over some SDL tutorials I did a while back to practice pointers/references, but I got stuck on something.

If I declare the SDL_Renderer globally everything works, but if I try to declare it in main, and pass the renderer to the functions that need it, I eventually get an SDL_Error saying “invalid renderer”.

What works:

...
// 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;
    }
...

What doesn’t work:

...
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";
        }
...

The texture class: (same file for both versions)

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
        }
...

The way I see it, SDL_CreateTextureFromSurface expects a pointer to an SDL_Renderer, which is what I’m passing. I don’t understand why it matters if it’s globally declared or declared in main. It should still just be a chunk of memory I’m pointing to somewhere. I understand I must be passing it wrong, but I can’t figure out how to do it correctly, and everything I’ve tried has produced the same error (passing the pointer as a reference, as a pointer to the pointer, etc.).

Side-note: I know I could just declare it globally or create a Singleton and be done with it, but this is more a learning exercise for me – so the point is to understand the pointer/reference aspect of the problem.

Answer

In the second snippet, if init changes the variable renderer, it will only change its local copy of the variable. It will not change the original variable renderer in the function main. This is because you are passing the variable by value. If you instead pass it by pointer or by reference, you will also modify the original variable’s value. That way, your program will behave the same way as your global variable version.

Note that in order to pass a pointer to another function by pointer, you will need a pointer to a pointer. In your case you will need an SDL_Renderer **.