user3467349
user3467349

Reputation: 3191

Memory allocation for structure extension

Suppose I have two structures one of which extends the other one (as below). What's the correct procedure for dealing with memory when creating an extended structure from an existing structure? Should malloc be called again for the new structure? Would this be doubling up memory allocation where it shouldn't be?

typedef struct Grid {
  int rows;
  int cols;
  int * grid;
} Grid;

typedef struct SuperGrid { 
    Grid; 
    char * property;
    int id;
} SuperGrid;

static Grid * new_grid(int rows, int cols) {
  Grid * grid = (Grid *)(malloc(sizeof(Grid))); 
  grid->rows= rows;
  grid->cols= cols;
  grid->grid = (int *)(calloc(rows*cols, sizeof(int)));
}

static SuperGrid * new_super_grid(int rows, int cols) {
    /* What needs to happen here? */
    return (SuperGrid *)new_grid(rows, cols); 
}

Upvotes: 0

Views: 79

Answers (1)

chqrlie
chqrlie

Reputation: 144800

Your structure extension mechanism is similar to a primitive C++ class derivation system. To achieve your goal, you need to separate structure allocation and initialization:

typedef struct Grid {
    int rows;
    int cols;
    int *grid;
} Grid;

typedef struct SuperGrid { 
    Grid; 
    char *property;
    int id;
} SuperGrid;

static Grid *init_grid(Grid *grid, int rows, int cols) {
    if (grid) {
        grid->rows = rows;
        grid->cols = cols;
        grid->grid = (int *)calloc(rows * cols, sizeof(int));
    }
    return grid;
}

static Grid *new_grid(int rows, int cols) {
    return init_grid((Grid *)malloc(sizeof(Grid)), rows, cols);
}

static SuperGrid *init_super_grid(SuperGrid *sg, int rows, int cols,
                                  const char *property, int id) {
    if (sg) {
        init_grid((Grid *)sg, rows, cols);
        sg->property = strdup(property);
        sg->id = id;
    }
    return sg;
}

static SuperGrid *new_super_grid(SuperGrid *sg, int rows, int cols,
                                 const char *property, int id) {
    return init_super_grid((SuperGrid *)malloc(sizeof(SuperGrid)),
                           rows, cols, property, id);
}

The allocators may fail and will return NULL in case of memory allocation failure. You may want them to fail in more explicit ways such as exiting the program with an error message via abort. Similarly, the delete_xxx functions should gracefully ignore NULL pointers as free does and the C++ delete operator.

For consistency, you will also separate the delete_grid and finalize_grid functions in order to call the finalize_grid from finalize_super_grid. It is not strictly necessary for this simple example, but may prove handy if you instantiate Grid structures that are not allocated (with static or automatic storage or embedded in more complex superstructures).

This poor man's C++ class system will quickly show its limitations. You may consider using C++ directly.

Upvotes: 3

Related Questions