Gribbles Chan
Gribbles Chan

Reputation: 123

Pointers with structs

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.

Upvotes: 0

Views: 88

Answers (1)

WhozCraig
WhozCraig

Reputation: 66194

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

Upvotes: 2

Related Questions