0

I'm programming with SDL2, but I cannot grasp the reason behind the following. This works:

SDL_Window  *window;
SDL_Surface *screen_surface;
SDL_Surface *picture;

auto initWindow(void)
{…}
auto loadMedia(void)
{…}
auto close(void)
{…}

int main(void)
{
    initWindow();
    loadMedia();
    …
    close();
}

However this does not:

auto initWindow(SDL_Window *window, SDL_Surface *screen_surface)
{…}
auto loadMedia(SDL_Surface *picture, std::string resource)
{…}
auto close(SDL_Window *window, SDL_Surface *picture)
{…}

int main(void)
{
    SDL_Window  *window;
    SDL_Surface *screen_surface;
    SDL_Surface *picture;
    initWindow(window, screen_surface);
    loadMedia(picture, "resource_file.bmp");
    …
    close(window, picture);
}

The only difference is that I take window, screen_surface and picture out of file scope and put it into block scope (i.e. the main function), and instead of referencing global variables inside these functions, I use parameters. However when I try to run this, it displays a white screen, but doesn't display any errors. I don't understand what goes wrong here.

hgiesel
  • 5,430
  • 2
  • 29
  • 56
  • Good candidate for stepping through both versions with your IDE's debugger to see what the difference is. – user4581301 Feb 10 '16 at 23:59
  • 1
    What does `void f(int x) {x = 5;}` `int main() {int i = 7; f(i); printf("%d\n", i);}` print, and why? – user253751 Feb 11 '16 at 00:01
  • To elaborate `initWindow(SDL_Window *window, SDL_Surface *screen_surface)` you are passing **copies** of the pointers in this declaration. – Jesse Good Feb 11 '16 at 00:03
  • @immibis `7`, because call-by-value, but this is call-by-reference, isn't it? – hgiesel Feb 11 '16 at 00:04
  • 1
    @henrikgiesel No? C has no such thing as call-by-reference. – user253751 Feb 11 '16 at 00:07
  • @immibis Then what's `void f(int *x);` compared to `void f2(int x);`? – hgiesel Feb 11 '16 at 00:09
  • @henrikgiesel In the first `x` is an `int*`, and in the second it's an `int`? – user253751 Feb 11 '16 at 00:11
  • @JesseGood I'm passing copies, but they still point to the same spot in memory – hgiesel Feb 11 '16 at 00:19
  • 2
    @henrikgiesel: Yes, they point to the same spot in memory before you call the `initWindow` function. However, inside the `initWindow` you _change_ what the *copied* pointer points to. The problem is this does not affect the pointer declared in `main`, you are **only modifying the *copied* pointer** inside `initWindow`. – Jesse Good Feb 11 '16 at 01:14

1 Answers1

3

Disclaimer : I have never done any SDL programming, so this is just an answer based on common sense and what I could read from the comments.

Say your initWindow function sets some value to the window variable. When that variable is declared in a global scope, modifications to it are also seen by all the other functions that use this variable.

This changes drastically when you change that variable to be a function parameter. Basing on the code you provided :

auto initWindow(SDL_Window *window, SDL_Surface *screen_surface)
{
    window = SDL_GetWindow(); /* or something */
}

int main(void)
{
    SDL_Window  *window;
    SDL_Surface *screen_surface;
    initWindow(window, screen_surface);
    /* some other code that uses 'window' */
}

Only the window in initWindow is actually set to the value of SDL_GetWindow. The window variable inside main is not changed : all the other functions that need to use it in main will be accessing an uninitialized variable, which is undefined behaviour. You also have a resource leak, since you're now never able to free what you got from SDL_GetWindow. initWindow actually receives a copy of window, a one that's totally unrelated to the window in main.

Seeing how you're using C++, the best way around this is to have initWindow accept a reference to the window variable, like this :

auto initWindow(SDL_Window *&window, SDL_Surface *&screen_surface)
{
    window = SDL_GetWindow(); /* or something */
}

int main(void)
{
    SDL_Window  *window;
    SDL_Surface *screen_surface;
    initWindow(window, screen_surface);
    /* some other code that uses 'window' */
}

Now, the window variable inside main will be updated with what initWindow does with it, and later code that uses window in main will access the resource that was acquired via SDL_GetWindow.

However, C++ allows you to manage resources in a more efficient way via the usage of constructors and destructors, a concept known as RAII (Resource Acquisition Is Initialization). Look for C++ wrappers around the SDL objects which will make your life much easier, and if you don't, spend a while to write your own, or make them work with std::unique_ptr (or std::shared_ptr if you know that you need it). You'll thank yourself for it later on.

Daniel Kamil Kozar
  • 18,476
  • 5
  • 50
  • 64
  • 1
    The one thing I would do slightly differently is that instead of using `SDL_Window *&window` as the argument, I would use `SDL_Window **window`. That forces you to use `&window` at the call site, making it clear at a glance that the value could change. (but this is just a style issue, others might not agree.) – Alex Feb 11 '16 at 01:04
  • @Alex : sure. I thought having a `SDL_Window**` would be too C-ish. ;) – Daniel Kamil Kozar Feb 11 '16 at 01:05
  • It works, but I wonder, why is the behavior like this? I know that `SDL_Window` is some kind of `struct` (SDL2 is actually a C library). When I declare a `struct` myself, make a pointer to that struct and pass that to a function just like I did, here, it works just as expected. – hgiesel Feb 11 '16 at 04:34