0

I'm having a strange behavior with the following simple ANSI C code. I have a pointer to a char inside a struct, and somehow, i have bad pointers, nulls and segfaults. Am i doing something stupid?

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>

#define MAX_ENTITIES 10

typedef enum
{
    COMPONENT_NONE = 0,
    COMPONENT_DISPLACEMENT = 1 << 0,
    COMPONENT_VELOCITY = 1 << 1,
    COMPONENT_APPEARANCE = 1 << 2
} component_t;

typedef struct
{
    float x;
    float y;
} Displacement;

typedef struct
{
    float x;
    float y;
} Velocity;

typedef struct
{
    char *name;
} Appearance;

typedef struct
{
    int entities[MAX_ENTITIES];

    Displacement displacement[MAX_ENTITIES];
    Velocity velocity[MAX_ENTITIES];
    Appearance appearance[MAX_ENTITIES];
} scene_t;

typedef struct
{
    int active;
    scene_t *current_scene;
} game_t;

unsigned int entity_create(scene_t *scene)
{
    unsigned int entity;

    for (entity = 0; entity < MAX_ENTITIES; ++entity) {
        if (scene->entities[entity] == COMPONENT_NONE) {
            printf("Entity created: %u\n", entity);
            return entity;
        }
    }

    printf("Error! No more entities left!\n");
    return MAX_ENTITIES;
}

unsigned int scene_add_box(scene_t *scene, float x, float y, float vx, float vy)
{
    unsigned int entity = entity_create(scene);

    scene->entities[entity] = COMPONENT_DISPLACEMENT | COMPONENT_VELOCITY | COMPONENT_APPEARANCE;

    scene->displacement[entity].x = x;
    scene->displacement[entity].y = y;

    scene->velocity[entity].x = vx;
    scene->velocity[entity].y = vy;

    scene->appearance[entity].name = "Box";

    return entity;
}

unsigned int scene_add_tree(scene_t *scene, float x, float y)
{
    unsigned int entity = entity_create(scene);

    scene->entities[entity] = COMPONENT_DISPLACEMENT | COMPONENT_APPEARANCE;

    scene->displacement[entity].x = x;
    scene->displacement[entity].y = y;

    scene->appearance[entity].name = "Tree";

    return entity;
}

unsigned int scene_add_ghost(scene_t *scene, float x, float y, float vx, float vy)
{
    unsigned int entity = entity_create(scene);

    scene->entities[entity] = COMPONENT_DISPLACEMENT | COMPONENT_VELOCITY;

    scene->displacement[entity].x = x;
    scene->displacement[entity].y = y;

    scene->velocity[entity].x = vx;
    scene->velocity[entity].y = vy;

    return entity;
}

void update_render(scene_t *scene)
{
    const int mask = (COMPONENT_DISPLACEMENT | COMPONENT_APPEARANCE);
    unsigned int entity;
    Displacement *d;
    Appearance *a;

    for (entity = 0; entity < MAX_ENTITIES; ++entity) {
        if ((scene->entities[entity] & mask) == mask) {
            d = &(scene->displacement[entity]);
            a = &(scene->appearance[entity]);

            printf("%s at (%f, %f)\n", a->name, d->x, d->y);
        }
    }
}

void game_init(game_t *game)
{
    scene_t scene;

    memset(&scene, 0, sizeof(scene));
    game->current_scene = &scene;
    game->active = 0;

    scene_add_tree(game->current_scene, 5.0f, -3.2f);
    scene_add_box(game->current_scene, 0.0f, 0.0f, 0.0f, 0.0f);
    scene_add_ghost(game->current_scene, 10.0f, 4.0f, 0.0f, 0.0f);
}

void game_update(game_t *game)
{
    update_render(game->current_scene);
}


int main(int argc, char **argv)
{
    game_t game;

    memset(&game, 0, sizeof(game));
    game_init(&game);

    while (game.active == 0) {
        game_update(&game);
    }

    return 0;
}

4 Answers 4

2

In game_init, you are declaring a local variable of type scene_t. Once game_init ends, this variable no longer exists - you cannot use it correctly outside that function, but that's what you try to do when you access the scene inside your game_t variable.

If you want to create a scene_t which can be used outside of the function, you need to allocate the memory for it manually by using

scene_t* scene = malloc(sizeof(scene_t));

...and then working with it as a pointer instead.

Sign up to request clarification or add additional context in comments.

7 Comments

Should i also apply memset to it? Or a malloc is enough?
@vinnylinux: You should still use memset since you don't know what might be in that piece of memory. Alternatively, you could use calloc instead of malloc to have it cleared during allocation.
What is faster? malloc and memset or calloc?
@vinnylinux: I would expect the performance to be more or less the same, and therefore not worth worrying about. If you really are in a situation where you need every last cycle, profile it and see which is better.
Alright, thanks! Also, do i have to malloc char *name inside the Appearance struct? And, when freeing a struct, do i also have to free it's members, or is this done automatically?
|
1

You are saving a local address, when function goes out of scope "scene" will be destroyed.

scene_t scene;

memset(&scene, 0, sizeof(scene));
game->current_scene = &scene;

You should instead allocate scene

scene_t* scene = malloc(sizeof(scene_t)); 

Comments

1

The problem is that you're not allocating a scene:

scene_t scene;

memset(&scene, 0, sizeof(scene));
game->current_scene = &scene;

the scene_t object here is on the stack and that memory will be reclaimed once you exit from the function. You should instead use

scene_t *pscene = malloc(sizeof(scene_t));

memset(pscene, 0, sizeof(scene));
game->current_scene = pscene;

this way the memory will be allocated from the heap, surviving the exit from the function.

You will need later to free this memory once done with the object.

3 Comments

When i free a struct, e.g: free(game), do i have to free it's members as well?
Do i have to malloc char *name inside the Appearance struct?
@vinnylinux: you don't free a struct, you free a pointer. For the memory manager routines malloc and free memory is just bytes and they don't care about what is the content. The struct game contains a pointer to another block of memory and you should take care of it before deallocate a game structure if you allocate it with malloc. On the other hand scene instead contains only arrays of other structs (and no pointers to other blocks of memory) and therefore nothing is needed for them.
1

You also don't assign a value to scene->appearance when you are creating a new ghost. When you then try and print the appearence later you'll get whatever the pointer happens to point to printed ou.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.