4

In C++ we have structures which have a constructor and the destructor. It makes life much easier especially when it going to have the pointers, therefore dynamically allocated memory in the structure. You can even use std::shared_pointer library to deal with the pointers.

class A{
private:
    int size;
    double* stack;

public:    
   A(int size) : this->size(size){}
   ~A(){free(stack);}
};

But my maths lecturer doesn't like C++ and prefers everything in C. So I had to use C instead and came up with the following structure:

typedef struct vectorOfDoubles{
    double* stack;
    int size;
} vector;

I made up function that calculate the median of the vector of doubles.

double median_(const vector* v) {
    vector n;  // creates vector class object
    n.stack = (double*)malloc(sizeof(double)*(n.size = v->size));  // takes double ptr and allocates the right amount of memory for it
    memcpy(n.stack, v->stack, sizeof(double)*n.size);  // copies the array of doubles.
    sort(&n);  // sorts the array of doubles 
    if(v->size%2)  // checks for odd size
        return n.stack[(v->size/2+1)];  // return median for odd size
    else
        return (n.stack[(v->size/2)]+n.stack[(v->size/2+1)])/2;  // return median for even size
}

As an example of the bad practices I didn't free the memory. When the function returns its value it destructs the local variables and structures. But my structure has a pointer that holds the allocated memory. Unlikely after some research on the internet I didn't find any good destruction method solution for these situations.

My question is how the old-school C programmers dealt with those situations when they want to free the pointers in the structure but they do not have the destructor for structure that would execute itself to do a certain job?

Kaspar Siricenko
  • 237
  • 2
  • 18
  • 4
    You have to call the appropriate `free` or `clean` or similarly called function yourself when the object is not needed. Documenting which function takes the ownership of an object (if it takes it at all) helps while doing that. That's C, it's all there is to it. – Daniel Kamil Kozar Mar 02 '16 at 18:48
  • 1
    As well as writing a destructor function (that calls `free()`), you should also consider writing a 'copy constructor' (which will do the `malloc()` and `memmove()` or `memcpy()` operation — and validate that the allocation was successful). And you make sure that after you've successfully called the constructor, you call the destructor before returning from your function. In context, that means you have to compute the median and save it in a local variable, destruct your sorted copy of the vector, and then return the saved value. – Jonathan Leffler Mar 02 '16 at 18:53
  • @JonathanLeffler that's what I was thinking for the first time but then decided to ask for any less dirty solution that this... – Kaspar Siricenko Mar 02 '16 at 18:59
  • 2
    The "old school" C programmers did not think in terms of constructors and destructors. "Old school" C had a different structure, where not the object was responsible for creation / deletion, but the *caller*. You create an object, operate on it bz passing it to worker functions, and delete it again. The object itself is "dumb". Take `FILE *` as an example. You `fopen()`, `fread()` / `fwrite()`, and `fclose()`. The `FILE` object itself is just passed around, it doesn't do things, it gets things done to it. – DevSolar Mar 02 '16 at 19:08

3 Answers3

1

If you want to free an object that uses some kind of hierarchical storage (i.e., internal pointers to other object) in C, I usually write a free function specific to that object. For instance:

void free_my_obj(my_obj_t *my_obj)
{
    free(my_obj->a);
    free_my_obj2(my_obj->my_obj2);
    free(my_obj);
}
Tom Karzes
  • 22,815
  • 2
  • 22
  • 41
1

A simple pointer needs a *alloc() and lastly a free().

A structure with dynamic fields deserves a crafted vector_alloc() and vector_free().

An old school flavored result:

// Return non-0 on error
int vector_alloc(vector *ptr, size_t size) {
  assert(ptr);
  ptr->stack = calloc(size, sizeof *(ptr->stack));
  if (ptr->stack) {
    ptr->size = size;
    return 0;
  }
  ptr->size = 0;
  return 1;
}

void vector_free(vector *ptr) {
  assert(ptr);
  free(ptr->stack);
  ptr->stack = NULL;
  ptr->size = 0;
}


double median_(const vector* v) {
    vector n;
    if (vector_alloc(&n, v->size)) return 0.0/0.0;
    memcpy(n, v->stack, sizeof *n->stack *n.size);
    sort(&n);  
    double y;   
    if(v->size%2) 
        y = n.stack[(v->size/2+1)];
    else
        y = (n.stack[(v->size/2)]+n.stack[(v->size/2+1)])/2;
    vector_free(&n);
    return y;
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
0

At beginning: n.stack=…malloc();

At end: free(n.stack);

Every malloc() near beginning of block, should have a matching free(); near end of block.

Arif Burhan
  • 507
  • 4
  • 12
  • Basically you're right, at least if there is no `return`, `continue`, `break` or `goto`in between. But even then, it is much more complicated than it looks when your data structures and control flows get more complex. Actually I should downvote this answer because it conveys a false impression of simplicity :) – Frank Puffer Mar 02 '16 at 19:42