Memory Leak Bugs in C Projects: Causes, Examples, and Solutions
This article shares practical experiences from unit testing a critical C module, describing common memory‑leak scenarios, their root causes, and concrete solutions while also offering a checklist of typical leak‑prone patterns for developers.
In a recent unit‑testing effort on an important low‑level C module (over 6,000 lines of code), the author decided to write a series of articles to summarize testing practices and experiences, with a future focus on white‑box testing theory.
Test Object: The target code is a C project consisting of roughly 6,000 lines.
Test Method: White‑box testing performed in two phases – static testing (code review) and dynamic testing using the open‑source gtest framework. Over 10,000 lines of test code were written, yielding a test‑to‑code ratio of about 1:1.8 and a function‑to‑test‑case ratio of roughly 1:9.
During testing, several classic defect types were discovered, including memory leaks, boundary‑value errors, illegal memory accesses, return‑code errors, unreachable branches, division‑by‑zero, redundant parameters, weak logic checks, logging mistakes, and general robustness suggestions.
The article now focuses on two representative memory‑leak bugs.
Memory Leak Bug 1: A function allocates heap memory, then throws an exception and returns early, bypassing the cleanup code at the end of the function.
Solution: Add explicit memory‑release statements before each early return in all exception paths.
Memory Leak Bug 2: For a complex structure that allocates memory in multiple steps, a failure or exception on the N‑th allocation causes an immediate return without freeing the memory allocated in the previous N‑1 steps.
The structure contains an int count and a char* array of count elements, each pointing to a string. Memory is allocated in a loop; if an exception occurs before all allocations succeed, previously allocated blocks are never freed, resulting in a leak.
Solution: Before any early exit, invoke a helper like free_String_vector(nodes, j, 0) to release all previously allocated blocks.
Root Causes of Memory Leaks:
Memory allocated within a function is not freed due to skipped cleanup statements.
Exceptions or early returns occur before the release code.
Multiple return paths exist without corresponding free operations.
Loops with continue / break bypass the free statements.
Code extensions add new return paths or modify loops without updating cleanup.
Memory allocated in one place is freed elsewhere, making it hard to track without tools.
Partial allocation failures in complex structures require explicit rollback of earlier allocations.
In C++, smart pointers or destructors can mitigate leaks (the author later adopted C++ smart pointers).
Additional notes mention that similar resource‑cleanup patterns apply to database connections, file descriptors, mutexes, sockets, etc.
The author promises future articles on boundary‑value testing and invites readers to stay tuned.
Summary
The fundamental issue behind the presented bugs is that memory‑release statements are skipped in certain execution paths; recognizing these patterns and applying systematic cleanup (or using RAII techniques) can prevent such leaks.
360 Quality & Efficiency
360 Quality & Efficiency focuses on seamlessly integrating quality and efficiency in R&D, sharing 360’s internal best practices with industry peers to foster collaboration among Chinese enterprises and drive greater efficiency value.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
