Allegro.cc - Online Community

Allegro.cc Forums » Allegro Development » Functions returning NULL pointers when I expect otherwise

Credits go to bamccaig for helping out!
This thread is locked; no one can reply to it. rss feed Print
Functions returning NULL pointers when I expect otherwise
NeoFahrenheit
Member #16,644
March 2017

Hi guys,

Sorry for the silly question, but I can't figure it out what's going on.
I'm following this great tutorial and decided to split the code into separate files to be more organized.

But my MS Visual Studio debugger is showing that all my timer, queue, disp and font pointers are NULL when reaching the main loop. I am mostly a begginer in C, so sorry if I missed something.

Code are in the attachments.

Can you guys help?
Thank you.

bamccaig
Member #7,536
July 2006
avatar

Just posting the code so we can read it easier...

Quote:

init.c#SelectExpand
1#include <stdio.h> 2#include <allegro5/allegro5.h> 3#include <allegro5/allegro_font.h> 4 5void init_all(ALLEGRO_TIMER* timer, ALLEGRO_EVENT_QUEUE* queue, ALLEGRO_DISPLAY* disp, ALLEGRO_FONT* font) 6{ 7 if (!al_init()) 8 { 9 printf("couldn't initialize allegro\n"); 10 exit(1); 11 } 12 13 if (!al_install_keyboard()) 14 { 15 printf("couldn't initialize keyboard\n"); 16 exit(1); 17 } 18 19 timer = al_create_timer(1.0 / 60.0); 20 if (!timer) 21 { 22 printf("couldn't initialize timer\n"); 23 exit(1); 24 } 25 26 queue = al_create_event_queue(); 27 if (!queue) 28 { 29 printf("couldn't initialize queue\n"); 30 exit(1); 31 } 32 33 disp = al_create_display(800, 600); 34 if (!disp) 35 { 36 printf("couldn't initialize display\n"); 37 exit(1); 38 } 39 40 font = al_create_builtin_font(); 41 if (!font) 42 { 43 printf("couldn't initialize font\n"); 44 exit(1); 45 } 46 47 al_register_event_source(queue, al_get_keyboard_event_source()); 48 al_register_event_source(queue, al_get_display_event_source(disp)); 49 al_register_event_source(queue, al_get_timer_event_source(timer)); 50}

init.h#SelectExpand
1#pragma once 2 3// Inicialize all basic essentials. 4void init_all(ALLEGRO_TIMER*, ALLEGRO_EVENT_QUEUE*, ALLEGRO_DISPLAY*, ALLEGRO_FONT*);

main.c#SelectExpand
1#include <stdio.h> 2#include <allegro5/allegro5.h> 3#include <allegro5/allegro_font.h> 4#include "init.h" 5 6int main() 7{ 8 ALLEGRO_TIMER* timer = NULL; 9 ALLEGRO_EVENT_QUEUE* queue = NULL; 10 ALLEGRO_DISPLAY* disp = NULL; 11 ALLEGRO_FONT* font = NULL; 12 13 init_all(&timer, &queue, &disp, &font); 14 15 bool done = false; 16 bool redraw = true; 17 ALLEGRO_EVENT event; 18 19 al_start_timer(timer); 20 while (1) 21 { 22 al_wait_for_event(queue, &event); 23 24 switch (event.type) 25 { 26 case ALLEGRO_EVENT_TIMER: 27 // game logic goes here. 28 redraw = true; 29 break; 30 31 case ALLEGRO_EVENT_KEY_DOWN: 32 case ALLEGRO_EVENT_DISPLAY_CLOSE: 33 done = true; 34 break; 35 } 36 37 if (done) 38 break; 39 40 if (redraw && al_is_event_queue_empty(queue)) 41 { 42 al_clear_to_color(al_map_rgb(0, 0, 0)); 43 al_draw_text(font, al_map_rgb(255, 255, 255), 0, 0, 0, "Hello world!"); 44 al_flip_display(); 45 46 redraw = false; 47 } 48 } 49 50 al_destroy_font(font); 51 al_destroy_display(disp); 52 al_destroy_timer(timer); 53 al_destroy_event_queue(queue); 54 55 return 0; 56}

You appear to be passing a pointer-to-a-pointer, but the function is accepting just a pointer here:

Quote:

  ALLEGRO_TIMER* timer = NULL;
  ALLEGRO_EVENT_QUEUE* queue = NULL;
  ALLEGRO_DISPLAY* disp = NULL;
  ALLEGRO_FONT* font = NULL;

  init_all(&timer, &queue, &disp, &font);

Which means instead of assigning to the variables in main you're just overwriting your own local variables (throwing away the parameter value).

You probably want to change the signature of init_all to accept pointer-to-pointer instead.

NeoFahrenheit
Member #16,644
March 2017

Thanks for the help bamcaig, but I don't know how to do that. I tried modifying the function all the ways I could, but I'm terrible using pointer-to-pointer.

If it isn't too much trouble for you, could you help me with code examples?
Also, I'm accepting tips in to know how and when to use pointer-to-pointer. :D

Thanks!

EDIT: I have made it work, but it was only luck and a lot of try and error. If you guys could enlighten me, It would be awesome. My updated code are in the attachments.

bamccaig
Member #7,536
July 2006
avatar

Instead of attaching a few files it is much nicer to just embed the code in your post so we don't have to download the files (if it's more than 3 files then probably zipping them is preferred).

You can format code by posting it in an XML tag: <code name="foo.c">teh codes</code>. The name attribute is optional.

It's actually really impressive if you figured that out just with trial and error. But pointers to pointers work the same way that regular pointers do, except that the data they point to is another pointer. In your example, you allocated variables in main to hold the pointers to the structures, and then passed a pointer to those variables to a function to allow you to modify them from within the function.

int x;
int * x_pointer = &x;

int * y_pointer = malloc(sizeof(int));
int ** y_pointer_to_pointer = &y_pointer;

When you dereference (`*varname`) a pointer-to-a-pointer you reference the original pointer.

For example, *y_pointer_to_pointer would return the address stored at y_pointer. First it looks up the address of y_pointer, and then it loads size int bytes) starting there, which we've promised will hold a pointer to another integer.

By passing pointers to local variables you can allow a function to modify those local variables. This can be utilized for multiple return values, for example. It's a common pattern for initialization functions if you're trying to avoid globals, though storing those values all in a structure can simplify the interface.

NeoFahrenheit
Member #16,644
March 2017

Thanks for the explanation, bamccaig. I'm afraid I will ask a bit more of you. ;D
Sorry for not posting my code here directly. I was thinking I was going to make my post too big and people would complain.

So, I have made a example code showing my doubts:

#SelectExpand
1#include <stdio.h> 2 3void swap(int* a, int* b) 4{ 5 int temp = *a; 6 7 *a = *b; 8 *b = *a; 9} 10 11int main() 12{ 13 int x = 10, y = 5; 14 swap(&x, &y); 15 16 return 0; 17} 18 19/* The example above shows the avarage use of a pointer function. That I can understand. 20But I remember when I was in my first semester in university, I had to pass pointer-to-pointer 21to functions dealing with trees or graphs if I want to modify them without returning the pointer. 22 23Why? 24 25The explanation that I have always got is that x and y variables are restricted to the main function. 26swap can't acess them directly, so we need to pass their adresses. But with data structures or with that 27allegro5 case, I was already sending (or so I tought) the pointers to init_all(), so why can't the main 28function retrieve them? Why do we need to use pointer-to-pointer in some cases? */

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

http://www.cplusplus.com/doc/tutorial/pointers/

Do some reading and be enlightened.

Pointers are normal variables, except they hold an address.

Now to modify a variable, you need its address. (&var).

So if you want to modify a pointer outside of its scope, you need it's address.

int a = 0;
int* pa = &a;
int** ppa = &pa;

So to modify pa indirectly, you need to pass ppa. Which is a pointer to pointer. All that means is that it holds the address of a pointer, so it can be modified.

EDIT
Your swap function is incorrect. You need to assign *b the value of temp, not *a.

bamccaig
Member #7,536
July 2006
avatar

A pointer allows you to modify the data that it points at.

#SelectExpand
1#include <stdio.h> 2 3void triplei(int n) 4{ 5 n = n * 3; 6} 7 8void triplep(int * n) 9{ 10 *n = *n * 3; 11} 12 13int main(int argc, char *argv[]) 14{ 15 int x = rand() % 100; 16 int y = x; 17 18 triplei(y); 19 20 printf("Triple %d is %d? (fail)\n", x, y); 21 22 y = x; 23 24 triplep(&y); 25 26 printf("Triple %d is %d? (win)\n", x, y); 27 28 return 0; 29}

In the above example, triplei has no effect on y. The reason is because it accepted a copy of y's value. It has no way to modify y itself. When we assign a new value to n in triplei it has no effect on y.

Contrarily, triplep works. Instead of accepting a copy of y it accepts a copy of y's memory address. By dereferencing the memory address we can read the value stored there, and we can also write to it! First we read the value, then multiply it by 3, and then overwrite the value stored at the memory address with the result. This does modify y.

In this example, our data was just a simple integer, but it works exactly the same way with a more complicated structure.

#SelectExpand
1#include <stdio.h> 2#include <stdlib.h> 3#include <time.h> 4#include <unistd.h> 5 6struct my_timer_ 7{ 8 int _start; 9 int _current; 10}; 11 12typedef struct my_timer_ my_timer_t; 13 14my_timer_t * create_my_timer(void) 15{ 16 // Allocating memory on the heap so it will still exist when we return. 17 my_timer_t * ptr = malloc(sizeof(my_timer_t)); 18 19 if (ptr == NULL) 20 { 21 return NULL; 22 } 23 24 ptr->_start = (unsigned int)time(NULL); // Contrived implementation. 25 ptr->_current = ptr->_start; 26 27 return ptr; 28} 29 30void tick_my_timer(my_timer_t * timer) 31{ 32 timer->_current = (unsigned int)time(NULL); 33} 34 35void destroy_my_timer(my_timer_t ** timer) 36{ 37 free(*timer); 38 *timer = NULL; 39} 40 41void initializep(my_timer_t * timer) 42{ 43 timer = create_my_timer(); 44} 45 46void initializepp(my_timer_t ** timer) 47{ 48 *timer = create_my_timer(); 49} 50 51int main(int argc, char * argv[]) 52{ 53 my_timer_t * timer = NULL; 54 55 initializep(timer); 56 57 printf("Timer is initialized by initializep? %s\n", 58 timer == NULL ? "no" : "yes"); 59 60 initializepp(&timer); 61 62 printf("Timer is initialized by initializepp? %s\n", 63 timer == NULL ? "no" : "yes"); 64 65 if (timer != NULL) 66 { 67 sleep(2); 68 69 tick_my_timer(timer); 70 71 printf("Timer has ticked? %s\n", 72 timer->_current - timer->_start ? "yes" : "no"); 73 74 destroy_my_timer(&timer); 75 } 76 77 return 0; 78}

Similarly, initializep does not work to initialize timer in main. And it also leaks memory! The timer variable in initializep starts out as a copy of the value of timer in main (i.e., NULL). When we allocate memory and assign it to timer in initializep all it does is store the new memory address in initializep. It doesn't affect main.

Whereas initializepp is able to actually assign to timer in main because we passed a copy of the memory addresss of timer in main. By dereferencing the pointer-to-a-pointer we can read the timer pointer value, but we can also write to it because we're actually pointing at the pointer! For the same reason, destroy_timer is able to actually assign NULL back to timer in main because we passed a pointer to it.

NeoFahrenheit
Member #16,644
March 2017

Things are more clearer now. I think my mistake was assuming functions that takes pointers as arguments creates it's local variable with the adresses it recevies. This is how I was doing in 1.jpeg.

But I tought, maybe, all variables inside functions has it's own unique adresses. That made a lot more sense. Besides, all the variables must be allocated before runtime. Using this tought, I made 2.jpeg. I'm not really sure if I was really getting it or just wanting and manipulating my hand and pencil to get it.

I think a little bit of more time and revisiting my notes will be enough to the knowlogde to stick.

Thanks a lot for your help, bamccaig. ;D
Thanks for the link, Edgar Reynaldo. :)

bamccaig
Member #7,536
July 2006
avatar

Actually not all variables are allocated before runtime. Only global/static variables that exist always get allocated in that way (technically they're still allocated at runtime, but I'm speculating that they're allocated as part of the memory that holds the actual program; I'm not sure about that though).

Local variables in functions are allocated when the function is called. There are generally two memory sources for programs: the stack and the heap. The stack is where your function calls and variables and return values are stored. The heap is where dynamic memory is allocated (e.g., my_timer_t example above uses malloc to allocate memory on the heap for a timer structure).

The program always has a pointer to the top of the stack. When a function is called the program jumps to some code that sets up the function call by allocating a fixed amount of memory based on how many and how big the parameters, variables, and return types are, with a few extra slots for housekeeping. Then it jumps to the actual code for the function and begins executing it. The code is executing in reference to where the function call was allocated on the stack. It generally knows that variable1 is X bytes from the reference point. It can access the variable location in memory by doing pointer arithmetic with that reference point. When the function returns there is code injected into the program to copy the return value back to the calling function and effectively unallocate/free the stack memory used for the previous function call.

So the location of local variables in memory varies depending on where it's stack was allocated and when the function is called in the program. If it's called from many different places in the call stack then its variables will exist at different places in memory depending on which call is executing. That call will know where they are, but the rest of the program doesn't.

Go to: