3

I'm struggling to come up with a clean way to handle my allocated memory in C. Suppose I have something like this:

void function(int arg) {
  char *foo;
  foo = (char *)malloc(sizeof(char) * 100);
  int i = func1(arg, &foo);

  char *bar;
  bar = (char *)malloc(sizeof(char) * 100);
  int j = func2(&bar);

  free(foo);
  free(bar);
}

My problem is that func1 and func2 may encounter error and exit(1), so I need to free foo and bar when that happens.

If func1 encounters error, I just call free(foo) and I'll be good. But if func2 encounters error, I cannot just call free(bar) since I also need to free foo. This can get really complicated and I feel that this is not the right way to handle memory.

Am I missing anything here? It will be awesome if someone can point me the right direction. Thanks!

danqing
  • 3,348
  • 2
  • 27
  • 43
  • 6
    If they are doing `exit(1)` then you don't need to free anything - the system will do that for you when the process is torn down. – Douglas Leeder Sep 18 '12 at 14:53
  • 2
    Generally (not in this case) [you need `goto`](http://stackoverflow.com/a/10464368/596781) to write systematic C in the presence of resource acquisition. – Kerrek SB Sep 18 '12 at 14:56
  • @DouglasLeeder so if I don't free when `exit(1)` is that not considered a memory leak? – danqing Sep 18 '12 at 15:00
  • @iBlue: generally, no. However, if you're code ever ends up using different means of error recovery, a memory leak could ensue if the then-required changes are not made. (If you declare all your pointers and initialize them to `NULL` before the first `malloc` takes place, your error recovery section can simply call `free` with all of them.) – eq- Sep 18 '12 at 15:02
  • 1
    Correct. You should not free before exiting. In general, however, having random functions `exit(1)` is not a good idea, as it inhibits code reuse. – Will Palmer Sep 18 '12 at 15:03
  • 1
    @WillPalmer so what's a better way than `exit(1)`? Should I just return something like -1 to mean error and handle that in main function? – danqing Sep 18 '12 at 15:13
  • 1
    Using `exit()` in production code might lead to situations comparable to parachuting out of your plane, just because you ran out of petrol. Try to design your system to be able to bring down the plane by normal ways in any possible case. – alk Sep 18 '12 at 15:25
  • 2
    @iBlue: it will be *reported* as a type of memory leak by your leak-detection software. So even though it's not harmful in production code, there's some incentive to clean up where possible in order to keep spurious warnings from obscuring any real memory leaks. In practice you care a lot more about another kind of leak: unreachable unfreed blocks while the program is still running are much worse than unfreed blocks at program exit. – Steve Jessop Sep 18 '12 at 16:09

6 Answers6

3

If a function calls exit you don't have to clean your memory usage at all, it will be freed by the OS. But if you need to release other resources (e.g. lock file, clean temp files, ...) then you can use the atexit function or if you use the gnu libc the on_exit function to do the job.

Kwariz
  • 1,306
  • 8
  • 11
2

If func1() or func2() is going to call exit(1) on some condition, you don't have worry about freeing memory for foo or bar as the operating sytem will typically do clean up once the process exits.

As long as you free at the right time (before going out of function) during the normal course of execution, you are just fine with no memory leak.

P.P
  • 117,907
  • 20
  • 175
  • 238
  • Does that mean when you do `exit(1)` you don't need to worry about memory leak at all and it will be taken care of? (so that when you run valgrind, for example, it won't complain?) – danqing Sep 18 '12 at 14:59
  • @iBlue, valgrind on a run that aborts is usually not telling you the right things about leaks anyhow. – Jens Gustedt Sep 18 '12 at 15:00
  • It will probably complain, though no harm is done in modern operating systems. I'd free the memory nevertheless. Can't you change func1 and func2 to return error codes instead of simply exiting? – Spidey Sep 18 '12 at 15:02
  • That's right. You code as such is quite fine. Because you can't handle the case where `exit` may be called and is also unnecessary. – P.P Sep 18 '12 at 15:03
  • @iBlue This is similar to what would happen if the process (your program) is killed directly by OS when it's still running. If you can't avoid calling `exit`, it's ok to leave it as you have now. – P.P Sep 18 '12 at 15:30
0

I think so there can be a one simple approach to this problem.

Just maintain an allocCode throughout your program when allocating resources something like one given below. There are few keypoints to remember. First is that, don't you break statement in your switch case. Increment the allocCode, for every successful allocation of resource. For every resource added, you should add a case in the switch at the top, with one higher number. So calling the function freeResourceBeforeExit() will free all your resources in correct order. Remember that, since there is no break, the switch case will enter at the correct position and free all the resource which are below its entry point.

I will write the psuedo-code.

int allocCode = 0;

int freeResourceBeforeExit()
{
    switch(allocCode)
    {
        case 4:
           free(resource3);
        case 3:
           free(resource2);
        case 2:
           free(resource1);
        case 1:
           free(resource0);
    }
    exit(0);
}


int main()
{
   ...
   resource0 = malloc(10);
   allocCode++;
   func1();
   ...
   resource1 = malloc(100);
   allocCode++;
   func2();
   ...
   resource2 = malloc(1000);
   allocCode++;
   ...
   func3();
   ...
   resource3 = malloc(10000);
   allocCode++;
   func4();
   ... 
   so on..
}

Hope this helps !

Raj
  • 3,300
  • 8
  • 39
  • 67
0

If you divide your work into several parts, it will be much easier to manage your resources.

void part1(int arg) {
  char *foo;
  foo = (char *)malloc(sizeof(char) * 100);
  int i = func1(arg, &foo);

  free(foo);
}

void part2(void) {
  char *bar;
  bar = (char *)malloc(sizeof(char) * 100);
  int j = func2(&bar);

  free(bar);
}

void function(int arg) {
    part1(arg);
    part2();
}

Now each part can free its parameter before exiting, if needed.

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
0

In principle you could install a handler with atexit that knows how to free your buffers. The handler will be called as a consequence of func1 calling exit. It's not very pleasant to use -- the handler takes no parameters, which means you need to use globals (or local static variables) to store the thing that needs to be freed. It can't be unregistered, which means you need to set those globals to null (or some other special value) to indicate that you've freed the resources yourself, but the handler will still be called. Normally you'd use the atexit handler as the "hook" from which to hang your own resource cleanup framework.

In practice, that's usually too much hassle for a few malloced buffers, because when the program exits a full-featured OS will in any case release all memory reserved by the process.

It can even be costly to free memory before exit - in order to free each allocation with free, the memory will be touched, which means it needs to be dragged into cache from main memory or even from swap. For a large number of small allocations that might take a while. When the OS does it for you, it just unmaps the memory map for the process and starts re-using that address space / memory / swap space for other things in future. There are benefits to cleaning up (for example, it makes your code easier to re-use and it makes real leaks easier to find), but also costs.

By the way, it's rather anti-social of the function func1 to call exit on error, because as you've discovered it places limits on users of the function. They can't recover even if they think their program can/should carry on despite func1 failing. func1 has in effect declared that it is too important for the program to even dream of continuing without its results. Yes, GMP, I do mean you.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
-2

One way of dealing with it:

void function() {
  int size = 100;
  char bar[size];
  char foo[size];

  // do stuff

  //compiler frees bar and foo for you
}
wroniasty
  • 7,884
  • 2
  • 32
  • 24
  • The compiler will most definitely, 100%ly **not** call `free` in this code. – Kerrek SB Sep 18 '12 at 14:55
  • Ok not call `free`, but will `free` the memory, no? – wroniasty Sep 18 '12 at 14:56
  • @KerrekSB so can you please tell me if the memory allocated this way will, or will not be released after the function exits? I'm just curiuos - I always thought it will allocate the space on stack, and release it afterwards. – wroniasty Sep 18 '12 at 15:02
  • In some way yes, after the program exits, the stack will be restored to the previous state, so the memory is effectively free. – Spidey Sep 18 '12 at 15:03
  • so is it not a way to deal with OP's problem? – wroniasty Sep 18 '12 at 15:04
  • Those variables are automatic, and their memory is managed automatically. It's exactly the same for `size`, and for some reason you were never worried about "freeing `size` for you", were you? – Kerrek SB Sep 18 '12 at 15:05
  • No I wasn't, and I thought that was the point of the question - a way not to worry about freeing memory. And they are not `automatic`, they are allocated on the stack. – wroniasty Sep 18 '12 at 15:06
  • @wroniasty, it's impossible to say. Perhaps OP would use this solution instead if the functions didn't require dynamically allocated memory. – eq- Sep 18 '12 at 15:06
  • @eq ok i see, he did say 'my *allocated* memory'. I stand corrected :) – wroniasty Sep 18 '12 at 15:08