Best way to handle memory allocation in C?

后端 未结 12 1737
春和景丽
春和景丽 2021-01-31 11:58

I think I\'ve got a good grasp on how to handle memory in C++ but doing it in C is different I\'m a bit off.

In C++ I\'ve got constructors and destructors, I\'ve got the

相关标签:
12条回答
  • 2021-01-31 12:15

    Part of the confusion is that it is inherently more difficult in C. malloc and free are similar to new and delete: malloc allocates new memory, and returns a pointer to that memory. free makes that memory available again, so long as it's memory that was allocated using malloc. Otherwise, it just makes hash of some chunk of memory. It doesn't care.

    The important thing with malloc/free is to decide on and consistently maintain a disciplined use. Here are some hints:

    ALWAYS check the returned pointer from malloc for NULL

    if((p = (char *) malloc(BUFSIZ)) == NULL {
       /* then malloc failed do some error processing. */
    }
    

    For belt and suspenders safety, set a pointer to NULL after freeing it.

    free(p);
    p = NULL ;
    

    try to malloc and free a chunk of memory within the same scope if possible:

     {  char * p ;
       if((p = malloc(BUFSIZ)) == NULL {
           /* then malloc failed do some error processing. */
       }
    
     /* do your work. */
    
       /* now you're done, free the memory */
    
       free(p);
       p = NULL ;  /* belt-and suspenders */
     }
    

    When you can't, make it clear that what you're returning is malloc'ed memory, so the caller can free it.

     /* foo: do something good, returning ptr to malloc memory */
     char * foo(int bar) {
         return (char *) malloc(bar);
     }
    
    0 讨论(0)
  • 2021-01-31 12:18

    There's a lot you can do to make your life easier. You already seems to have hit on the idea of creating factories/constructors for your C-objects. That's a good start follow up on it.

    Some other ideas to consider.

    1. don't settle for the standard malloc/free. go looking for a better one that's been opensourced or write one that suites the memory use of the objects that you are creating. also, we're talking C here, you're going to overwrite your objects free more and once and forget to free some, so build some debugging support into your malloc. writing your own is not hard if you cannot find one that meets your needs.

    2. use more than one heap. use one per class of object you create, use temporary heaps if you know you you are going to have a large number of transient objects that are related, this keeps memory fragmentation down and allows you to manage memory according to use.

    3. look at strategies like Objective-C's pools

    4. if you think you understand how C++ works, then adding constructor behavior to memory allocation in an object factory is not so hard to do and using a custom built free can then provide you the capability to call a destructor on the object being free'd giving you back some of the C++ behavior you liked

    0 讨论(0)
  • 2021-01-31 12:19

    While writing this I've realized this is more a question about me understanding the flow of C than anything else, but one question at a time.

    I honestly think you should read up on K&R if you haven't.

    0 讨论(0)
  • 2021-01-31 12:19

    I'm not quite sure what you're asking, but C is pretty straightforward:

    struct Foo *f0 = malloc(sizeof(*f));   // alloc uninitialized Foo struct
    struct Foo *f1 = calloc(1,sizeof(*f)); // alloc Foo struct cleared to all zeroes
    
    //You usually either want to clear your structs using calloc on allocation, or memset. If 
    // you need a constructor, just write a function:
    Foo *Foo_Create(int a, char *b)
    {
       Foo *r = calloc(1,sizeof(*r));
       r->a = a;
       r->b = strdup(b);
       return r;
    }
    
    Here is a simple C workflow with arrays:
    struct Foo **foos = NULL;
    int n_foos = 0;
    ...
    for(i = 0; i < n_foos; ++i)
    {
       struct Foo *f = calloc(1,sizeof(*f));
       foos = realloc(foos,sizeof(*foos)*++n_foos); // foos before and after may be different
       foos[n_foos-1] = f;
    }
    

    If you get fancy, you can write macros to help:

    #define MALLOCP(P) calloc(1,sizeof(*P)) // calloc inits alloc'd mem to zero
    

    A couple of points:

    • malloc, calloc, realloc, etc. all use free(), so managing these things is easy. Just be consistant.
    • performance for mallocs can be slow. Someone posted a link on this above. These days fast multi-threaded allocations is key, see tcmalloc et al. You probably don't have to worry about this.
    • on a modern virtual memory architecture, malloc will almost never fail unless you are out of virtual address space. If this happens, switch to 64bit ;)
    • Make sure you're using a system that has bounds checking, wiped free values, leak tracking and all that good stuff (see valgrind, win32 debug heap, etc.)
    0 讨论(0)
  • 2021-01-31 12:25

    One way I "hide" the memory allocation and de-allocation is to pass it off to custom containers. Pass the container an un-malloced object. Let it worry about malloc and when I remove the object let it worry about the free. Of course this only works if you are only storing an object in one container. If I have object references all over the place I will create the equivalent of constructor and destructor methods with c syntax:

     glob* newGlob(); 
     void freeGlob(glob* g);
    

    (by object I mean anything you would point to - not c++ objects).

    0 讨论(0)
  • 2021-01-31 12:27

    Unfortunately there are limited strategies for automating memory allocation and deallocation in C. The C++ compiler generates a lot of code behind the scenes for you -- it keeps track of each variable on the stack and makes sure that the appropriate destructor is called when the stack is cleaned up. This is actually a fairly sophisticated type of code generation, especially when you throw exceptions into the mix.

    C on the other hand is much simpler, which is why it's sometimes called "high level assembly language". C doesn't have any mechanism to guarantee that a particular bit of code is called when a function exits or a variable is popped off the stack, so it's up to you to keep track of each bit of memory you allocate and every file or network socket you open and clean them up at the appropriate point. There's no practical way to build an automatic smart pointer in C.

    One concept you should look at is "memory pools". Basically, rather that try to keep track of every individual block of memory you allocate, you create a pool, do some chunk of work, placing every memory block you allocate into the pool, then free the whole pool when you're done. You trade off a little bit of performance and control here in order to ease the cognitive load on the programmer, but most of the time it's well worth it.

    You should take a peek at the Apache Portable Runtime project. They have a memory pool library (docs at http://apr.apache.org/docs/apr/1.3/group__apr__pools.html ). If APR is too much for you to dive into, you can implement a very simple memory pool using three functions and a linked list data structure. Pseudocode would be something like:

    struct Pool {
      void* memoryBlock;
      struct Pool *next;
    }
    
    struct Pool *createPool(void) {
      /* allocate a Pool and return it */
    }
    
    void addToPool(struct Pool *pool, void *memoryBlock) {
      /* create a new Pool node and push it onto the list */
    }
    
    void destroyPool(struct Pool *pool) {
      /* walk the list, free each memory block then free its node */
    }
    

    Using the pool is something like this:

    int main(void) {
      struct Pool *pool = createPool();
      /* pool is empty */
    
      doSomething(pool);
    
      /* pool full of crap, clean it up and make a new one */
      destroyPool(pool);
      pool = createPool();
      /* new pool is empty */
    
      doMoreStuff(pool);
      destroyPool(pool);
    
      return 0;
    }
    
    0 讨论(0)
提交回复
热议问题