memory allocation questions (newbie)

Discussion in 'C Programming' started by Stephen Ramsay, Jul 2, 2004.

  1. I'm wondering if someone can help me untangle some muddled thinking
    about memory allocation in C. I'm writing my first non-trivial app in
    C (and having a great time), but I'm finding it difficult to stamp out
    memory leaks.

    I'm sure most of what I'm about to say is wrong. I'd really be very
    grateful to know why.

    The use of malloc() in most of the books I've read make it sound pretty
    straightforward. If you allocate some memory (because the required
    amount of space isn't known until runtime), you have to free that space
    before the program ends.

    The standard examples go something like this: You need to allocate space
    for an array of ints with n elements. When the program knows the value
    of n, you can allocate the memory like so:

    int *p;

    p = malloc(n * sizeof(int));

    (I understand from lurking on this list that p = (int *) malloc(n *
    sizeof(int)) is deeply offensive to many C fans :)

    When I'm done with that array, I need to free it like so:

    free(p);

    My way of describing this may not be too precise, but I think I
    understand this, and I've been able to use it this way in my programs
    without incident.

    However, I quickly run into problems the minute things get even slightly
    more complex. For example:

    0. Let's suppose that I allocate space for p, but there are branches in
    the program that won't actually use p. Do I need to free the memory at
    every exit point? Or does the fact that this can happen indicate a bad
    design on my part?

    1. Let's suppose that I pass p into a function. If that function is the
    last one that will ever need p, then presumably I can (and should) free
    it. But what if p is also used elsewhere? At a certain point, it seems
    like I would have to write some kind of reference counter . . .

    2. On a similar note, when I pass a pointer to some allocated memory as
    an argument to a function, pass it back out, pass it in somewhere else,
    aren't I always dealing with the same memory block? Will it not suffice
    to release the memory after I'm done with all of this, or am I thinking
    about it incorrectly?

    3. Is there anything wrong with just freeing all allocated memory blocks
    at every exit point? I realize I might still have other sorts of
    problems, but wouldn't this prevent leakage?

    4. What happens if I attempt to free memory that has already been freed?

    My apologies if all of this seems too basic for this list. Perhaps
    someone knows of a book that deals with these sorts of things in detail?

    Steve

    --
    Stephen Ramsay
    Department of English
    University of Georgia
    web: http://cantor.english.uga.edu/
    Stephen Ramsay, Jul 2, 2004
    #1
    1. Advertising

  2. Stephen Ramsay

    Mike Wahler Guest

    "Stephen Ramsay" <> wrote in message
    news:filFc.2028$...
    > I'm wondering if someone can help me untangle some muddled thinking
    > about memory allocation in C. I'm writing my first non-trivial app in
    > C (and having a great time), but I'm finding it difficult to stamp out
    > memory leaks.
    >
    > I'm sure most of what I'm about to say is wrong. I'd really be very
    > grateful to know why.
    >
    > The use of malloc() in most of the books I've read make it sound pretty
    > straightforward.


    It is.

    > If you allocate some memory (because the required
    > amount of space isn't known until runtime), you have to free that space
    > before the program ends.


    Right. Some implementations and operating systems will do it for you
    but it's poor practice to depend upon that.

    > The standard examples go something like this: You need to allocate space
    > for an array of ints with n elements. When the program knows the value
    > of n, you can allocate the memory like so:
    >
    > int *p;
    >
    > p = malloc(n * sizeof(int));


    Right. However many (including myself) prefer:

    p = malloc(n * sizeof *p);

    This means that if the type of 'p' later changes, it still works.

    > (I understand from lurking on this list that p = (int *) malloc(n *
    > sizeof(int)) is deeply offensive to many C fans :)


    Right. :)

    > When I'm done with that array, I need to free it like so:
    >
    > free(p);


    Right.

    > My way of describing this may not be too precise,


    You did a good job.

    >but I think I
    > understand this, and I've been able to use it this way in my programs
    > without incident.
    >
    > However, I quickly run into problems the minute things get even slightly
    > more complex. For example:
    >
    > 0. Let's suppose that I allocate space for p,


    I assume you meant space for 'p' to point to. Space for 'p' itself
    is already allotted when you write:

    int *p;

    >but there are branches in
    > the program that won't actually use p. Do I need to free the memory at
    > every exit point?


    You should free it before the program terminates.

    >Or does the fact that this can happen indicate a bad
    > design on my part?


    Yes, I think having multiple exit points is not a good idea.
    (I suppose sometimes it might be unavoidable, but I strive
    to have only one exit point from any function, especially
    main().)

    Or are you terminating your program in ways other than returning
    from main(), e.g. calling 'exit()'. I'd only use that in
    extreme cases.

    > 1. Let's suppose that I pass p into a function. If that function is the
    > last one that will ever need p,


    How will you know that? Can you always predict in which order
    your functions are called and how many times they're called?
    IMO trying to do that is a recipe for insanity. :)

    > then presumably I can (and should) free
    > it.


    No, don't do it there. Do it in the same function where the memory
    was allocated. IOW consider whichever function did the allocation
    to 'own' that memory and take responsibility for freeing it.

    void func1(int *p)
    {
    /* do something with 'p' */
    }

    void func2(int *p)
    {
    /* do something with 'p' */
    }

    void func3(int *p)
    {
    /* do something with 'p' */
    }

    void foo(void)
    {
    int *p = malloc(100);
    func1();
    func2();
    func1();
    func3();
    free(p);
    }

    Often functions will be conditionally called, or called
    within a loop, or indirectly through other functions. No
    point in trying to manually keep track of a pointer being
    passed around among them.

    Often I'll do all my allocation 'up front' in 'main()', then free the
    memory just before 'main()' returns.

    > But what if p is also used elsewhere?


    Exactly.

    >At a certain point, it seems
    > like I would have to write some kind of reference counter . . .


    Don't make life complicated unnecessarily.
    See above.


    >
    > 2. On a similar note, when I pass a pointer to some allocated memory as
    > an argument to a function, pass it back out,


    By 'pass it back out', do you mean return its value?

    > pass it in somewhere else,
    > aren't I always dealing with the same memory block?


    Yes, unless one of the functions changes its value (which
    would require a 'pointer-to-pointer' parameter).

    >Will it not suffice
    > to release the memory after I'm done with all of this,


    If I understand you correctly, yes.

    >or am I thinking
    > about it incorrectly?


    It seems you think you need to do things the 'hard way'
    when you don't. :)

    >
    > 3. Is there anything wrong with just freeing all allocated memory blocks
    > at every exit point?


    No. But try to minimize your exit points, ideally having only
    one: the return from 'main()'.

    > I realize I might still have other sorts of
    > problems, but wouldn't this prevent leakage?


    The way I described above will minimize the possibility of
    leakage, since if the allocation and freeing are all done
    in the same place (function), it's easier to maintain.

    > 4. What happens if I attempt to free memory that has already been freed?


    Nasal demons (undefined behavior).

    Some folks like to use a preventative measure against this,
    by assigning NULL to any pointer immediately after freeing it,
    since 'free(NULL)' is a 'no-op' by definition. Of course others
    say this is just a prevention against sloppy coding in the first
    place. YMMV.

    > My apologies if all of this seems too basic for this list.


    Not at all. Your questions were intelligent and well presented.
    Novices are very welcome here. But you might want to also
    check out alt.comp.lang.learn.c-c++, which is specifically geared
    toward the novice. But since it discusses both C and C++, when
    posting there, be sure to indicate which language you're asking about.

    I suppose sooner or later you'll have occasion to use 'realloc()'.
    Be careful, and have fun! :)

    > Perhaps
    > someone knows of a book that deals with these sorts of things in detail?


    See the C FAQ (section 18) for book recommendations:
    http://www.eskimo.com/~scs/C-faq/top.html

    HTH,
    -Mike
    Mike Wahler, Jul 3, 2004
    #2
    1. Advertising

  3. On 2004-07-02, Mike Wahler <> wrote:
    > "Stephen Ramsay" <> wrote in message
    > news:filFc.2028$...
    >> I'm wondering if someone can help me untangle some muddled thinking
    >> about memory allocation in C. I'm writing my first non-trivial app in
    >> C (and having a great time), but I'm finding it difficult to stamp out
    >> memory leaks.
    >>
    >> I'm sure most of what I'm about to say is wrong. I'd really be very
    >> grateful to know why.
    >>
    >> The use of malloc() in most of the books I've read make it sound pretty
    >> straightforward.

    >
    > It is.


    First, let me thank you for this wonderful reply. I was half expecting
    a "read the faq" answer. Instead, I get a response that has really
    helped me get several important things straight. To wit . . .

    >> p = malloc(n * sizeof(int));

    >
    > Right. However many (including myself) prefer:
    >
    > p = malloc(n * sizeof *p);
    >
    > This means that if the type of 'p' later changes, it still works.


    Ahah. I've seen this and never really understood what it meant.

    Wait. I think I still don't. Now, if I understand correctly, "sizeof"
    is an operator that takes a type name as it operand. So by saying
    "sizeof *p," you're saying that you want the size to be equivalent to a
    pointer-to-int. I think this means something like "a pointer that is
    prepared to point to a memory location big enough to hold an int." In
    either case, though, the lvalue of the expression is still a
    pointer-to-int. I'm lost . . .

    >> 0. Let's suppose that I allocate space for p,

    >
    > I assume you meant space for 'p' to point to. Space for 'p' itself
    > is already allotted when you write:
    >
    > int *p;


    Yes, I keep eliding this distinction . . .

    >>but there are branches in
    >> the program that won't actually use p. Do I need to free the memory at
    >> every exit point?

    >
    > You should free it before the program terminates.
    >
    >>Or does the fact that this can happen indicate a bad
    >> design on my part?

    >
    > Yes, I think having multiple exit points is not a good idea.
    > (I suppose sometimes it might be unavoidable, but I strive
    > to have only one exit point from any function, especially
    > main().)
    >
    > Or are you terminating your program in ways other than returning
    > from main(), e.g. calling 'exit()'. I'd only use that in
    > extreme cases.


    The example I'm thinking of is less convoluted than this, but I've spent
    the last twenty minutes trying to think of an example and I think I've
    convinced myself that my problem is Not Really A Problem.

    > No, don't do it there. Do it in the same function where the memory
    > was allocated. IOW consider whichever function did the allocation
    > to 'own' that memory and take responsibility for freeing it.
    >
    > void func1(int *p)
    > {
    > /* do something with 'p' */
    > }
    >
    > void func2(int *p)
    > {
    > /* do something with 'p' */
    > }
    >
    > void func3(int *p)
    > {
    > /* do something with 'p' */
    > }
    >
    > void foo(void)
    > {
    > int *p = malloc(100);
    > func1();
    > func2();
    > func1();
    > func3();
    > free(p);
    > }


    Ah! Okay wait. I understand what you're saying. And if a funtion
    returns a pointer to the caller, it becomes the caller's responsibility,
    yes?

    > Often functions will be conditionally called, or called
    > within a loop, or indirectly through other functions. No
    > point in trying to manually keep track of a pointer being
    > passed around among them.
    >
    > Often I'll do all my allocation 'up front' in 'main()', then free the
    > memory just before 'main()' returns.


    Ah, that sounds so much easier than what I had been thinking about. But
    this would have to include any malloc'd storage returned from functions
    called by main()?

    > It seems you think you need to do things the 'hard way'
    > when you don't. :)
    >
    >> My apologies if all of this seems too basic for this list.

    >
    > Not at all. Your questions were intelligent and well presented.
    > Novices are very welcome here. But you might want to also
    > check out alt.comp.lang.learn.c-c++, which is specifically geared
    > toward the novice. But since it discusses both C and C++, when
    > posting there, be sure to indicate which language you're asking about.


    Thanks! I wasn't aware of the existence of this list. I'll post my
    newbie questions there from now on and go back to lurking on this one.

    > I suppose sooner or later you'll have occasion to use 'realloc()'.
    > Be careful, and have fun! :)


    Good grief :(

    I have to say, at the risk of starting a new thread, that I'm enjoying
    all of this C business immensely. C was my first language, but I never
    wrote anything serious in it because it was too hard, to confusing, too
    error-prone, etc. I've been programming for years (on UNIX systems)
    using Perl, Ruby, Java, and stuff like that. I like these languages a
    lot, but it's really wonderful to come back to C after years of
    experience writing programs in high-level languages. I have a much
    better understanding of how computers work (above confusions aside) and
    of software design, and now I find that the absolute freedom and
    expressiveness of C is a real breath of fresh air. I wish I were faster
    in it (I seem to spend a lot of time programming under seige with lint,
    valgrind, and gdb close by), but it's a lot of fun to work things out
    and try to get the most elegant solution possible. I wonder if many
    people have had the experience of coming "back to C" from
    ultra-high-level languages only to find that there's something really
    cool about programming closer to the metal.

    Thanks again,

    Steve

    --
    Stephen Ramsay
    Department of English
    University of Georgia
    web: http://cantor.english.uga.edu/
    Stephen Ramsay, Jul 3, 2004
    #3
  4. On Sat, 03 Jul 2004 00:59:44 +0000, Stephen Ramsay wrote:

    > First, let me thank you for this wonderful reply. I was half expecting
    > a "read the faq" answer. Instead, I get a response that has really
    > helped me get several important things straight. To wit . . .
    >
    >>> p = malloc(n * sizeof(int));

    >>
    >> Right. However many (including myself) prefer:
    >>
    >> p = malloc(n * sizeof *p);
    >>
    >> This means that if the type of 'p' later changes, it still works.

    >
    > Ahah. I've seen this and never really understood what it meant.
    >
    > Wait. I think I still don't. Now, if I understand correctly, "sizeof"
    > is an operator that takes a type name as it operand. So by saying
    > "sizeof *p," you're saying that you want the size to be equivalent to a
    > pointer-to-int. I think this means something like "a pointer that is

    No, you say that you want it to be the size of what p points to.

    > prepared to point to a memory location big enough to hold an int." In
    > either case, though, the lvalue of the expression is still a
    > pointer-to-int. I'm lost . . .
    >
    >>> 0. Let's suppose that I allocate space for p,

    >>
    >> I assume you meant space for 'p' to point to. Space for 'p' itself
    >> is already allotted when you write:
    >>
    >> int *p;

    >
    > Yes, I keep eliding this distinction . . .
    >
    >>>but there are branches in
    >>> the

    >> Often functions will be conditionally called, or called
    >> within a loop, or indirectly through other functions. No
    >> point in trying to manually keep track of a pointer being
    >> passed around among them.
    >>
    >> Often I'll do all my allocation 'up front' in 'main()', then free the
    >> memory just before 'main()' returns.

    >
    > Ah, that sounds so much easier than what I had been thinking about. But
    > this would have to include any malloc'd storage returned from functions
    > called by main()?

    Just as as side note, most OS's deallocate dynamically allocated
    memory on process termination, thus no need for freeing things when
    exiting.
    =?iso-8859-1?q?Nils_O=2E_Sel=E5sdal?=, Jul 3, 2004
    #4
  5. Stephen Ramsay

    Severian Guest

    On Sat, 03 Jul 2004 03:52:04 +0200, Nils O. Selåsdal <>
    wrote:

    <snip>

    >Just as as side note, most OS's deallocate dynamically allocated
    >memory on process termination, thus no need for freeing things when
    >exiting.


    Besides the obvious case of memory leaks in functions that are called
    repeatedly:

    I have often turned entire programs into functions, and having already
    taken care of memory deallocation is a time-saver, especially when
    scattered through multiple modules.

    --
    Sev
    Severian, Jul 3, 2004
    #5
  6. On Fri, 2 Jul 2004, Stephen Ramsay wrote:

    > I'm wondering if someone can help me untangle some muddled thinking
    > about memory allocation in C. I'm writing my first non-trivial app in
    > C (and having a great time), but I'm finding it difficult to stamp out
    > memory leaks.
    >
    > I'm sure most of what I'm about to say is wrong. I'd really be very
    > grateful to know why.
    >
    > The use of malloc() in most of the books I've read make it sound pretty
    > straightforward. If you allocate some memory (because the required
    > amount of space isn't known until runtime), you have to free that space
    > before the program ends.
    >
    > The standard examples go something like this: You need to allocate space
    > for an array of ints with n elements. When the program knows the value
    > of n, you can allocate the memory like so:
    >
    > int *p;
    >
    > p = malloc(n * sizeof(int));
    >
    > (I understand from lurking on this list that p = (int *) malloc(n *
    > sizeof(int)) is deeply offensive to many C fans :)
    >
    > When I'm done with that array, I need to free it like so:
    >
    > free(p);
    >
    > My way of describing this may not be too precise, but I think I
    > understand this, and I've been able to use it this way in my programs
    > without incident.


    Everything is good so far.

    > However, I quickly run into problems the minute things get even slightly
    > more complex. For example:
    >
    > 0. Let's suppose that I allocate space for p, but there are branches in
    > the program that won't actually use p. Do I need to free the memory at
    > every exit point? Or does the fact that this can happen indicate a bad
    > design on my part?


    Hmm, you mean something like:

    allocate memory

    if condition1
    free the memory
    exit

    some more code

    if condition2
    free the memory
    exit

    some more code

    if condition3
    free the memory
    exit

    some more code

    free the memory
    return 0;

    There are cleaner ways to handle this. You could get all the runtime
    information and allocate all your memory at once (or at least in large
    groups). For example, I might want to allocate an array of strings. I find
    at runtime I need 50 strings, each 100 characters long. I could call
    malloc 50 times for 100 char each or I could allocate one large block of
    5000 and set the 50 pointers to point into the single block of memory.

    How you design to reduce the number of malloc calls will differ for each
    application. Over time you should be able to figure things out. Or you
    could post exact code and ask how it could be written more elegantly.

    > 1. Let's suppose that I pass p into a function. If that function is the
    > last one that will ever need p, then presumably I can (and should) free
    > it. But what if p is also used elsewhere? At a certain point, it seems
    > like I would have to write some kind of reference counter . . .


    Different people set different policies for how to deal with this. The
    important thing is to be consistent. Generally speaking, free the memory
    just after the last place you will need to access it, worst case scenario.

    > 2. On a similar note, when I pass a pointer to some allocated memory as
    > an argument to a function, pass it back out, pass it in somewhere else,
    > aren't I always dealing with the same memory block? Will it not suffice
    > to release the memory after I'm done with all of this, or am I thinking
    > about it incorrectly?


    You are dealing with the same memory block. For example, I could allocate
    100 bytes in main(), pass the pointer to func1(), return to main(), pass
    the pointer to func2(), return to main(), etc. Finally, in main() I will
    free the memory and exit the program.

    > 3. Is there anything wrong with just freeing all allocated memory blocks
    > at every exit point? I realize I might still have other sorts of
    > problems, but wouldn't this prevent leakage?


    The more code you have to change the more risk you will make a mistake.
    You want to keep the code simple, elegant and easy to maintain. What if
    you create a program with 23 exit points and call free(p); at all of them.
    Later you change the design and no longer need p. Now you have to go back
    and remove all those calls to free(p);. Worse, what if you freed an alias
    for p in some function? Most of the elimination of p would cause the rest
    of the code to generate compiler errors but some might not be all that
    clear.

    > 4. What happens if I attempt to free memory that has already been freed?


    Undefined behaviour. The C standard indicates that free(NULL) has no
    action. I like to set up a macro so a call to free() is actually a call to
    free() then set the variable to NULL. This way if you call free again on
    the same variable it has no action.

    > My apologies if all of this seems too basic for this list. Perhaps
    > someone knows of a book that deals with these sorts of things in detail?
    >
    > Steve
    >
    > --
    > Stephen Ramsay
    > Department of English
    > University of Georgia
    > web: http://cantor.english.uga.edu/
    >


    --
    Send e-mail to: darrell at cs dot toronto dot edu
    Don't send e-mail to
    Darrell Grainger, Jul 3, 2004
    #6
  7. On Sat, 3 Jul 2004, Stephen Ramsay wrote:

    > On 2004-07-02, Mike Wahler <> wrote:
    > > "Stephen Ramsay" <> wrote in message
    > > news:filFc.2028$...
    > >> I'm wondering if someone can help me untangle some muddled thinking
    > >> about memory allocation in C. I'm writing my first non-trivial app in
    > >> C (and having a great time), but I'm finding it difficult to stamp out
    > >> memory leaks.
    > >>
    > >> I'm sure most of what I'm about to say is wrong. I'd really be very
    > >> grateful to know why.
    > >>
    > >> The use of malloc() in most of the books I've read make it sound pretty
    > >> straightforward.

    > >
    > > It is.

    >
    > First, let me thank you for this wonderful reply. I was half expecting
    > a "read the faq" answer. Instead, I get a response that has really
    > helped me get several important things straight. To wit . . .
    >
    > >> p = malloc(n * sizeof(int));

    > >
    > > Right. However many (including myself) prefer:
    > >
    > > p = malloc(n * sizeof *p);
    > >
    > > This means that if the type of 'p' later changes, it still works.

    >
    > Ahah. I've seen this and never really understood what it meant.
    >
    > Wait. I think I still don't. Now, if I understand correctly, "sizeof"
    > is an operator that takes a type name as it operand. So by saying
    > "sizeof *p," you're saying that you want the size to be equivalent to a
    > pointer-to-int. I think this means something like "a pointer that is
    > prepared to point to a memory location big enough to hold an int." In
    > either case, though, the lvalue of the expression is still a
    > pointer-to-int. I'm lost . . .


    Think about it this way:

    #include <stdio.h>
    #inclide <stdlib.h>

    int main(void)
    {
    int i = 5;
    int *p;

    if((p = malloc(sizeof int)) == NULL)
    exit(EXIT_FAILURE);

    *p = i;
    printf("%d\n", *p);

    free(p);
    return 0;

    Can you see that sizeof(*p), sizeof(i) and sizeof(int) are all the same
    size? Note that *p is an int and that p is a pointer to an int.

    > >> 0. Let's suppose that I allocate space for p,

    > >
    > > I assume you meant space for 'p' to point to. Space for 'p' itself
    > > is already allotted when you write:
    > >
    > > int *p;

    >
    > Yes, I keep eliding this distinction . . .
    >
    > >>but there are branches in
    > >> the program that won't actually use p. Do I need to free the memory at
    > >> every exit point?

    > >
    > > You should free it before the program terminates.
    > >
    > >>Or does the fact that this can happen indicate a bad
    > >> design on my part?

    > >
    > > Yes, I think having multiple exit points is not a good idea.
    > > (I suppose sometimes it might be unavoidable, but I strive
    > > to have only one exit point from any function, especially
    > > main().)
    > >
    > > Or are you terminating your program in ways other than returning
    > > from main(), e.g. calling 'exit()'. I'd only use that in
    > > extreme cases.

    >
    > The example I'm thinking of is less convoluted than this, but I've spent
    > the last twenty minutes trying to think of an example and I think I've
    > convinced myself that my problem is Not Really A Problem.
    >
    > > No, don't do it there. Do it in the same function where the memory
    > > was allocated. IOW consider whichever function did the allocation
    > > to 'own' that memory and take responsibility for freeing it.
    > >
    > > void func1(int *p)
    > > {
    > > /* do something with 'p' */
    > > }
    > >
    > > void func2(int *p)
    > > {
    > > /* do something with 'p' */
    > > }
    > >
    > > void func3(int *p)
    > > {
    > > /* do something with 'p' */
    > > }
    > >
    > > void foo(void)
    > > {
    > > int *p = malloc(100);
    > > func1();
    > > func2();
    > > func1();
    > > func3();
    > > free(p);
    > > }

    >
    > Ah! Okay wait. I understand what you're saying. And if a funtion
    > returns a pointer to the caller, it becomes the caller's responsibility,
    > yes?


    Yes, this is a good policy. If you call malloc() then you are responsible
    for freeing the memory. If you call a function that returns the pointer,
    e.g.

    int *somefunc(int size)
    {
    int *p = malloc(size * sizeof *p);
    return p;
    }

    then you need to free that result as well.

    > > Often functions will be conditionally called, or called
    > > within a loop, or indirectly through other functions. No
    > > point in trying to manually keep track of a pointer being
    > > passed around among them.
    > >
    > > Often I'll do all my allocation 'up front' in 'main()', then free the
    > > memory just before 'main()' returns.

    >
    > Ah, that sounds so much easier than what I had been thinking about. But
    > this would have to include any malloc'd storage returned from functions
    > called by main()?


    Yes.

    > > It seems you think you need to do things the 'hard way'
    > > when you don't. :)
    > >
    > >> My apologies if all of this seems too basic for this list.

    > >
    > > Not at all. Your questions were intelligent and well presented.
    > > Novices are very welcome here. But you might want to also
    > > check out alt.comp.lang.learn.c-c++, which is specifically geared
    > > toward the novice. But since it discusses both C and C++, when
    > > posting there, be sure to indicate which language you're asking about.

    >
    > Thanks! I wasn't aware of the existence of this list. I'll post my
    > newbie questions there from now on and go back to lurking on this one.
    >
    > > I suppose sooner or later you'll have occasion to use 'realloc()'.
    > > Be careful, and have fun! :)

    >
    > Good grief :(
    >
    > I have to say, at the risk of starting a new thread, that I'm enjoying
    > all of this C business immensely. C was my first language, but I never
    > wrote anything serious in it because it was too hard, to confusing, too
    > error-prone, etc. I've been programming for years (on UNIX systems)
    > using Perl, Ruby, Java, and stuff like that. I like these languages a
    > lot, but it's really wonderful to come back to C after years of
    > experience writing programs in high-level languages. I have a much
    > better understanding of how computers work (above confusions aside) and
    > of software design, and now I find that the absolute freedom and
    > expressiveness of C is a real breath of fresh air. I wish I were faster
    > in it (I seem to spend a lot of time programming under seige with lint,
    > valgrind, and gdb close by), but it's a lot of fun to work things out
    > and try to get the most elegant solution possible. I wonder if many
    > people have had the experience of coming "back to C" from
    > ultra-high-level languages only to find that there's something really
    > cool about programming closer to the metal.


    Does this mean you are ready to move to the assembly language newsgroups?
    8^)

    --
    Send e-mail to: darrell at cs dot toronto dot edu
    Don't send e-mail to
    Darrell Grainger, Jul 3, 2004
    #7
  8. On 2004-07-03, Darrell Grainger <> wrote:
    > Hmm, you mean something like:
    >
    > allocate memory
    >
    > if condition1
    > free the memory
    > exit
    >
    > some more code
    >
    > if condition2
    > free the memory
    > exit
    >
    > some more code
    >
    > if condition3
    > free the memory
    > exit
    >
    > some more code
    >
    > free the memory
    > return 0;


    Yes. This is exactly what I was envisioning.

    > There are cleaner ways to handle this. You could get all the runtime
    > information and allocate all your memory at once (or at least in large
    > groups). For example, I might want to allocate an array of strings. I find
    > at runtime I need 50 strings, each 100 characters long. I could call
    > malloc 50 times for 100 char each or I could allocate one large block of
    > 5000 and set the 50 pointers to point into the single block of memory.


    Whoa. Wait. Can you do that?

    Well, I'm sure you're not lying to me, but I've never encountered
    anything like this in any of the C books I've read. It sounds perilous
    (but then, surely quicker than running 50 function calls). I think I'll
    just have to go figure it out. ;)

    > How you design to reduce the number of malloc calls will differ for each
    > application. Over time you should be able to figure things out. Or you
    > could post exact code and ask how it could be written more elegantly.


    When I write some code that is not embarrassing to me, I will consider
    it an honor to have my betters critique its elegance.

    (I'm sure that would be incredibly enlightening, actually)

    >> 1. Let's suppose that I pass p into a function. If that function is the
    >> last one that will ever need p, then presumably I can (and should) free
    >> it. But what if p is also used elsewhere? At a certain point, it seems
    >> like I would have to write some kind of reference counter . . .

    >
    > Different people set different policies for how to deal with this. The
    > important thing is to be consistent. Generally speaking, free the memory
    > just after the last place you will need to access it, worst case scenario.


    This sounds like the closest we'll ever get to a heuristic. Memory
    management in C is starting to sound more and more like a series of
    design decisions based on experience that will differ from project to
    project.

    >> 2. On a similar note, when I pass a pointer to some allocated memory as
    >> an argument to a function, pass it back out, pass it in somewhere else,
    >> aren't I always dealing with the same memory block? Will it not suffice
    >> to release the memory after I'm done with all of this, or am I thinking
    >> about it incorrectly?

    >
    > You are dealing with the same memory block. For example, I could allocate
    > 100 bytes in main(), pass the pointer to func1(), return to main(), pass
    > the pointer to func2(), return to main(), etc. Finally, in main() I will
    > free the memory and exit the program.
    >
    >> 3. Is there anything wrong with just freeing all allocated memory blocks
    >> at every exit point? I realize I might still have other sorts of
    >> problems, but wouldn't this prevent leakage?

    >
    > The more code you have to change the more risk you will make a mistake.
    > You want to keep the code simple, elegant and easy to maintain. What if
    > you create a program with 23 exit points and call free(p); at all of them.
    > Later you change the design and no longer need p. Now you have to go back
    > and remove all those calls to free(p);. Worse, what if you freed an alias
    > for p in some function? Most of the elimination of p would cause the rest
    > of the code to generate compiler errors but some might not be all that
    > clear.


    Yes. I was getting myself into this situation when I posted my
    questions.


    Thanks for these comments. Very, very helpful.

    Steve


    --
    Stephen Ramsay
    Department of English
    University of Georgia
    web: http://cantor.english.uga.edu/
    Stephen Ramsay, Jul 3, 2004
    #8
  9. Stephen Ramsay

    /* frank */ Guest

    Mike Wahler ha scritto:

    >int *p;
    >p = malloc(n * sizeof(int));


    But if malloc returns a void* , how this assignment
    is legal (force p that's a int* to a return value that it's void*) ?

    A bit confused?

    Thanks
    /* frank */, Jul 3, 2004
    #9
  10. Stephen Ramsay

    Chris Torek Guest

    >On 2004-07-03, Darrell Grainger <> wrote:
    >> ... For example, I might want to allocate an array of strings. I find
    >> at runtime I need 50 strings, each 100 characters long. I could call
    >> malloc 50 times for 100 char each or I could allocate one large block of
    >> 5000 and set the 50 pointers to point into the single block of memory.


    In article <news:6VqFc.2714$>
    Stephen Ramsay <> writes:
    >Whoa. Wait. Can you do that?


    Certainly. As you note, it is probably more efficient. It has
    one obvious drawback though: since there was only one malloc(),
    there can be only one free(). It is no longer possible to create
    and destroy individual strings -- they come or go in this unit,
    the "50-string block".

    >> Different people set different policies for how to deal with

    [releasing memory once it is no longer required].

    >This sounds like the closest we'll ever get to a heuristic. Memory
    >management in C is starting to sound more and more like a series of
    >design decisions based on experience that will differ from project to
    >project.


    Indeed.

    Something I find quite helpful is the notion of the "owner" of any
    given allocated block. A large program is usually made up of many
    small parts, each with its own concerns; but in many cases, one
    particular bit of code might "own" a block of memory, and thus be
    responsible for allocating and freeing it. At some point, that
    code might "sell" the memory to some other sub-program, or at least
    "loan it out".

    This whole accounting scheme can break down when it is no longer
    clear who "owns" a block and who has it "borrowed". This is where
    garbage collecting languages win: the language keeps track of you
    who is using something, and once *nobody* is using it anymore, it
    just goes away automatically. But garbage-collectors have a price,
    and C does not make you pay it. If you want to implement your own
    (in this case a simple reference-counting gc might do the trick),
    you can; if you can get away with simple "one owner" accounting,
    you do not even have to pay that runtime cost.
    --
    In-Real-Life: Chris Torek, Wind River Systems
    Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
    email: forget about it http://web.torek.net/torek/index.html
    Reading email is like searching for food in the garbage, thanks to spammers.
    Chris Torek, Jul 3, 2004
    #10
  11. Stephen Ramsay

    Severian Guest

    On Sat, 03 Jul 2004 08:26:56 +0200, /* frank */
    <> wrote:

    >Mike Wahler ha scritto:
    >
    >>int *p;
    >>p = malloc(n * sizeof(int));

    >
    >But if malloc returns a void* , how this assignment
    >is legal (force p that's a int* to a return value that it's void*) ?
    >
    >A bit confused?


    By definition of the C standard, pointer-to-void can be converted to
    pointer-to-anything (and vice versa). It makes perfect sense, and I'm
    sure someone will chime in with chapter & verse.

    --
    Sev
    Severian, Jul 3, 2004
    #11
  12. Stephen Ramsay

    /* frank */ Guest

    Severian ha scritto:


    > By definition of the C standard, pointer-to-void can be converted to
    > pointer-to-anything (and vice versa). It makes perfect sense, and I'm
    > sure someone will chime in with chapter & verse.


    Then, since malloc returns a void*,
    it should be done a cast.

    int* p;
    p = (int*) (malloc ( n * sizeof (int)));


    Is it?
    /* frank */, Jul 3, 2004
    #12
  13. "/* frank */" <> wrote in message
    news:...
    > Severian ha scritto:
    > > By definition of the C standard, pointer-to-void can be converted to
    > > pointer-to-anything (and vice versa).


    A pointer to void can be implicitly converted to just about any other pointer (and vice
    versa).

    > > It makes perfect sense,


    The decision to do this was quite deliberate. As to the sense of it... I won't go there.

    > > and I'm
    > > sure someone will chime in with chapter & verse.


    6.3

    > Then, since malloc returns a void*,
    > it should be done a cast.


    No. The implicit conversion of void* pointers was made to _avoid_ casts.

    > int* p;
    > p = (int*) (malloc ( n * sizeof (int)));
    >
    > Is it?


    The idiom is precisely...

    p = malloc(n * sizeof *p);

    The cast is only required if you desire the code to be compilable as C++.

    --
    Peter
    Peter Nilsson, Jul 3, 2004
    #13
  14. Stephen Ramsay

    Malcolm Guest

    "Stephen Ramsay" <> wrote in message
    >
    > The use of malloc() in most of the books I've read make it sound pretty
    > straightforward. If you allocate some memory (because the required
    > amount of space isn't known until runtime), you have to free that space
    > before the program ends.
    >

    The basics are simple. malloc() grabs a block of memory from somewhere and
    gives you exclusive use of it. free() puts it back for re-use.
    >
    > 0. Let's suppose that I allocate space for p, but there are branches in
    > the program that won't actually use p. Do I need to free the memory at
    > every exit point? Or does the fact that this can happen indicate a bad
    > design on my part?
    >

    Bad design. Usually each call to malloc() should be matched by one and only
    one call to free().
    >
    > 1. Let's suppose that I pass p into a function. If that function is the
    > last one that will ever need p, then presumably I can (and should) free
    > it. But what if p is also used elsewhere? At a certain point, it seems
    > like I would have to write some kind of reference counter . . .
    >

    Usually you solve this by freeing in the same block that you allocate in.
    void foo(void)
    {
    char *ptr = malloc(N);

    strcpy(ptr, "a string");

    bar(ptr);

    free(ptr);
    }

    void bar(char *ptr)
    {
    if(!strcmp(ptr, "That's all folks"))
    {
    bleep();
    /* bad design here, free in foo */
    free(ptr);
    }

    }
    >
    > 2. On a similar note, when I pass a pointer to some allocated memory
    > as an argument to a function, pass it back out, pass it in somewhere
    > else, aren't I always dealing with the same memory block? Will it not
    > sufficeto release the memory after I'm done with all of this, or am I
    > thinking about it incorrectly?
    >

    Yes. In C pointers pass about the address of the same physical block of
    memory. However usually a function that receives a pointer to a data item
    doesn't need to know whether it is allocated with malloc() or not.
    >
    > 3. Is there anything wrong with just freeing all allocated memory blocks
    > at every exit point? I realize I might still have other sorts of
    > problems, but wouldn't this prevent leakage?
    >

    It causes huge problems, because you can free memory twice, and you've got
    to maintain every path. Sometimes you do need to have more than one matching
    free() to each call to malloc(), but only rarely.

    Lets take this example

    struct tree
    {
    struct tree *child1;
    struct tree *child2;
    char data[32];
    };

    void treefunction(void)
    {
    struct tree *root = malloc(sizeof(struct tree));
    root->child1 = 0;
    root->child2 = 0;
    strcpy(root->data, "root node");

    buildthetree( root );

    /* what do we do here ? */
    }

    void buildthetree(struct tree *root)
    {
    /* huge function that adds nodes to the tree, allocated with malloc() */
    }

    The answer is that we write a function

    void deletetree(struct tree *root)
    {
    if(root->child1)
    deletetree(root->child1);
    if(root->child2)
    deletetree(root-<child2);

    free(root);
    }

    Now we call it from the function treefunction() to clean up.

    We haven't quite managed to keep to the ideal of a call to malloc() followed
    by a matching call to free() in the smae block, but we've come pretty close.
    As long as the tree is built correctly the whole thing is controlled and the
    chance of a memory leak slipping in is small.
    >
    > 4. What happens if I attempt to free memory that has already been
    > freed?
    >

    It is undefined behaviour, which means anything can happen. Most likely the
    computer would segfault or issue a "block corrupted" type warning. However
    you could corrupt some vital data and send Mrs Bloggs an electricity bill
    for 6 thousand million dollars.
    >
    > My apologies if all of this seems too basic for this list.
    >

    It's a very good question.
    Malcolm, Jul 3, 2004
    #14
  15. Stephen Ramsay

    Malcolm Guest

    "Peter Nilsson" <> wrote in message
    >
    > No. The implicit conversion of void* pointers was made to _avoid_
    > casts.
    >

    According to Plauger it was a hangover from the looser days of K and R C,
    when there was no such thing as a void *, and retained to avoid breaking old
    code.
    >
    > The cast is only required if you desire the code to be compilable as
    > C++.
    >

    That's the main reason for keeping it in. Also to make it readable as C++
    for someone who switches a lot between both languages.
    Malcolm, Jul 3, 2004
    #15
  16. Stephen Ramsay

    Mac Guest

    On Sat, 03 Jul 2004 03:52:04 +0200, Nils O. Selåsdal wrote:

    [snip]

    > Just as as side note, most OS's deallocate dynamically allocated
    > memory on process termination, thus no need for freeing things when
    > exiting.


    Right. You just want to make sure that you don't do something like this:

    p=malloc(n * sizeof *p);

    /* code which does not free p or assign its value to another variable */

    p=malloc(m * sizeof *p);

    That is a memory leak.

    Mac
    Mac, Jul 3, 2004
    #16
  17. On Sat, 3 Jul 2004 06:26:56 UTC, /* frank */ <>
    wrote:

    > Mike Wahler ha scritto:
    >
    > >int *p;
    > >p = malloc(n * sizeof(int));

    >
    > But if malloc returns a void* , how this assignment
    > is legal (force p that's a int* to a return value that it's void*) ?


    malloc is defined as a fuction returning a pointer to void. The C
    standard says clearly that you can assign easy the value of a pointer
    to void to a pointer of each type you needs really and you can even
    use a pointer of each type in place when a pointer to void is
    required. It is the C runtime that makes the conversion for you under
    cover.

    Each type above means clearly each type of data - not function as
    function ponters can never converted to data pointers or the other way
    around.

    --
    Tschau/Bye
    Herbert

    Visit http://www.ecomstation.de the home of german eComStation
    Herbert Rosenau, Jul 4, 2004
    #17
  18. On Sat, 3 Jul 2004 08:35:31 UTC, /* frank */ <>
    wrote:

    > Severian ha scritto:
    >
    >
    > > By definition of the C standard, pointer-to-void can be converted to
    > > pointer-to-anything (and vice versa). It makes perfect sense, and I'm
    > > sure someone will chime in with chapter & verse.

    >
    > Then, since malloc returns a void*,
    > it should be done a cast.
    >
    > int* p;
    > p = (int*) (malloc ( n * sizeof (int)));
    >
    >
    > Is it?


    Never, really never. This will hide the most common mistake one can
    ever do! Without a prototype in sight you will convert garbidge to to
    garbidge when you tries to convert an int (that is what C assumes
    malloc returns without the prototype in sight) to a pointer.

    You knows: C says implicite a fuction without prototype in sight is a
    fuction returning int. Many implementations use different locations
    for return an int and return an pointer. So you cast fails to convert
    something not set to something else, ignoring the return value from
    malloc completely or at least partially.

    NEVER, rreally NEVER use the cast operator (except you has double
    checked the double check that you really KNOWS what you are doing.
    castinng a pointer to void to anything else is ALWAYS a big mistake!
    Whenever the compiler whines it whines because you are dumb enough to
    use the result of malloc (or any other function that returns void*
    without a prototype in sight. Correct the error using a unprototyped
    function instead to make things with one hand. Use your head and
    include the right header.


    --
    Tschau/Bye
    Herbert

    Visit http://www.ecomstation.de the home of german eComStation
    Herbert Rosenau, Jul 4, 2004
    #18
  19. On Fri, 2 Jul 2004 22:26:19 UTC, Stephen Ramsay <>
    wrote:

    > I'm wondering if someone can help me untangle some muddled thinking
    > about memory allocation in C. I'm writing my first non-trivial app in
    > C (and having a great time), but I'm finding it difficult to stamp out
    > memory leaks.
    >
    > I'm sure most of what I'm about to say is wrong. I'd really be very
    > grateful to know why.
    >
    > The use of malloc() in most of the books I've read make it sound pretty
    > straightforward. If you allocate some memory (because the required
    > amount of space isn't known until runtime), you have to free that space
    > before the program ends.


    Yes, that it's.

    > The standard examples go something like this: You need to allocate space
    > for an array of ints with n elements. When the program knows the value
    > of n, you can allocate the memory like so:
    >
    > int *p;
    >
    > p = malloc(n * sizeof(int));
    >
    > (I understand from lurking on this list that p = (int *) malloc(n *
    > sizeof(int)) is deeply offensive to many C fans :)
    >
    > When I'm done with that array, I need to free it like so:
    >
    > free(p);


    correct.

    >
    > My way of describing this may not be too precise, but I think I
    > understand this, and I've been able to use it this way in my programs
    > without incident.
    >
    > However, I quickly run into problems the minute things get even slightly
    > more complex. For example:
    >
    > 0. Let's suppose that I allocate space for p, but there are branches in
    > the program that won't actually use p. Do I need to free the memory at
    > every exit point? Or does the fact that this can happen indicate a bad
    > design on my part?


    Doesn't matter. You should allocate dynamic mememory at a point when
    you knows you have to use it. Do so on the latest possible logical
    point.
    - initialise each pointer at creation time - in worst case
    with NULL - when you have to create it some steps befor you
    use it yet. This saves you weeks of debug time!
    - allocate memory for a piece of data. That means one list member,
    the whole array or whatever you needs now to work on.
    - Initialise it; epically each pointer in it to have all pointer
    alwways
    in a clean, intitialised state (unused pointer shoulb be clearly
    NULL,
    not with random content!
    - use it
    - free it so soon as possible because your program is not the only one
    that uses lots of memory. It may give unused memory free for use in
    other applications.
    - free it so soon as possible because your program may need
    another bigger chunk immediately after the action is done.
    It helps houseceeping memory accessible on the system and in your
    app.
    - assign NULL to each pointer and its sibilings when you have free'd
    the
    memory it points to (superflous only when the pointer itself will
    go out of usage and visibility immediately after the free().
    That means you allocs memory to a automatic pointer at startup of a
    function
    and frees it when the function goes straight to return to its caller
    For pointers at file or extern scope you should ALWAYS assign NULL
    to it
    to have clean setup an unused pointer. So the program will crash
    definitely
    whenever it accesses it at an unwanted time. You can so even call
    free()
    multiple times without any problem because free(NULL) does
    definitely nothing
    whereas free(pointer to already free'd memory) invokes undefined
    behavior.



    > 1. Let's suppose that I pass p into a function. If that function is the
    > last one that will ever need p, then presumably I can (and should) free
    > it. But what if p is also used elsewhere? At a certain point, it seems
    > like I would have to write some kind of reference counter . . .


    Who says that this fuction is definitely the last one? Yeah, You may
    still use a helper fuction to allocate memory for the action you are
    starting up - and another helper that cleans the action up.

    But whatever you do be sure that the logic your program is in any way:
    - startup an action (open resources needed by that action)
    when not all resources needed accessible: cleanup and exit action or
    whole process
    - work on the action
    On error underway: don't forget to cleanup the resources
    before you breaks either the action or the whole process.
    - cleanup the action (close resources)

    > 2. On a similar note, when I pass a pointer to some allocated memory as
    > an argument to a function, pass it back out, pass it in somewhere else,
    > aren't I always dealing with the same memory block? Will it not suffice
    > to release the memory after I'm done with all of this, or am I thinking
    > about it incorrectly?


    Depends. Whenever you needs a resoure like files or memory:
    - get it ready for use
    - use it
    - close/free it
    - look ehat is to do next. (maybe you starts to use another set of
    memory, files,...)

    > 3. Is there anything wrong with just freeing all allocated memory blocks
    > at every exit point? I realize I might still have other sorts of
    > problems, but wouldn't this prevent leakage?


    No. But it is ever better to free it immediately after it is truly
    clear that there is no need for anymore. Whenever you starts again to
    use it you starts from scratch - even when your program runs only
    again through the same action.

    > 4. What happens if I attempt to free memory that has already been freed?


    You starts to go into undefined behavior like you does when you starts
    to access it further. Anything can occure like:
    - demons flying out of your nose
    - the 3. world war starts up
    - your home explodes
    - your emplolyer will cash back the last 12 month salary from your
    bank
    - something else

    Avoiding it is easy: assign NULL to the pointer you've free()d. So
    even when you frees it again nothing happens. Or when you access it
    now mistakenly the program will crash immediately. So debugging this
    mistake gets easy.

    Hint: to handle dynamic memory you should always test malloc() for
    success! malloc returns NULL when it fails to deliver the requested
    amount of memory.

    Hint: sometimes it is more easy to use realloc than malloc() + copy
    something + free the old location. But realloc() likes to fail like
    malloc(). Assigning the result from realloc() to the same pointer you
    give realloc for size change ends up in memory leak! Use a trmprary
    pointer instead to store the result. Whenever realloc says that it has
    resezed the mempory override the old pointer with the new one. When
    realloc returns NULL the old memory (and the pointer points to) left
    unchanged. It lefts then to you to do something with it and free() it
    when anything is done.

    Hint: It is mostenly a bad idea to exit() the program only because
    malloc/realloc() fails! You have another resources open that needs a
    defined cleanup (like write buffers, close files, free other
    memory.....). Often you can recover easy from that error. Let the
    higher level of your code decide what is to do in that case.

    --
    Tschau/Bye
    Herbert

    Visit http://www.ecomstation.de the home of german eComStation
    Herbert Rosenau, Jul 4, 2004
    #19
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Eirik WS

    Memory allocation questions

    Eirik WS, Feb 7, 2004, in forum: C Programming
    Replies:
    3
    Views:
    405
    Mike Wahler
    Feb 8, 2004
  2. s.subbarayan

    Dynamic memory allocation and memory leak...

    s.subbarayan, Mar 18, 2005, in forum: C Programming
    Replies:
    10
    Views:
    682
    Eric Sosman
    Mar 22, 2005
  3. Ken
    Replies:
    24
    Views:
    3,842
    Ben Bacarisse
    Nov 30, 2006
  4. chris
    Replies:
    6
    Views:
    974
    chris
    Oct 28, 2005
  5. Bjarke Hammersholt Roune
    Replies:
    14
    Views:
    1,171
    Bjarke Hammersholt Roune
    Mar 6, 2011
Loading...

Share This Page