Thread: A memory leak

  1. #1
    Registered User
    Join Date
    May 2020
    Posts
    23

    A memory leak

    Everyone,

    I wrote this trivial program in minutes to see how well it would come out. I produces the correct answers. But I don't know how to plug the memory leak Splint found. Would you please tell me how to solve the problem.

    If you think I caught OCD, you're. right. I'd hate to hear Splint complain about anything. That's why I assign function values to integer variables when most programmers wouldn't do that.

    Code:
    #define MAXNUMS 30#include <stdio.h>
    #include <string.h>
    #include <stdbool.h>
    
    
    static bool between(const unsigned int low,  const unsigned int high, const unsigned int value)
    {
        return value >= low && value <= high;
    }
    
    
    static void print_list(unsigned int list[],  const unsigned int length)
    {
        register  unsigned int place;
        int n;
    
        n = puts("Fibonacci numbers");
        for (place = 0; place < length; ++place)
            n = printf("%u ", list[place]);
        
        n = putchar('\n');
    }
    
    
    static unsigned  int *fibonacci_numbers(unsigned int numbers[], const unsigned int n)
    {
       register  unsigned int place;
    
    
        numbers[0] = 0;
        numbers[1] = 1;
    
    
        for (place = 2; place <= n; ++place)
            numbers[place] = numbers[place-1] + numbers[place-2];
        return numbers;
     }
    
     
    int main(void)
    {
         int n;
         unsigned int count; 
         unsigned int numbers[MAXNUMS];
    
    
         n = puts("How many Fibonacci numbers should I generate?");
         n = scanf("%u", &count);
         if (!between(1, MAXNUMS, count))
             printf("%u is not betweem 1 and %u.\n", count, MAXNUMS);
         else
            print_list(fibonacci_numbers(numbers, count), count);
         return 0;
     }

  2. #2
    Registered User rstanley's Avatar
    Join Date
    Jun 2014
    Location
    New York, NY
    Posts
    1,127
    Your "memory leak" is erroneous! If you are on Linux, you could run valgrind. Splint is not that reliable, IMHO. I consider splint report of a memory leak as a false positive.

    I would also forget using the "register" keyword. The compiler is better at optimizing the code that a human is! ;^) With modern processors, I don't think it would matter whether or not you use the "register" keyword.

    Code:
    $ valgrind ./bar
    ==36540== Memcheck, a memory error detector
    ==36540== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
    ==36540== Using Valgrind-3.20.0 and LibVEX; rerun with -h for copyright info
    ==36540== Command: ./bar
    ==36540== 
    How many Fibonacci numbers should I generate?
    12
    Fibonacci numbers
    0 1 1 2 3 5 8 13 21 34 55 89 
    ==36540== 
    ==36540== HEAP SUMMARY:
    ==36540==     in use at exit: 0 bytes in 0 blocks
    ==36540==   total heap usage: 2 allocs, 2 frees, 2,048 bytes allocated
    ==36540== 
    ==36540== All heap blocks were freed -- no leaks are possible
    ==36540== 
    ==36540== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
    Last edited by rstanley; 03-16-2024 at 04:59 AM.

  3. #3
    Registered User rstanley's Avatar
    Join Date
    Jun 2014
    Location
    New York, NY
    Posts
    1,127
    If you think I caught OCD, you're. right. I'd hate to hear Splint complain about anything. That's why I assign function values to integer variables when most programmers wouldn't do that.
    If you are capturing the output from output functions just to prevent messages from splint, don't waste your time! use:
    Code:
    splint -retvalint  ./source_file.c
    You should be checking the return value from scanf() to see if the entry was correct:
    Code:
    How many Fibonacci numbers should I generate?
    a12
    65636608 is not betweem 1 and 30.
    Also, you should be initializing all your local variables, which explains the erroneous value in my test with "A12".
    Last edited by rstanley; 03-16-2024 at 05:25 AM.

  4. #4
    Registered User
    Join Date
    Apr 2021
    Posts
    144
    It appears that you are using equality in your range-top comparison. That is, count <= MAX. You do the same thing in your fibonacci routine. This means it is possible to enter the array size limit as a valid value, but accessing that element of the array is not permitted - arrays go from 0 .. n-1, accessing array[n] is not permitted (although computing that address is valid).

    In C, it is "idiomatic" to use non-inclusive ranges in order to accommodate the zero-based nature of arrays. You might find your code "just works better" if you get used to thinking in terms of 0 <= x < max.

  5. #5
    Registered User
    Join Date
    May 2020
    Posts
    23
    Quote Originally Posted by aghast View Post
    It appears that you are using equality in your range-top comparison. That is, count <= MAX. You do the same thing in your fibonacci routine. This means it is possible to enter the array size limit as a valid value, but accessing that element of the array is not permitted - arrays go from 0 .. n-1, accessing array[n] is not permitted (although computing that address is valid).

    In C, it is "idiomatic" to use non-inclusive ranges in order to accommodate the zero-based nature of arrays. You might find your code "just works better" if you get used to thinking in terms of 0 <= x < max.
    Thank you for the excellent points. aghast. I know that in C, array indexing begins at zero. So I should have considered that when writing the "between" function. Lately, I'm programming in Racket, Common Lisp and OCaml.

  6. #6
    Registered User
    Join Date
    May 2020
    Posts
    23
    It's a relief to hear that memory didn't leak. Since I know a leak can be dangerous, I asked you experts about it.

    Sadly, there's no Macport of valgrind.

    I'll initialize variables from now on.

  7. #7
    Registered User
    Join Date
    May 2020
    Posts
    23
    Everyone, do I need to correct the program because splint printed these messages?

    plicitly temp storage numbers returned as implicitly only:
    numbers
    Temp storage (associated with a formal parameter) is transferred to a
    non-temporary reference. The storage may be released or new aliases created.
    (Use -temptrans to inhibit warning)
    fibo.c: (in function main)
    fibo.c:48:38: Passed storage numbers not completely defined (*numbers is
    undefined): fibonacci_numbers (numbers, ...)
    Storage derivable from a parameter, return value or global is not defined.
    Use /*@out@*/ to denote passed or returned storage which need not be defined.
    (Use -compdef to inhibit warning)
    fibo.c:48:20: New fresh storage (type unsigned int *) passed as implicitly temp
    (not released): fibonacci_numbers(numbers, count)
    A memory leak has been detected. Storage allocated locally is not released
    before the last reference to it is lost. (Use -mustfreefresh to inhibit
    warning)


    Finished checking --- 3 code warningsibo.c:32:9: Implicitly temp storage numbers returned as implicitly only:
    numbers
    Temp storage (associated with a formal parameter) is transferred to a
    non-temporary reference. The storage may be released or new aliases created.
    (Use -temptrans to inhibit warning)
    fibo.c: (in function main)
    fibo.c:48:38: Passed storage numbers not completely defined (*numbers is
    undefined): fibonacci_numbers (numbers, ...)
    Storage derivable from a parameter, return value or global is not defined.
    Use /*@out@*/ to denote passed or returned storage which need not be defined.
    (Use -compdef to inhibit warning)
    fibo.c:48:20: New fresh storage (type unsigned int *) passed as implicitly temp
    (not released): fibonacci_numbers(numbers, count)
    A memory leak has been detected. Storage allocated locally is not released
    before the last reference to it is lost. (Use -mustfreefresh to inhibit
    warning)


    Finished checking --- 3 code warnings

  8. #8
    Registered User rstanley's Avatar
    Join Date
    Jun 2014
    Location
    New York, NY
    Posts
    1,127
    BillMcEnaney:

    Everyone, do I need to correct the program because splint printed these messages?
    IMHO, No.

    splint is outdated and has too many "False Positives". Apparently it has not been updated since 2007, prior to the C11 and C17 C Standards were published!

    [splint 2007-07-12 (3.1.2)]

    I would dump splint!

    Please look a the list of static Code checkers on Wikipedia, and try several of the ones compatible with MacOS, you seem to be using.

    Your code is correct as written, with one exception, although there are more than one way to code Fibonacci numbers.

    Please look at my suggestions in my previous post #2, especially checking the return value from scanf() to check for bad input! This one is very important!

    You will need a loop to check for errors, remove the garbage from the input buffer, but NOT with fflush(), then ask the user to input a new entry till legitimate input has been entered.
    Last edited by rstanley; 03-25-2024 at 07:17 AM.

  9. #9
    Registered User rstanley's Avatar
    Join Date
    Jun 2014
    Location
    New York, NY
    Posts
    1,127
    Actually, there is one minor correction I would make besides the my other reccmendations:
    Code:
    printf("%u is not betweem 1 and %u.\n", count, MAXNUMS);
    
    // Should be:
    
    printf("%u is not betweem 1 and %d.\n", count, MAXNUMS);
    The #define is interpreted by the compiler as an "int", not an "unsigned int".

    It doesn't result in an error, but when I use "cppcheck" on Linux, it does report as a warning.

    Only ran cppcheck on Linux. It should be available for MacOS, along with others.

    ccpcheck output:
    Code:
    foo.c:52:10: warning: %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'. [invalidPrintfArgType_uint]
             printf("%u is not betweem 1 and %u.\n", count, MAXNUMS);
             ^
    Last edited by rstanley; 03-25-2024 at 08:04 AM.

  10. #10
    Registered User
    Join Date
    May 2020
    Posts
    23
    RStanley, thank you. I tried to use unsigned integers because they can't be negative. Nothing in my program could produce a negative number. But I like my data types to be the best ones for what I want to do. I'm fluent in ANSI Standard Pascal not in C. But I now prefer C to Pascal because it's more concise than Pascal. Call me old-fashioned because I always want to write fully portable code without dialect-specific features.

    Though I love Python, it annoys me because old features go when new ones come. After I get fluent in Haskell and OCaml, Python's changes won't matter to me anymore. Haskell, OCaml, Racket, and Common Lisp will be the only languages I'll use.

    My best friend, Dr. Tim Kohl, the Unix System Administrator at Boston University, doesn't care for functional programming. He adores Perl instead because it'll help him control the computer much more thoroughly than a functional programming language will. I thought the same way years ago. Now I strive for concise, mathematically beautiful code, even when I need to pay for those qualities with slower run time and more main storage. If I needed to write a Fibonacci function, it would be like the one you guys found in my C program. I wouldn't write one recursively since a recursive Fibonacci function is tree recursive. It makes the computer calculate the same numbers more than once. That wastes storage and time.

    I love Lisp, Racket, Haskell, and Python partly because their programs make the machine collect garbage. So memory won't leak and the machine won't cause a segmentation by trying to follow a null pointer. C is for programmers much more skillful than I'll ever be. Other languages help me avoid stupid mistakes. That's why I'll take all the help a language will give me.

  11. #11
    Registered User
    Join Date
    May 2020
    Posts
    23
    Thank you for the excellent tips, rstanley. I'll do what you suggest. Meanwhile, please suggest a program to replace splint because I don't think anyone ported lint to MacOS. That OS doesn't even come with yacc and bison. Then again, I'll think I'm cheating to use a parser generator when I write a compiler.

    Naturally, I know that sane programmers don't want to reinvent the wheel. So I realize that a parser generator would save me plenty of work and even a headache or two. But I want to learn how to write elegant code on my own. If you've read any programs a from a parser generator, you know it won't make you exclaim, "Wow, that's gorgeous."

    You should have seen the lousy Cobol programs I maintained years ago. I still hate the wordy language. When I program, I want my code's meaning to be clear to anyone who knows the language I wrote it in. But that goal is hard to reach when you need about 20 lines to write a "Hello, world" program.

    Though I don't mean to brag, in my Cobol days, only I knew how to find a square root in Cobol. Or so it seemed. Another programmer earned a B.S. degree in mathematics before his programming carer. Nobody there thought, "Aha, I'll raise a number to the half power."

    I drove home, translated a Pascal function into Cobol, and handed the Cobol code to them mathematician who thought it contained in infinite loop. But another guy added my code the program that needed it. That code worked correctly the first time and forever after that. Maybe that convinced the other programmers that it was good for me to keep my head in the academic clouds, though I risked getting my nose sideswiped by a Boeing 767.

    Then again, it disappointed me to know the square root "function" was the only original code I put into production back then. I longed for a day when my boss would let me write a program from scratch because my AI professor said I wrote "damn good code." That boss handed me a specification for a new program before we discovered that his boss decided to lay me off. Yes, I do mean "layoff." It's not a euphemism for getting fired. A few mons later, the company went out of business.

  12. #12
    Registered User rstanley's Avatar
    Join Date
    Jun 2014
    Location
    New York, NY
    Posts
    1,127
    Quote Originally Posted by BillMcEnaney View Post
    Call me old-fashioned because I always want to write fully portable code without dialect-specific features.
    You are not being "Old Fashioned"! Just stick to using the Standard C Library and your code will be very portable. Avoid most, if not all of the compiler specific extension functions!

    Quote Originally Posted by BillMcEnaney View Post
    C is for programmers much more skillful than I'll ever be.
    C is for anyone who will take the time to thoroughly learn the language, understand it's pitfalls, and how to avoid them! You don't have to become an "expert" to use and appreciate the language!

    If you read my previous reply, you know that cppcheck is available for MacOS. Look at the Cppcheck web page, and you will see that the command presented, "brew install cppcheck" should install it on you Mac.

    I don't use Mac so you are on your own if the command you need is different than the one presented.

    As for other possible Static Code Analyzers for MacOS, a simple Google search command will present many sites you can explore for other analyzers. I can't do all the work for you!

    Good luck!
    Last edited by rstanley; 03-27-2024 at 05:23 AM.

  13. #13
    Registered User
    Join Date
    May 2020
    Posts
    23
    rstanley,

    You're right. I can program in C. But I'm trying to do what a professor told me when I audited a course about algorithms. He said, "Find a language to excel at and always use it." It probably will be a functional one.

    I'll install cppcheck.

    I ran it on my program. But cppcheck didn't make the machine complain about anything. Maybe I'll uninstall splint.
    Last edited by BillMcEnaney; 03-27-2024 at 02:23 PM.

  14. #14
    Registered User rstanley's Avatar
    Join Date
    Jun 2014
    Location
    New York, NY
    Posts
    1,127
    You may also want to check out this latest article on the Static Code Analysis options being added to gcc 14, or already added to the current gcc compiler.

    Check out all the gcc options, especially the section, "Static Analyzer Options".

    Also, if using clang instead of gcc, please check out the clang options.
    Last edited by rstanley; 04-04-2024 at 07:17 AM.

  15. #15
    Registered User
    Join Date
    May 2020
    Posts
    23
    Thank you, rstanley. I run gcc and clang.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. memory leak
    By *nick in forum Windows Programming
    Replies: 8
    Last Post: 09-17-2012, 06:15 PM
  2. Possible memory leak?
    By 127.0.0.1 in forum C Programming
    Replies: 21
    Last Post: 05-02-2011, 04:53 PM
  3. Replies: 2
    Last Post: 09-28-2006, 01:06 PM
  4. Is this a memory leak?
    By jasonhv in forum C++ Programming
    Replies: 5
    Last Post: 10-20-2005, 08:37 PM
  5. How is this a Memory Leak?
    By Cheeze-It in forum C++ Programming
    Replies: 4
    Last Post: 05-24-2002, 04:59 AM

Tags for this Thread