• Re: OT: Possible memory leak in an exercise of a C handbook

    From Michael =?utf-8?B?S2rDtnJsaW5n?=@21:1/5 to All on Mon Dec 16 16:50:01 2024
    On 16 Dec 2024 16:05 +0100, from martellif67@gmail.com (Franco Martelli):
    Is there a memory leak? What it sounds strange to me is that Valgrind reports: "total heap usage: 9 allocs, 8 frees, …" when for me the calls to "malloc" should be 8, not 9.

    Put in something to count the number of calls to malloc() and free() respectively. Don't forget calls outside of loops.

    This can be as simple as a `printf("+");` and `printf("-");`
    respectively at each call site. Or put breakpoints at each (or at the respective function entry point) and keep a manual count.

    See how many times each is reached.

    --
    Michael Kjörling
    🔗 https://michael.kjorling.se

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Greg Wooledge@21:1/5 to Franco Martelli on Mon Dec 16 17:00:02 2024
    On Mon, Dec 16, 2024 at 16:05:26 +0100, Franco Martelli wrote:
    void add_element( unsigned int i )
    {
    DIGIT *p;
    /* If the first element (the head) has not been
    * created, create it now.
    */
    if ( head == NULL )
    {
    head = last = (DIGIT *) malloc( sizeof ( DIGIT ) );
    head->dgt = last->dgt = i;
    head->next = head->prev = last->next = last->prev = NULL;
    return;
    }
    /* Otherwise, find the last element in the list */
    for (p = head; p->next != NULL; p = p->next)
    ; /* null statement */

    next = (DIGIT *) malloc( sizeof ( DIGIT ) );
    next->prev = p;
    next->dgt = i;
    next->next = NULL;
    last = p->next;
    }

    If you're already keeping a "last" pointer which points to the end of
    the linked list, you don't need that for loop to search for the end of
    the list every time.

    That's got nothing to do with memory leaks. Just an observation.

    void dealloc()
    {
    for ( const DIGIT *p = head; p->next != NULL; p = p->next )
    if ( p->prev != NULL )
    free( p->prev );
    free( last );
    }

    I think you might have an off-by-one error in this function. You stop
    the for loop when p->next == NULL, which means you never enter the body
    during the time when p == last. Which means you never free the
    second-to-last element in the list.

    Given a list of 5 items, you will free items 1, 2, 3 (during the loop)
    and 5 (after the loop), but not 4.

    Unless I've misread something. You should definitely double-check me.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Greg Wooledge@21:1/5 to Franco Martelli on Mon Dec 16 18:00:02 2024
    On Mon, Dec 16, 2024 at 17:34:36 +0100, Franco Martelli wrote:
    void dealloc()
    {
    for ( const DIGIT *p = head; p->next != NULL; p = p->next )
    if ( p->prev != NULL )
    free( p->prev );
    free( last );
    }

    I think you might have an off-by-one error in this function. You stop
    the for loop when p->next == NULL, which means you never enter the body during the time when p == last. Which means you never free the second-to-last element in the list.

    It is p->prev not p->next, by doing so free() don't apply for the "last" element so I've to free apart


    Given a list of 5 items, you will free items 1, 2, 3 (during the loop)
    and 5 (after the loop), but not 4.

    OK I'll try some test removing the "if statement"

    No, it's not the if statement. It's the for loop's iteration condition.

    for ( const DIGIT *p = head; p->next != NULL; p = p->next )

    Your iteration condition is p->next != NULL so you stop iterating as
    soon as p->next == NULL.

    Given this linked list:

    NULL <- [1] <-> [2] <-> [3] <-> [4] <-> [5] -> NULL
    ^ ^
    head last

    Your first loop iteration, when p == head, does nothing because of
    the if statement. That's fine.

    The second iteration, when p points to [2], frees [1].

    The third iteration, when p points to [3], frees [2].

    The fourth iteration, when p points to [4], frees [3].

    The fifth iteration, when p points to [5], NEVER HAPPENS, because
    next == NULL at that point, and the for loop stops. So [4] is
    not freed.

    After the loop, you free [5] which is where last points.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Charles Curley@21:1/5 to Franco Martelli on Mon Dec 16 19:40:01 2024
    On Mon, 16 Dec 2024 16:05:26 +0100
    Franco Martelli <martellif67@gmail.com> wrote:

    I'm doing the exercises of a C language handbook.

    By all means do the exercises in your handbook as a learning
    experience. After that, I have found very useful Roger Sessions,
    Reusable Data Structures For C, Prentice Hall (1989).

    --
    Does anybody read signatures any more?

    https://charlescurley.com
    https://charlescurley.com/blog/

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Michael =?utf-8?B?S2rDtnJsaW5n?=@21:1/5 to All on Mon Dec 16 20:50:01 2024
    On 16 Dec 2024 17:21 +0100, from martellif67@gmail.com (Franco Martelli):
    Put in something to count the number of calls to malloc() and free()
    respectively. Don't forget calls outside of loops.

    There isn't calls to malloc() or free() outside loops. What do you mean?

    From a quick re-glance through your code...

    if ( head == NULL )
    {
    head = last = (DIGIT *) malloc( sizeof ( DIGIT ) );
    head->dgt = last->dgt = i;
    head->next = head->prev = last->next = last->prev = NULL;
    return;
    }
    /* Otherwise, find the last element in the list */
    for (p = head; p->next != NULL; p = p->next)
    ; /* null statement */

    p->next = (DIGIT *) malloc( sizeof ( DIGIT ) );

    and

    for ( const DIGIT *p = head; p->next != NULL; p = p->next )
    if ( p->prev != NULL )
    free( p->prev );
    free( last );

    certainly look to me like they're outside of loops.

    --
    Michael Kjörling
    🔗 https://michael.kjorling.se

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From songbird@21:1/5 to Franco Martelli on Tue Dec 17 05:00:01 2024
    Franco Martelli wrote:
    ...
    I'd prefer a mailing-list instead, once finished all the exercises, I'd
    like to looking for somebody that he has my same handbook and to ask him
    for exchange the exercises for comparison purpose.
    Does anybody know a mailing-list for C language questions?

    comp.lang.c which is not a mailing list but instead a
    usenet group (which is much better than a mailing list
    for such types of conversations).


    songbird

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anssi Saari@21:1/5 to Franco Martelli on Tue Dec 17 12:30:01 2024
    Franco Martelli <martellif67@gmail.com> writes:

    I'd prefer a mailing-list instead, once finished all the exercises,
    I'd like to looking for somebody that he has my same handbook and to
    ask him for exchange the exercises for comparison purpose.

    Just curious, which handbook is it?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Greg Wooledge@21:1/5 to Jeffrey Walton on Tue Dec 17 22:20:02 2024
    On Tue, Dec 17, 2024 at 16:09:09 -0500, Jeffrey Walton wrote:
    I would rewrite the cleanup code like so:

    void dealloc()
    {
    DIGIT *next, *p = head;
    while( p )
    next = p->next, free( p ), p = next;
    }

    The logic looks good, but I'm not a fan of this use of the comma operator.
    Is that a fashionable thing nowadays?

    Especially when teaching a new programmer the ropes, I would stick with
    the regular syntax.

    void dealloc() {
    DIGIT *next, *p = head;
    while (p) {
    next = p->next;
    free(p);
    p = next;
    }
    }

    You can put the opening { on the next line if you prefer that way. I
    know some people have very strong feeling about that.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Jean-Fran=C3=A7ois_Bachel@21:1/5 to All on Wed Dec 18 05:50:01 2024
    Hello :)

    Le 17/12/2024 à 12:20, Anssi Saari a écrit :
    Franco Martelli <martellif67@gmail.com> writes:

    I'd prefer a mailing-list instead, once finished all the exercises,
    I'd like to looking for somebody that he has my same handbook and to
    ask him for exchange the exercises for comparison purpose.

    Just curious, which handbook is it?
    Good question ! makes me curious too :)

    Jeff

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From tomas@tuxteam.de@21:1/5 to Greg Wooledge on Wed Dec 18 06:10:01 2024
    On Tue, Dec 17, 2024 at 04:18:17PM -0500, Greg Wooledge wrote:
    On Tue, Dec 17, 2024 at 16:09:09 -0500, Jeffrey Walton wrote:
    I would rewrite the cleanup code like so:

    void dealloc()
    {
    DIGIT *next, *p = head;
    while( p )
    next = p->next, free( p ), p = next;
    }

    The logic looks good, but I'm not a fan of this use of the comma operator.
    Is that a fashionable thing nowadays?

    Especially when teaching a new programmer the ropes, I would stick with
    the regular syntax.

    void dealloc() {
    DIGIT *next, *p = head;
    while (p) {
    next = p->next;
    free(p);
    p = next;
    }
    }

    You can put the opening { on the next line if you prefer that way. I
    know some people have very strong feeling about that.

    The comma saves the braces, but then, this might be a Bad Thing, as
    Apple learnt the hard way a couple of years ago [1] :-)

    Now taking my tongue out-of-cheek, I'd prefer your code, too. Much
    clearer and much less hidden footguns.

    I'm all for concise code, but I usually revert some things in a second
    pass when they seem to hurt clarity. After all, you write your code for
    other people to read it.

    Cheers

    [1] https://dwheeler.com/essays/apple-goto-fail.html
    --
    t

    -----BEGIN PGP SIGNATURE-----

    iF0EABECAB0WIQRp53liolZD6iXhAoIFyCz1etHaRgUCZ2JXbAAKCRAFyCz1etHa RlUNAJsGe74OE6IoirUG1xgdPy/hqlq9/QCfVKt7M/t8S8EI0UfSDEVVA4gpTrE=
    =KPlV
    -----END PGP SIGNATURE-----

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From tomas@tuxteam.de@21:1/5 to Kevin Chadwick on Wed Dec 18 12:00:02 2024
    On Wed, Dec 18, 2024 at 10:45:43AM +0000, Kevin Chadwick wrote:
    18 Dec 2024 05:03:12 tomas@tuxteam.de:

    I'm all for concise code, but I usually revert some things in a second
    pass when they seem to hurt clarity. After all, you write your code for other people to read it.

    As you wrote the code then uness that second pass is weeks or months later then clarity likely still suffers

    Definitely: I've learnt a lot by trying to wrap my head
    around code I wrote myself five years earlier. But of
    course, one has to get the chance to do that in the first
    place.

    This is why Ada is so fantastic. Adas main requirements included being optimised for reading and maintenance  to reduce the D.O.D.s software costs.

    It's skills, not tools. Or more precisely: skills first, tools
    then.

    (Back then, in a BigCo, I coined the term "redemption by tool"
    to describe the most widespread malady there).

    Cheers
    --
    t

    -----BEGIN PGP SIGNATURE-----

    iF0EABECAB0WIQRp53liolZD6iXhAoIFyCz1etHaRgUCZ2KptAAKCRAFyCz1etHa RlCIAJ4+YSGj1KbKkw4A+/B8ZB56qtbKswCdHZkoxzCvutvN2Id9BohUYrimC4s=
    =Vpu9
    -----END PGP SIGNATURE-----

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anssi Saari@21:1/5 to Franco Martelli on Wed Dec 18 11:20:01 2024
    Franco Martelli <martellif67@gmail.com> writes:

    Peter A. Darnell, Philip E. Margolis - "C A Software Engineering Approach":

    https://www.google.it/books/edition/_/1nsS5q9aZOUC?hl=it&gbpv=0

    Do you have it too? It's pretty old, with some typo, but it looks to
    me good.

    Sorry, no, doesn't look familiar. I remember I bought some C book in the
    90s as a student. It had a lot of issues in the example code snippets
    but that wasn't bad for learning.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)