Recently I've been trying to learn SDL, a graphics library amongst other things, for C. I haven't gotten very far but with the basics I've learnt from this tutorial (Yes, I know it's for C++ but it seems the majority of things are still the same), I've tried to create a "template" SDL program to start all my other future programs from. This is what I had:
#include <stdio.h>
#include <stdlib.h>
#include <SDL.h>
int initProg(SDL_Window **window, SDL_Surface **surface, char *name, int x, int y, int w, int h, int flags);
void mainLoop(SDL_Window *window, SDL_Surface *surface);
void closeProg(SDL_Window *window);
SDL_Surface *loadImage(char *path);
int main(int argc, char *args[]) {
SDL_Window *window = NULL;
SDL_Surface *surface = NULL;
char windowName[13] = "SDL Tutorial";
int windowXPos = SDL_WINDOWPOS_UNDEFINED;
int windowYPos = SDL_WINDOWPOS_UNDEFINED;
int windowWidth = 600;
int windowHeight = 600;
int flags = SDL_WINDOW_SHOWN;
if (!initProg(&window, &surface, windowName, windowXPos, windowYPos, windowWidth, windowHeight, flags)) {
return 1;
}
mainLoop(window, surface);
closeProg(window);
return 0;
}
int initProg(SDL_Window **window, SDL_Surface **surface, char *name, int x, int y, int w, int h, int flags) {
if (SDL_Init(SDL_INIT_EVERYTHING) < 0) {
printf("Failed to initialize SDL.\nError: %s\n", SDL_GetError());
return 0;
} else {
*window = SDL_CreateWindow(name, x, y, w, h, flags);
if (*window == NULL) {
printf("Failed to create a window.\nError:%s\n", SDL_GetError());
return 0;
} else {
*surface = SDL_GetWindowSurface(*window);
return 1;
}
}
}
void mainLoop(SDL_Window *window, SDL_Surface *surface) {
// Simple program to fade between white and black background
int g = 0;
int diff = -1;
int quit = 0;
SDL_Event e;
while (!quit) {
while(SDL_PollEvent(&e) != 0) {
if (e.type == SDL_QUIT) {
quit = 1;
}
}
SDL_FillRect(surface, NULL, SDL_MapRGB(surface->format, g, g, g));
SDL_UpdateWindowSurface(window);
if (g == 0 || g == 255) {
diff *= -1;
}
g += diff;
SDL_Delay(10);
}
}
void closeProg(SDL_Window *window) {
SDL_DestroyWindow(window);
window = NULL;
SDL_Quit();
}
The program is divided into three sections: one to initialize SDL, one where the main program occurs, and one to close SDL and free the surfaces.
I realized the problem with this is that if I had created another surface then I would have to add code to the closeProg() function to free the surface at the end. N.B: Keep in mind that these "surfaces" are just pointers to the actual thing, which as far as I'm aware you don't really interact with, you just deal with the pointer to it.
To work around this, I decided to create an array of these pointers-to-surfaces, and then make a function that creates a surface and adds it's pointer to the array. This would allow me at the end in the closeProg() function to go through each surface and free it, then free the array too. (Note that the window's surface is not added to this list as it gets freed automatically with the SDL_DestroyWindow() function)
Here is the declaration of this new array:
// The pointer to the array. Currently NULL until something is added
SDL_Surface **surfaces = NULL;
// Keeps track of the size of the array
size_t surfaceCount = 0;
This is the function that adds to the array:
int addSurface(SDL_Surface *surface, SDL_Surface **surfaces, size_t *surfaceCount) {
size_t new_count = *surfaceCount + 1;
SDL_Surface **temp = realloc(surfaces, sizeof(*temp) * new_count);
if (temp == NULL) {
printf("Failed to reallocate to %d bytes of memory.", sizeof(*temp) * new_count);
return 0;
} else {
surfaces = temp;
*(surfaces + new_count - 1) = surface;
*surfaceCount = new_count;
return 1;
}
}
And here it is in use. Note that loadImage() returns a surface from an image, and showSurfaces() prints out the contents of the surfaces array.
// Check contents before adding anything
showSurfaces(surfaces, *surfaceCount);
// Add an image
SDL_Surface *fire = loadImage("./fire.bmp");
addSurface(fire, surfaces, surfaceCount);
// Check contents again
showSurfaces(surfaces, *surfaceCount);
// Add another image and check contents again
SDL_Surface *ice = loadImage("./ice.bmp");
addSurface(ice, surfaces, surfaceCount);
showSurfaces(surfaces, *surfaceCount);
// Add another image and check contents a final time
SDL_Surface *man = loadImage("./man.bmp");
addSurface(man, surfaces, surfaceCount);
showSurfaces(surfaces, *surfaceCount);
Up to the first two surfaces, everything went well. However, when I tried to add the third surface, the program hanged immediately before closing (indication that something is wrong in the closeProg() function?). I decided to print out the contents of the array and here is what I got.
No current surfaces.
Current surfaces:
Index 0: 00753C98
Current surfaces:
Index 0: 00753C98
Index 1: 00754780
Current surfaces:
Index 0: 02805150
Index 1: 008F00C0
Index 2: 201339FC
In the first two print-outs, everything seemed well but in the third one, you can notice that the previous addresses have changed. This happened repeatedly and no matter how many surfaces I added. After the first two reallocations, the array's content kept on changing.
I think that is why the program hangs when closing, because in the closeProg function, the program is told to free an unknown pointer that is not a surface, hence it crashes. Not to mention that I also set that pointer to NULL, and who knows what else that could cause.
Is this changing of content of the array normal? And if not, I would really appreciate if you could help me find what is causing this strange behavior. I repeat that I am a COMPLETE BEGINNER in this and so any help, even in a matter not relating to this question would be GREATLY appreciated. Thank you in advance :)
For reference, here are the images I used.
Here is the full code if you're interested:
#include <stdio.h>
#include <stdlib.h>
#include <SDL.h>
int initProg(SDL_Window **window, SDL_Surface **surface, char *name, int x, int y, int w, int h, int flags);
void mainLoop(SDL_Window *window, SDL_Surface *surface, SDL_Surface **surfaces, size_t *surfaceCount);
void closeProg(SDL_Window *window, SDL_Window **surfaces, size_t surfaceCount);
SDL_Surface *loadImage(char *path);
int addSurface(SDL_Surface *surface, SDL_Surface **surfaces, size_t *surfaceCount);
int main(int argc, char *args[]) {
SDL_Window *window = NULL;
SDL_Surface *surface = NULL;
// The pointer to the array. Currently NULL untill something is added
SDL_Surface **surfaces = (SDL_Surface *)calloc(1, sizeof(*surfaces));
// Keeps track of the size of the array
size_t surfaceCount = 0;
char windowName[13] = "SDL Tutorial";
int windowXPos = SDL_WINDOWPOS_UNDEFINED;
int windowYPos = SDL_WINDOWPOS_UNDEFINED;
int windowWidth = 600;
int windowHeight = 600;
int flags = SDL_WINDOW_SHOWN;
if (!initProg(&window, &surface, windowName, windowXPos, windowYPos, windowWidth, windowHeight, flags)) {
return 1;
}
mainLoop(window, surface, surfaces, &surfaceCount);
closeProg(window, surfaces, surfaceCount);
return 0;
}
int initProg(SDL_Window **window, SDL_Surface **surface, char *name, int x, int y, int w, int h, int flags) {
if (SDL_Init(SDL_INIT_EVERYTHING) < 0) {
printf("Failed to initialize SDL.\nError: %s\n", SDL_GetError());
return 0;
} else {
*window = SDL_CreateWindow(name, x, y, w, h, flags);
if (*window == NULL) {
printf("Failed to create a window.\nError:%s\n", SDL_GetError());
return 0;
} else {
*surface = SDL_GetWindowSurface(*window);
return 1;
}
}
}
void mainLoop(SDL_Window *window, SDL_Surface *surface, SDL_Surface **surfaces, size_t *surfaceCount) {
// Simple program to fade between white and black background
int g = 0;
int diff = -1;
// Check contents before adding anything
showSurfaces(surfaces, *surfaceCount);
// Add an image
SDL_Surface *fire = loadImage("./fire.bmp");
addSurface(fire, surfaces, surfaceCount);
// Check contents again
showSurfaces(surfaces, *surfaceCount);
// Add another image and check contents again
SDL_Surface *ice = loadImage("./ice.bmp");
addSurface(ice, surfaces, surfaceCount);
showSurfaces(surfaces, *surfaceCount);
// Add another image and check contents a final time
SDL_Surface *man = loadImage("./man.bmp");
addSurface(man, surfaces, surfaceCount);
showSurfaces(surfaces, *surfaceCount);
int quit = 0;
SDL_Event e;
while (!quit) {
while(SDL_PollEvent(&e) != 0) {
if (e.type == SDL_QUIT) {
quit = 1;
}
}
SDL_FillRect(surface, NULL, SDL_MapRGB(surface->format, g, g, g));
SDL_UpdateWindowSurface(window);
if (g == 0 || g == 255) {
diff *= -1;
}
g += diff;
SDL_Delay(10);
}
}
void closeProg(SDL_Window *window, SDL_Window **surfaces, size_t surfaceCount) {
// Go through the array and free each surface.
for (int i = 0; i < surfaceCount; i++){
SDL_FreeSurface(*(surfaces + i));
*(surfaces + i) = NULL;
}
// Free the array itself.
free(surfaces);
SDL_DestroyWindow(window);
window = NULL;
SDL_Quit();
}
SDL_Surface *loadImage(char *path) {
SDL_Surface *image = SDL_LoadBMP(path);
if (image == NULL) {
printf("Failed to load image.\nError: %s\n", SDL_GetError());
}
return image;
}
int addSurface(SDL_Surface *surface, SDL_Surface **surfaces, size_t *surfaceCount) {
size_t new_count = *surfaceCount + 1;
SDL_Surface **temp = realloc(surfaces, sizeof(*temp) * new_count);
if (temp == NULL) {
printf("Failed to reallocate to %d bytes of memory.", sizeof(*temp) * new_count);
return 0;
} else {
surfaces = temp;
*(surfaces + new_count - 1) = surface;
*surfaceCount = new_count;
return 1;
}
}
void showSurfaces(SDL_Surface **surfaces, size_t surfaceCount) {
if (surfaceCount == 0) {
printf("\nNo current surfaces.\n");
} else {
printf("\nCurrent surfaces:\n");
for (int i = 0; i < surfaceCount; i++) {
printf("\tIndex %d: %p\n", i, *(surfaces + i));
}
putchar('\n');
}
}
If you can replicate this error, please comment down below so I know it's not something wrong with my machine or anything like that.
NOTE: I am using SDL 2.0
I realized the problem with this is that if I had created another
surface then I would have to add code to the closeProg() function to
free the surface at the end.
Not really. It's cleanest to do that, but the OS can be relied upon to release the program's memory, including any remaining dynamically allocated memory, when the program terminates. You could as easily just hold the allocated memory until then, instead of freeing it explicitly just prior to termination.
Anyway,
In the first two print-outs, everything seemed well but in the third one, you can notice that the previous addresses have changed. This
happened repeatedly and no matter how many surfaces I added. After the
first two reallocations, the array's content kept on changing.
I think that is why the program hangs when closing, because in the
closeProg function, the program is told to free an unknown pointer
that is not a surface, hence it crashes. Not to mention that I also
set that pointer to NULL, and who knows what else that could cause.
I think your analysis is plausible.
Is this changing of content of the array normal?
No. Reallocation with realloc() preserves the contents of the original space, copying it to the new space if it does not overlap, up to the lesser of the sizes of the two spaces.
I would really appreciate if you could help me find what is causing
this strange behavior.
Your addSurface() function is flawed. Although you have done well with the details of the reallocation itself, accounting both for the possibility that reallocation does not occur in place and the possibility that it fails, you do not successfully convey the new pointer to the caller.
In particular, consider this excerpt:
surfaces = temp;
*(surfaces + new_count - 1) = surface;
*surfaceCount = new_count;
The surface and surfaceCount variables are both function parameters. You seem to understand that in order to convey a new surface count value back to the caller via the latter, it must be a pointer to a variable accessible to the caller, so that the function can update that value via the pointer. That's what the last line of the excerpt does.
The situation is no different for the surfaces pointer. When in the first line of the excerpt you assign to surfaces, you are not making a change that will be visible to the caller. You're only modifying the value of a local variable inside the function. You must either add a layer of indirection for surface, or else convey the new pointer back to the caller via the function's return value. Personally, I'd go with the latter, because three-* programming is not widely accepted as good style.
Related
I have some code that should manage a 3 dimensional array with O(1) access and reading time in Erlang. Therefor I'm using Erlang NIFs. Everything is working fine except for the release() function. I always get a segmentation fault when calling it and I have no idea why.
Here is my code:
#include "erl_nif.h"
static ErlNifResourceType *DATA_RESOURCE;
typedef struct
{
int size;
ERL_NIF_TERM *** array;
ERL_NIF_TERM defaultValue;
} DATA;
static ERL_NIF_TERM new3DimArray(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
DATA *data = (DATA *)enif_alloc_resource(DATA_RESOURCE, sizeof(DATA));
int size;
enif_get_int(env, argv[0], &size);
if(argc > 1)
{
data->defaultValue = argv[1];
}else{
data->defaultValue = NULL;
}
data->size = size;
data->array = (ERL_NIF_TERM ***)enif_alloc(sizeof(ERL_NIF_TERM **) * size);
int x = 0;
while(x < size)
{
data->array[x] = (ERL_NIF_TERM **)enif_alloc(sizeof(ERL_NIF_TERM *) * size);
int y = 0;
while(y < size)
{
data->array[x][y] = (ERL_NIF_TERM *)enif_alloc(sizeof(ERL_NIF_TERM) * size);
y++;
}
x++;
}
return enif_make_resource(env, data);
}
static ERL_NIF_TERM get_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
DATA *data;
enif_get_resource(env, argv[0], DATA_RESOURCE, &data);
int x;
int y;
int z;
enif_get_int(env, argv[1], &x);
enif_get_int(env, argv[2], &y);
enif_get_int(env, argv[3], &z);
ERL_NIF_TERM res = data->array[x][y][z];
if(res == NULL && data->defaultValue != NULL)
{
res = data->defaultValue;
}
return res;
}
static void set_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
DATA *data;
enif_get_resource(env, argv[0], DATA_RESOURCE, &data);
int x;
int y;
int z;
enif_get_int(env, argv[1], &x);
enif_get_int(env, argv[2], &y);
enif_get_int(env, argv[3], &z);
ERL_NIF_TERM value = argv[4];
data->array[x][y][z] = value;
}
static void release(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
DATA *data;
enif_get_resource(env, argv[0], DATA_RESOURCE, &data);
int x = 0;
while(x < data->size)
{
int y = 0;
while(y < data->size)
{
enif_free(data->array[x][y]);
y++;
}
enif_free(data->array[x]);
x++;
}
enif_free(data->array);
enif_release_resource(data);
}
static void cleanup(ErlNifEnv *env, void *obj){}
static int load(ErlNifEnv *env, void **priv_data, ERL_NIF_TERM load_info){
DATA_RESOURCE = enif_open_resource_type(env, "mutArray", "DATA_RESOURCE", &cleanup, ERL_NIF_RT_CREATE, 0);
return 0;
}
static ErlNifFunc nif_funcs[] = {
{"new_3_dim_array", 1, new3DimArray},
{"new_3_dim_array", 2, new3DimArray},
{"get", 4, get_nif},
{"set", 5, set_nif},
{"release", 1, release}
};
ERL_NIF_INIT(mutArray, nif_funcs, load, NULL, NULL, NULL);
This is my Erlang code (to make the arity clearer):
module(mutArray).
%% ====================================================================
%% API functions
%% ====================================================================
-export([init/0, new_3_dim_array/1, new_3_dim_array/2, get/4, set/5, release/1]).
init() ->
erlang:load_nif("./mutArray", 0).
new_3_dim_array(_Size) ->
"NIF not loaded yet.".
new_3_dim_array(_Size, _DefaultValue) ->
"NIF not loaded yet.".
get(_Array, _X, _Y, _Z) ->
"NIF not loaded yet.".
set(_Array, _X, _Y, _Z, _Value) ->
"NIF not loaded yet.".
release(_Array) ->
"NIF not loaded yet.".
Btw, this is my Testcode:
mutArray:init(),
A = mutArray:new_3_dim_array(100),
mutArray:release(A).
EDIT: Ok it gets more and more weird... After some testing I fidured out that I get ** exception error: [] if enif_free(data->array); is the last call of the function. At every other position I still get the segmentation fault, even if there is just a println() after enif_free(data->array);. After some debugging I also figured out that every line before enif_free(data->array); was called. So the exception seems to happen at enif_free(data->array). Does anybody know what this means?
EDIT2: Simply leaving enif_free(data->array); out doesn't help either. I get a segmentation fault then as well.
I was able to get your code running correctly by fixing several problems.
First, your code assumes that it's OK to check the validity of an ERL_NIF_TERM by comparing it to NULL, which is incorrect. You can fix this by either initializing all of your array elements to 0 (by calling enif_make_int(env, 0) to set each element), or by using an array of structs where each struct holds an ERL_NIF_TERM and an unsigned char flag to indicate whether the term is valid or not. If you choose the latter approach, you could simply memset the struct values to 0, and if a caller requests an uninitialized element via mutArray:get/4, just return enif_make_badarg(env) to indicate they passed bad arguments to the call.
Second, both your set_nif and release functions are declared to return void when they need to return ERL_NIF_TERM instead. To fix this you can correct their return types, and then return argv[4] from set_nif and enif_make_int(env, 0) from release.
Lastly, the second argument to your enif_open_resource_type call needs to be NULL rather than the "mutArray" value you're passing, as the erl_nif man page indicates.
I am currently starting to work with c and allegro5. I want to put my initialization code into an init() method, but I am struggling to return the pointers to the ALLEGRO_EVENT_QUEUE and the ALLEGRO_DISPLAY. Although the pointers are initialized in the function, they are still NULL afterwards; I thought that values assigned to pointers would survive the end of the current scope, because the underlying value is modified, but the memory address stays the same.
Here is my code:
#include <stdio.h>
#include <allegro5/allegro.h>
void init(ALLEGRO_DISPLAY *display_ptr, ALLEGRO_EVENT_QUEUE *queue_ptr) {
al_init();
al_install_keyboard();
display_ptr = al_create_display(640, 480);
queue_ptr = al_create_event_queue();
al_register_event_source(queue_ptr, al_get_display_event_source(display_ptr));
al_clear_to_color(al_map_rgb(0,0,0));
al_flip_display();
}
int main(int argc, char **argv){
ALLEGRO_DISPLAY *display = NULL;
ALLEGRO_EVENT_QUEUE *event_queue = NULL;
init(display, event_queue);
printf("%d\n", display == NULL); //prints out 1
printf("%d\n", event_queue == NULL); //prints out 1
//Further code
return 0;
}
Any hint or help is greatly appreciated.
You need to pass the address of the pointer itself.
Try it like this
void init(ALLEGRO_DISPLAY **display_ptr, ALLEGRO_EVENT_QUEUE **queue_ptr) {
al_init();
al_install_keyboard();
*display_ptr = al_create_display(640, 480);
*queue_ptr = al_create_event_queue();
/* I assume you are guaranteed to not recieve `NULL's or why don't you check ? */
al_register_event_source(*queue_ptr, al_get_display_event_source(*display_ptr));
al_clear_to_color(al_map_rgb(0,0,0));
al_flip_display();
}
and
init(&display, &event_queue);
Remember that in c you always pass by value, so the passed pointers are copied, while they contain the same address they are stored in different places, hence altering one of them, doesn't affect the other.
Passing the address to the pointer, you can modify the address stored in the pointer.
If you want to check that what I say is true, try printing the address of the pointers in each function and you will see that they are different.
Parameters are called by value, i.e. in your case the pointer address is not returned.
To work around that you need to pass pointers to pointers like this:
#include <stdio.h>
#include <allegro5/allegro.h>
void init(ALLEGRO_DISPLAY **display_ptr, ALLEGRO_EVENT_QUEUE **queue_ptr) {
al_init();
al_install_keyboard();
*display_ptr = al_create_display(640, 480);
*queue_ptr = al_create_event_queue();
al_register_event_source(*queue_ptr, al_get_display_event_source(*display_ptr));
al_clear_to_color(al_map_rgb(0,0,0));
al_flip_display();
}
int main(int argc, char **argv){
ALLEGRO_DISPLAY *display = NULL;
ALLEGRO_EVENT_QUEUE *event_queue = NULL;
init(&display, &event_queue);
printf("%d\n", display == NULL); //prints out 1
printf("%d\n", event_queue == NULL); //prints out 1
//Further code
return 0;
}
So I've been studying C programming for a little while now, and while I can understand the basic concept behind pointers, I'm still having a lot of trouble implementing them in my programming, especially when it comes to passing them across multiple different structs. For example, I have this code snippet (I've omitted the non-relevant parts):
main.c
Globals globals;
static void display(Globals *globals)
{
int i;
for (i = 0; i < 20; i++)
{
glBegin(GL_QUADS);
glTexCoord2f(0, 0);
glVertex3f(globals->trees[i].pos.x, globals->trees[i].pos.y, globals->trees[i].pos.z);
glTexCoord2f(1, 0);
glVertex3f(globals->trees[i].pos.x + 5, globals->trees[i].pos.y, globals->trees[i].pos.z);
glTexCoord2f(1, 1);
glVertex3f(globals->trees[i].pos.x + 5, globals->trees[i].pos.y + 5, globals->trees[i].pos.z);
glTexCoord2f(0, 1);
glVertex3f(globals->trees[i].pos.x, globals->trees[i].pos.y + 5, globals->trees[i].pos.z);
glEnd();
}
}
globals.h
struct Globals
{
Tree *trees; // tree array
};
globals.c
void initGlobals(Globals *globals)
{
initTrees(&globals->trees, &globals->grid);
}
tree.h
#pragma once
#if __cplusplus
extern "C" {
#endif
#include "utils.h"
typedef struct Tree Tree;
typedef struct Grid Grid;
struct Tree
{
vec3f pos;
};
void initTrees(Tree *trees, Grid *grid);
#if __cplusplus
}
#endif
trees.c
#include "tree.h"
void initTrees(Tree *trees, Grid *grid)
{
trees = (Tree *) calloc(20, sizeof(Tree));
if (trees == NULL)
{
printf("COULD NOT ALLOCATE.");
exit(1);
}
srand(time(NULL));
float x_rand, z_rand;
int i;
for (i = 0; i < 20; i++)
{
x_rand = rand() & 50;
z_rand = rand() & 50;
Tree tree;
tree.pos = cVec3f(x_rand, accumHeight(grid, x_rand, z_rand) + 1, z_rand);
trees[i] = tree;
}
}
At the moment, I'm trying to add a set of tree structs to a tree array and then draw them one by one in main.c, but I'm getting an unhandled exception. I'm almost 100% sure that it's because there's something wrong with my pointer syntax, but I'm not sure what.
You're passing the address of a pointer (and thus a pointer-to-pointer) to a function expecting a single-indirection pointer.
Anytime you see this:
void foo(Something* p)
{
p = some allocation function
...
}
raise your brow, because its likely going to result in a memory leak, if not an outright invalid indirection, (and likely both).
In your case,
void initTrees(Tree *trees, Grid *grid);
is being invoked with
//globals.c
void initGlobals(Globals *globals)
{
initTrees(&globals->trees, &globals->grid);
}
where globals->trees is declared as:
//globals.h
struct Globals
{
Tree *trees; // tree array
};
The type of what is being passed is Tree**, yet the function expects Tree* (incorrectly, but we'll get to that in a minute). The pattern I described in the outset of this answer is repeated here:
void initTrees(Tree *trees, Grid *grid)
{
trees = (Tree *) calloc(20, sizeof(Tree)); // LOOK HERE
if (trees == NULL)
{
printf("COULD NOT ALLOCATE.");
exit(1);
}
srand(time(NULL));
float x_rand, z_rand;
int i;
for (i = 0; i < 20; i++)
{
x_rand = rand() & 50;
z_rand = rand() & 50;
Tree tree;
tree.pos = cVec3f(x_rand, accumHeight(grid, x_rand, z_rand) + 1, z_rand);
trees[i] = tree;
}
}
All this does is allocate some memory and use a trees as a value-parameter (the value is the address of... something... but thats not relevant; that address is lost the moment you make that assignment).
initTrees, needs a double-indirection pointer Tree ** and should dereference it to save the proper data being allocated:
void initTrees(Tree **trees, Grid *grid)
{
Tree *res = calloc(20, sizeof(Tree)); // NOTE: local used for allocation
if (res == NULL)
{
printf("COULD NOT ALLOCATE.");
exit(1);
}
srand(time(NULL));
float x_rand, z_rand;
int i;
for (i = 0; i < 20; i++)
{
x_rand = rand() & 50;
z_rand = rand() & 50;
res[i].pos = cVec3f(x_rand, accumHeight(grid, x_rand, z_rand) + 1, z_rand);
}
// save result
*trees = res; // NOTE: save to out-parameter
}
Or something similar. Note your compiler should be screaming at your about passing an invalid parameter (types mismatching). Also, note the removal of the calloc cast, which isn't needed (nor advised) in C programs.
Best of luck
I defined a struct Board with an int n, the value of which is a small integer. Before this method is called by the gtk signal "clicked", board->n has the correct value.
However, when calling this funciton, the value of n printed out by the first printf statement is something really large 32665.
draw_token (GtkButton *button, cairo_t *cr, Board *board){
printf("n: %d\n", board->n);
printf("button clicked\n");
//parse label of a button into the corresponding column number
guint col = (int)strtol(gtk_button_get_label(button), NULL, 0);
if (make_move(board, col) == false){
printf("draw cairo\n");
}
}
the struct:
typedef struct board Board;
struct board{
int k;
int n;
char *moves;
int player;
};
function where the callback is located:
void gui_make_buttons(GtkWidget *box, Board *board){
guint n = board->n;
for (int i = 1 ; i <= n ; i++){
GtkWidget *button = gtk_button_new();
//make label for button
char label[3];
sprintf(label, "%d", i-1);
gtk_button_set_label((GtkButton*)button,label);
// gtk_widget_show(button);
gtk_container_add(GTK_CONTAINER(box), button);
g_signal_connect(button, "clicked",G_CALLBACK(gui_draw_token), board);
}
}
Could someone please explain to me why the value of n has changed to large values like 36751 whenever the button is clicked?
Thank you very much
complete code with main
gui.c:
#include <stdio.h>
#include <stdlib.h>
#include <gtk/gtk.h>
#include <stdbool.h>
#include "gui.h"
#include "board.h"
#include "connect.h"
gboolean draw_board (GtkWidget *widget, cairo_t *cr, gpointer board){
guint width, height;
width = gtk_widget_get_allocated_width (widget);
height = gtk_widget_get_allocated_height (widget);
guint row = height/((Board*)board)->n;
guint col = width/((Board*)board)->n;
printf("%d\n", row);
printf("%d\n", col);
// cairo_set_source_rgb (cr, 100, 100, 255);
// cairo_paint (cr);
cairo_set_source_rgb(cr, 0, 0, 255);
for (int i = 1; i < ((Board*)board)->n ; i++){
//draw horizontal grids;
cairo_move_to (cr, 0, row*i);
cairo_line_to (cr, width, row*i);
//draw vertical grids;
cairo_move_to (cr, col*i, 0);
cairo_line_to (cr, col*i, height);
}
// cairo_arc (cr, 100, 100, 50, 0, 2 * G_PI);
// cairo_move_to (cr, 30, 30);
// cairo_line_to (cr, 50, 50);
cairo_stroke (cr);
return false;
}
int main (int argc, char *argv[]){
//check for correct number of arguments.
if (!check_argument(argc, argv))
return EXIT_FAILURE;
int k = strtol(argv[1], NULL, 0);
int n = strtol(argv[2], NULL, 0);
play_game(k, n);
return EXIT_SUCCESS;
}
//show widgets and get gtk going
CGUI *gui_start_gtk(Board *board){
//assigns board to the gui struct at beginning of game
CGUI *gui = make_gui (board);
// gtk_widget_queue_draw (gui->drawing_area);
// gui_draw_init_board(gui);
gtk_widget_show_all (gui->window);
gtk_main ();
return gui;
}
/*
void gui_draw_init_board(GtkWidget *widget, cairo_t *cr, CGUI *gui){
printf("HI\n");
if (gui) {
guint k = gui->board->k;
guint n = gui->board->n;
printf("%d\n", k);
}
}
*/
void gui_make_buttons(GtkWidget *box, Board *board){
// guint n = board->n;
for (int i = 1 ; i <= (board->n) ; i++){
GtkWidget *button = gtk_button_new();
//make label for button
char label[3];
// sprintf(label, "%d", i-1);
sprintf(label, "%d", i-1);
gtk_button_set_label((GtkButton*)button,label);
// gtk_widget_show(button);
gtk_container_add(GTK_CONTAINER(box), button);
g_signal_connect(button, "clicked",G_CALLBACK(gui_draw_token), board);
}
}
void gui_draw_token (GtkButton *button, cairo_t *cr, gpointer board){
printf("button clicked\n");
printf("n: %d\n", ((Board*)board)->n);
//parse label of a button into the corresponding column number
guint col = (int)strtol(gtk_button_get_label(button), NULL, 0);
printf("%d\n", col);
printf("n: %d\n", ((Board*)board)->n);
if (make_move(board, col) == false){
printf("draw cairo\n");
}
}
CGUI *make_gui(Board *board){
CGUI *gui = (CGUI*) malloc(sizeof(CGUI));
//assign variables to gui object
gui->board = board;
GtkWidget *window;
GtkWidget *frame;
GtkWidget *drawing_area;
gtk_init(NULL, NULL);
//set up initial window
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "Connect-N-K");
gtk_window_set_default_size (GTK_WINDOW(window), 600, 650);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_widget_show(window);
g_signal_connect_swapped(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), gui);
// g_signal_connect (window, "draw", G_CALLBACK (gui_draw_buttons), board);
//create boxes to fit buttons and drawing area
GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
GtkWidget *draw_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
GtkWidget *button_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
//make all buttons take up even amount of space
gtk_box_set_homogeneous((GtkBox*)button_box, true);
gtk_widget_set_size_request(button_box, 600, 50);
// gtk_box_pack_start (box, window, false, false, false);
gtk_container_add(GTK_CONTAINER(window), box);
gtk_container_add(GTK_CONTAINER(box), draw_box);
gtk_container_add(GTK_CONTAINER(box), button_box);
//set up initial frame
frame = gtk_frame_new (NULL);
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
gtk_widget_set_size_request(frame, 600, 600);
gtk_container_add(GTK_CONTAINER(draw_box), frame);
//create and pack buttons.
gui_make_buttons(button_box, board);
//set up drawing area
drawing_area = gtk_drawing_area_new ();
gtk_widget_set_size_request(drawing_area, 600, 600);
gtk_container_add (GTK_CONTAINER (frame), drawing_area);
g_signal_connect (drawing_area, "draw", G_CALLBACK (draw_board), board);
printf("n: %d\n", board->n);
// printf("board in gui: %d\n", *board);
gui->window = window;
gui->frame = frame;
gui->drawing_area = drawing_area;
/*
//show widgets
connect.c :
/* connect.c */
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include "gui.h"
#include "board.h"
#include "connect.h"
#define BUFFER_SIZE 10
/*
static int check_argument(int argc, char *argv[]);
static void play_game(int k, int n);
*/
/*
int main(int argc, char *argv[]){
//check for correct number of arguments.
if (!check_argument(argc, argv))
return EXIT_FAILURE;
int k = strtol(argv[1], NULL, 0);
int n = strtol(argv[2], NULL, 0);
play_game(k, n);
return EXIT_SUCCESS;
}
*/
int check_argument(int argc, char *argv[]){
if(argc < 3 || argc >3){
fprintf(stderr, "Parameters entered incorrectly. Input two integers for k and n respectively.\n");
return false;
}
else
return true;
}
//go through all steps of the game
void play_game(int k, int n){
//check to see if k and n are appropriate
if(k>n){
fprintf(stderr, "k is greater than n, game will never be won.\n");
return;
}
Board *board = make_board(k, n);
// print_board(board);//print initial board
//initiate gui
// CGUI *gui = make_gui();//set first three vars
// gui_set_board(gui, board);//set the fourth var
// CGUI *gui = gui_start_gtk(board);
// connect_play_game_text(board);
connect_play_game_gui(board);
/* int player = 1; //let first player go first
char *s = (char*)malloc(BUFFER_SIZE);//allocate memory for int to read
int move_result;
do{
fgets(s, BUFFER_SIZE, stdin);
int cols = strtol(s, NULL, 0);
move_result = make_move(board,cols,player);
//switch players if legal move and no one wins
if(move_result == false)
player = 3-player;
//do nothing is move is illegal(move_result = -1, thus letting
//the same player choose again.
}
while(move_result != true);
//free up resources
free(s);
destroy_board(board);
*/
}
int connect_play_game_gui(Board *board){
printf("n in connect: %d\n", board->n);
CGUI *gui = gui_start_gtk(board);
// gui_set_board(gui, board);//set the fourth var
// int player = 1; //let first player go first
// char *s = (char*)malloc(BUFFER_SIZE);//allocate memory for int to read
// int move_result;
// fgets(s, BUFFER_SIZE, stdin);
// int cols = strtol(s, NULL, 0);
// move_result = make_move(board,cols,player);
//switch players if legal move and no one wins
// if(move_result == false){
// player = 3-player;
return true;
// }
//do nothing if move is illegal(move_result = -1, thus letting
//the same player choose again.
// while(move_result != true);
//free up resources
// free(s);
// destroy_board(board);
}
int connect_make_move_gui(int col, Board *board, int player){
return 1;
}
void connect_play_game_text(Board *board){
print_board(board);//print initial board
char *s = (char*)malloc(BUFFER_SIZE);//allocate memory for int to read
int move_result;
do{
fgets(s, BUFFER_SIZE, stdin);
int cols = strtol(s, NULL, 0);
move_result = make_move(board,cols);
}
while(move_result != true);
//free up resources
free(s);
destroy_board(board);
}
//initiate gui
//static void connect_init_gui
board.c:
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include "board.h"
#define DRAW 2
#define ILLEGAL_MOVE -1
static char set_player_char(int player);
static int check_draw(Board *board);
//make a new board
Board *make_board(int k, int n){
Board *board = (Board *) malloc(sizeof(Board));
board->moves = (char *)malloc(n*n);
(board->k) = k;
(board->n) = n;
board->player = 1;
//make an array of empty slots based of size n^2
for(int i = 0; i < n*n; i++){
board->moves[i] = '.';
}
return board;
}
//print board with updated moves, print last row first.
void print_board(Board *board){
int n = board->n;
//loop through each row
for(int i = n-1; i >= 0; i--){
//loop through each column
for(int j = 0; j<n; j++){
printf("%c", (board->moves)[i*n+j]);
printf(" ");//add space between columns
}
printf("\n"); //wrap around each row
}
printf("\n");
}
//set char for player
static char set_player_char(int player){
char player_char;
if (player == 1)
player_char = 'o';
else
player_char = '*';
return player_char;
}
//update board based on player's input, return the row move is made
int make_move(Board *board, int x){
printf("inmakemove n: %d\n", board->n);
// printf("board in make_move: %d\n", &board);
//if move is illegal, return
if (!check_legal_move(board, x))
return ILLEGAL_MOVE;
int n = board->n;
int row;
//loop through the rows of the given column to find an empty spot.
for (int i = 0; i < n; i++){
if ((board->moves)[i*n+x] == '.'){
(board->moves)[i*n+x] = set_player_char(board->player);
row = i;
break;
}
}
print_board(board);
//Check to see if a player has won the game.;
int stat = check_win(board, x, row);
if (stat == true){
fprintf(stdout, "Player %d won the game.\n", board->player);
return true;
}
//if all slots are filled, game is a draw.
if(stat == DRAW){
fprintf(stdout, "Game was a draw.\n");
return true;
}
//if no one won, game continues.
else{
board->player = 3-(board->player);
return false;
}
}
//check to see if move x is legal
int check_legal_move(Board *board, int x){
int n = board->n;
//see if column entered is legal.
if (x >= (board->n) || x<0){
fprintf(stderr, "Illegal move by player %d at column %d\
, number entered outside range of available columns.\n", board->player, x);
return false;
}
//see if column entered is already filled
if ((board->moves)[(n-1)*n+x] != '.'){
fprintf(stderr, "Illegal move by player %d at column %d\
, column is already filled.\n", board->player, x);
return false;
}
return true;
}
//check for winning move
int check_win(Board* board, int x, int row){
int n = board->n;
int k = board->k;
int current_move = row*n+x; //slot that the current move fills
char *moves = board->moves;
char player_char = set_player_char(board->player);
int score;
score = 1;
//Check to see how many continuous slots are filled with the current player'
//s token horizontally.
//going right
for (int i = 1; i<k && x+i<n; i++){
if(moves[current_move+i] == player_char)
score ++;
else
break;
}
//going left
for(int i = 1; i<k && x-i>=0; i++){
if(moves[current_move-i] == player_char)
score++;
else
break;
}
//if horizontally connect to k, the player wins.
if (score>=k)
return true;
//if not, check vertical.
score = 1;
//going up
for (int i = 1; i<k && row+i<n; i++){
if(moves[current_move+n*i] == player_char)
score ++;
else
break;
}
//going down
for(int i = 1; i<k && row-i>=0; i++){
if(moves[current_move-n*i] == player_char)
score ++;
else
break;
}
//if vertically connect to k, the player wins.
if (score>=k)
return true;
//if not, check rising to right diagnol. Reset score like previously.
score = 1;
//going right and up
for(int i = 1; i<k && row+i<n && x+i<n; i++){
if(moves[current_move+n*i+i] == player_char)
score ++;
else
break;
}
//going left and down
for(int i = 1; i<k && row-i>=0 && x-i>=0; i++){
if(moves[current_move-n*i-i] == player_char)
score ++;
else
break;
}
//if right diagonally connect to k, the player wins.
if (score>=k)
return true;
//if not, check left rising diagonal.
score = 1;
//check right and down
for(int i = 1; i<k && x+i<n && row-i>=0; i++){
if(moves[current_move-n*i+i] == player_char)
score ++;
else
break;
}
//check left and up
for(int i = 1; i<k && x-i>=0 && row+i<n; i++){
if(moves[current_move+n*i-i] == player_char)
score ++;
else
break;
}
//if left diagonally connect to k, the player wins.
if (score>=k)
return true;
if(check_draw(board))
return DRAW;
//if no k connect is made in any direction, game is not won.
return false;
}
//check to see if game has come to a draw
static int check_draw(Board *board){
int n = board->n;
//loop through the top row to see if there are any empty slots left
for (int i = 0; i<n; i++){
if((board->moves)[n*(n-1)+i] == '.')
return false;
}
//if no empty slots left, game was a draw.
return true;
}
//Free up resources.
void destroy_board(Board *board){
free(board->moves);
free(board);
}
I'm sorry this is long and messy, I'm still working on the code and new to C.
You need to change void gui_draw_token (GtkButton *button, cairo_t *cr, gpointer board) to void gui_draw_token (GtkButton *button, gpointer board)
You can't just make up callback signatures and hope things work. For signatures you need to look at the documentation appropriate to the signal. For example in this case the GtkButton clicked signal documentation.
It would also help us to answer your question if you would remove all the unnecessary code and commented out code.
not that I know gtk, but a quick google, it seems, the board you are getting in your callback isn't actually a board you are passing. Your callback signature for the clicked event is incorrect
it should look a little like
void button_clicked(GtkWidget *widget, gpointer data)
EDIT: Yeah don't pay attention to this. I thought the char label[3] was initialized outside the loop and he was updating the label[0] three times, resulting in undefined output for label[1] and label[2]
That's what I get for a cursory look at 739782781 lines of code. Sorry about that everyone.
you're not telling the label which one to update in the sprintf
char label[3];
sprintf(label, "%d", i-1);
^ is wrong
it should be
char label[3];
sprintf(label[n-1], "%d", i-1);
I think.
I have an array of typedef structure.
It is declared as such:
vertex vertexArray[numberVertices];
I then have this in my main code:
for(i=0;i<numberVertices;i++)
{
if(vertexArray[i].source == 5)
{
source = vertexArray[i].number;
walk(vertexArray, vertexArray[i], source, headMaxPairList );
}
}
To hopefully perform the walk:
void walk(vertex *vertexArray, vertex v, int source, maxPairing *head)
{
int i;
adjEdge *traverse;
int moveVertex;
int sink;
moveVertex = vertexArray[v.number-1].number;
if(vertexArray[moveVertex-1].color != 5 && vertexArray[moveVertex-1].sink == 5)
{
sink = vertexArray[moveVertex-1].number;
vertexArray[moveVertex-1].color = 5;
addMaxPair(head, source, sink);
}
else
{
walk(vertexArray, vertexArray[moveVertex-1], source, head);
}
}
However, I am getting a seg-fault on the function:
in walk (vertexArray=Cannot access memory at address 0x7fffff3fefe8
I believe this has to do with the way I'm passing vertexArray.
It is my understanding that arrays are actually pointers, hence the vertex *vertexArray and then an individual member is just a vertex, not a pointer hence vertex v.
If anyone could help me with getting this passed correctly I'd be appreciative.
Side note, if anyone can tell if my walk looks like it'll work correctly that's a plus!
Arrays are not same as pointers.
Read the following Links for clarification:
Q: So what is meant by the ``equivalence of pointers and arrays'' in C?
http://c-faq.com/aryptr/aryptrequiv.html
Q: But I heard that char a[] was identical to char *a.
http://c-faq.com/aryptr/aryptr2.html
To avoid Seg-Fault, add the following checks to walk() function:
void walk(vertex *vertexArray, vertex v, int source, maxPairing *head)
{
int i;
adjEdge *traverse;
int moveVertex;
int sink;
/* Add this Check to Avoid Seg Fault, you need to make the value of
'numberVertices'available to this function as this is your array size */
if ((((v.number-1)<0)||((v.number-1)>numberVertices))
{
return;
}
/* Check Ends */
moveVertex = vertexArray[v.number-1].number;
/* Another Check */
if((moveVertex-1<0)||(moveVertex-1>numberVertices))
{
return;
}
/* Check Ends */
if(vertexArray[moveVertex-1].color != 5 && vertexArray[moveVertex-1].sink == 5)
{
sink = vertexArray[moveVertex-1].number;
vertexArray[moveVertex-1].color = 5;
addMaxPair(head, source, sink);
}
else
{
walk(vertexArray, vertexArray[moveVertex-1], source, head);
}