C Programming Day 12
Final Practical Projects
Introduction
C Programming Day 12 — Dynamic Memory Allocation in C: malloc(), calloc(), realloc(), free() & Final Practical Projects
Welcome to Day 12 of the C Programming Full Course.
Congratulations! 🎉 You have reached the final major lesson in this C Programming learning journey.
In the previous lesson, you learned about File Handling in C and how programs can store information permanently using files. Today, we will go one step deeper and learn how C programs can manage memory dynamically during program execution.
In this lesson, you will learn Dynamic Memory Allocation in C using malloc(), calloc(), realloc(), and free(). You will also learn about dynamic arrays, memory leaks, dangling pointers, and practical C programming projects.
What is Dynamic Memory Allocation?
Dynamic Memory Allocation is the process of requesting memory while a program is running.
Instead of deciding the exact amount of memory in advance, a program can request memory when it is needed.
#include <stdlib.h>
int *numbers;
numbers = malloc(5 * sizeof(int));
In this example, the program requests enough memory to store five integers.
Why Do We Need Dynamic Memory?
Consider the following fixed-size array:
int numbers[1000];
This creates space for 1,000 integers even if the program only needs a few elements.
With dynamic memory allocation, the program can determine the required size during execution.
int *numbers;
numbers = malloc(size * sizeof(int));
This approach is especially useful when the required amount of data is not known before the program starts.
Static vs Dynamic Memory
| Feature | Fixed Array | Dynamic Memory |
|---|---|---|
| Size | Usually fixed | Can be determined at runtime |
| Flexibility | Limited | High |
| Allocation | Array declaration | malloc(), calloc(), realloc() |
| Resizing | Not directly possible | realloc() can resize an allocation |
| Release | Automatic for local arrays | Use free() for allocated memory |
Stack and Heap Memory
To understand dynamic memory allocation, you should know the basic difference between stack and heap storage.
Stack Memory
Local variables commonly have automatic storage duration.
void example()
{
int x = 10;
}
When the function finishes, its local automatic objects cease to exist.
Heap Memory
Dynamic allocation obtains storage from an area commonly called the heap.
int *ptr = malloc(sizeof(int));
The allocated storage remains available until it is released using free().
The stdlib.h Header
The dynamic memory allocation functions are declared in the standard library header:
#include <stdlib.h>
A typical program using dynamic memory may therefore begin with:
#include <stdio.h>
#include <stdlib.h>
malloc() in C
malloc() stands for memory allocation. It allocates a specified number of bytes.
Syntax:
malloc(number_of_bytes);
Example:
int *ptr;
ptr = malloc(sizeof(int));
This requests enough storage for one integer object.
Understanding sizeof
Instead of manually assuming the size of a data type, use the sizeof operator.
sizeof(int)
For an array of 10 integers:
10 * sizeof(int)
A better general pattern is:
int *numbers = malloc(10 * sizeof(*numbers));
Basic malloc() Example
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *ptr;
ptr = malloc(sizeof(int));
if(ptr == NULL)
{
printf("Memory allocation failed.\n");
return 1;
}
*ptr = 100;
printf("Value = %d\n", *ptr);
free(ptr);
return 0;
}
Here, ptr stores the address of dynamically allocated memory, and *ptr accesses the integer stored there.
malloc() for Multiple Elements
Suppose you need memory for five integers:
int *numbers;
numbers = malloc(5 * sizeof(*numbers));
You can access the allocated elements using array notation:
numbers[0]
numbers[1]
numbers[2]
numbers[3]
numbers[4]
Example
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *numbers;
int i;
numbers = malloc(5 * sizeof(*numbers));
if(numbers == NULL)
{
printf("Memory allocation failed.\n");
return 1;
}
for(i = 0; i < 5; i++)
{
numbers[i] = (i + 1) * 10;
}
for(i = 0; i < 5; i++)
{
printf("%d ", numbers[i]);
}
free(numbers);
return 0;
}
Output:
10 20 30 40 50
Important: malloc() Does Not Initialize Memory
Memory returned by malloc() has indeterminate values. You should not assume that allocated integers automatically contain zero.
If you need specific initial values, initialize the elements yourself or use calloc() when zero-initialized storage is appropriate.
calloc() in C
calloc() stands for contiguous allocation.
Syntax:
calloc(number_of_elements, size_of_each_element);
Example:
int *numbers;
numbers = calloc(5, sizeof(*numbers));
The allocated bytes are initialized to zero.
calloc() Example
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *numbers;
int i;
numbers = calloc(5, sizeof(*numbers));
if(numbers == NULL)
{
printf("Memory allocation failed.\n");
return 1;
}
for(i = 0; i < 5; i++)
{
printf("%d ", numbers[i]);
}
free(numbers);
return 0;
}
Output:
0 0 0 0 0
Difference Between malloc() and calloc()
| Feature | malloc() | calloc() |
|---|---|---|
| Arguments | 1 | 2 |
| Purpose | Allocates a block of bytes | Allocates an array-like block |
| Initial contents | Indeterminate | All allocated bytes are initialized to zero |
| Example | malloc(10 * sizeof(int)) | calloc(10, sizeof(int)) |
realloc() in C
realloc() is used to attempt to resize an existing dynamic memory allocation.
Syntax:
realloc(pointer, new_size);
Example:
int *numbers;
numbers = malloc(5 * sizeof(*numbers));
numbers = realloc(numbers, 10 * sizeof(*numbers));
The resized allocation may remain at the same address or may be moved to another location. The pointer returned by realloc() must therefore be used.
Safe Use of realloc()
A safer pattern is to store the return value in a temporary pointer.
int *temp;
temp = realloc(numbers, new_size);
if(temp == NULL)
{
printf("Reallocation failed.\n");
}
else
{
numbers = temp;
}
This preserves the original pointer if the reallocation fails.
Complete realloc() Example
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *numbers;
int *temp;
int i;
numbers = malloc(3 * sizeof(*numbers));
if(numbers == NULL)
{
printf("Initial allocation failed.\n");
return 1;
}
for(i = 0; i < 3; i++)
{
numbers[i] = i + 1;
}
temp = realloc(numbers, 5 * sizeof(*numbers));
if(temp == NULL)
{
printf("Reallocation failed.\n");
free(numbers);
return 1;
}
numbers = temp;
numbers[3] = 4;
numbers[4] = 5;
for(i = 0; i < 5; i++)
{
printf("%d ", numbers[i]);
}
free(numbers);
return 0;
}
Output:
1 2 3 4 5
free() in C
When dynamically allocated memory is no longer needed, release it using free().
free(pointer);
Example:
int *numbers;
numbers = malloc(10 * sizeof(*numbers));
/* Use numbers */
free(numbers);
numbers = NULL;
Calling free() releases the allocated storage so it can be reused by the memory-management system.
Why Is free() Important?
If a program continuously allocates memory without releasing it, it may consume increasing amounts of memory. This problem is called a memory leak.
A good memory-management pattern is:
Allocate
↓
Use
↓
Free
Setting a Pointer to NULL After free()
After freeing dynamically allocated storage, a pointer no longer points to a valid allocated object.
A common defensive practice is:
free(ptr);
ptr = NULL;
This makes it explicit that the pointer should no longer be used to access the former allocation.
What is a Memory Leak?
A memory leak occurs when dynamically allocated memory is no longer reachable by the program but has not been released.
Incorrect example:
int *ptr;
ptr = malloc(100 * sizeof(*ptr));
ptr = NULL;
The original allocation has been lost.
Correct approach:
int *ptr;
ptr = malloc(100 * sizeof(*ptr));
if(ptr != NULL)
{
free(ptr);
ptr = NULL;
}
What is a Dangling Pointer?
A dangling pointer is a pointer whose referenced storage is no longer valid.
Example:
int *ptr = malloc(sizeof(*ptr));
free(ptr);
/* ptr must not be dereferenced here */
Never access the former allocation after it has been freed.
What is Double Free?
A double free occurs when the same allocation is released more than once.
Incorrect:
free(ptr);
free(ptr);
This results in undefined behavior.
A safer pattern is:
free(ptr);
ptr = NULL;
Checking Allocation Failure
Dynamic memory allocation can fail, so always check the returned pointer.
int *numbers = malloc(1000 * sizeof(*numbers));
if(numbers == NULL)
{
printf("Unable to allocate memory.\n");
return 1;
}
Practical Program 1 — Dynamic Array
Let's create a program that asks the user how many numbers they want to store.
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *numbers;
int n;
int i;
printf("How many numbers? ");
scanf("%d", &n);
if(n <= 0)
{
printf("Invalid size.\n");
return 1;
}
numbers = malloc((size_t)n * sizeof(*numbers));
if(numbers == NULL)
{
printf("Memory allocation failed.\n");
return 1;
}
for(i = 0; i < n; i++)
{
printf("Enter number %d: ", i + 1);
scanf("%d", &numbers[i]);
}
printf("\nNumbers:\n");
for(i = 0; i < n; i++)
{
printf("%d ", numbers[i]);
}
free(numbers);
numbers = NULL;
return 0;
}
Practical Program 2 — Dynamic Array Average
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *numbers;
int n;
int i;
long long sum = 0;
double average;
printf("Enter number of elements: ");
scanf("%d", &n);
if(n <= 0)
{
printf("Invalid number of elements.\n");
return 1;
}
numbers = malloc((size_t)n * sizeof(*numbers));
if(numbers == NULL)
{
printf("Memory allocation failed.\n");
return 1;
}
for(i = 0; i < n; i++)
{
printf("Enter number %d: ", i + 1);
scanf("%d", &numbers[i]);
sum += numbers[i];
}
average = (double)sum / n;
printf("Sum = %lld\n", sum);
printf("Average = %.2f\n", average);
free(numbers);
return 0;
}
Practical Program 3 — Dynamic Array with calloc()
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *numbers;
int n;
int i;
printf("Enter array size: ");
scanf("%d", &n);
if(n <= 0)
{
printf("Invalid size.\n");
return 1;
}
numbers = calloc((size_t)n, sizeof(*numbers));
if(numbers == NULL)
{
printf("Memory allocation failed.\n");
return 1;
}
printf("Initial values:\n");
for(i = 0; i < n; i++)
{
printf("%d ", numbers[i]);
}
free(numbers);
return 0;
}
Practical Program 4 — Expand a Dynamic Array
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *numbers;
int *temp;
int oldSize = 3;
int newSize = 6;
int i;
numbers = malloc((size_t)oldSize * sizeof(*numbers));
if(numbers == NULL)
{
printf("Allocation failed.\n");
return 1;
}
for(i = 0; i < oldSize; i++)
{
numbers[i] = i + 1;
}
temp = realloc(numbers, (size_t)newSize * sizeof(*numbers));
if(temp == NULL)
{
printf("Reallocation failed.\n");
free(numbers);
return 1;
}
numbers = temp;
for(i = oldSize; i < newSize; i++)
{
numbers[i] = i + 1;
}
printf("Expanded array:\n");
for(i = 0; i < newSize; i++)
{
printf("%d ", numbers[i]);
}
free(numbers);
return 0;
}
Dynamic Memory and Structures
Dynamic memory allocation becomes especially useful when working with structures.
struct Student
{
int id;
char name[50];
float marks;
};
You can dynamically allocate one structure:
struct Student *student;
student = malloc(sizeof(*student));
Then use the arrow operator:
student->id = 101;
student->marks = 90.5;
Practical Program 5 — Dynamic Structure
#include <stdio.h>
#include <stdlib.h>
struct Student
{
int id;
char name[50];
float marks;
};
int main()
{
struct Student *student;
student = malloc(sizeof(*student));
if(student == NULL)
{
printf("Memory allocation failed.\n");
return 1;
}
printf("Enter ID: ");
scanf("%d", &student->id);
printf("Enter name: ");
scanf("%49s", student->name);
printf("Enter marks: ");
scanf("%f", &student->marks);
printf("\n===== Student Information =====\n");
printf("ID: %d\n", student->id);
printf("Name: %s\n", student->name);
printf("Marks: %.2f\n", student->marks);
free(student);
return 0;
}
Practical Program 6 — Dynamic Array of Structures
#include <stdio.h>
#include <stdlib.h>
struct Student
{
int id;
char name[50];
float marks;
};
int main()
{
struct Student *students;
int n;
int i;
printf("Enter number of students: ");
scanf("%d", &n);
if(n <= 0)
{
printf("Invalid number.\n");
return 1;
}
students = malloc((size_t)n * sizeof(*students));
if(students == NULL)
{
printf("Memory allocation failed.\n");
return 1;
}
for(i = 0; i < n; i++)
{
printf("\nStudent %d\n", i + 1);
printf("ID: ");
scanf("%d", &students[i].id);
printf("Name: ");
scanf("%49s", students[i].name);
printf("Marks: ");
scanf("%f", &students[i].marks);
}
printf("\n===== Student Records =====\n");
for(i = 0; i < n; i++)
{
printf("ID: %d | Name: %s | Marks: %.2f\n",
students[i].id,
students[i].name,
students[i].marks);
}
free(students);
return 0;
}
Dynamic Memory + File Handling
You can combine the concepts learned in Day 11 and Day 12.
User enters number of records
↓
Allocate memory dynamically
↓
Input records
↓
Process records
↓
Write records to file
↓
Free memory
This combination can be used to build practical record-management applications.
Final Practical Project 1 — Student Management System
Create a menu-driven Student Management System.
================================
STUDENT MANAGEMENT
================================
1. Add Student
2. View Students
3. Search Student
4. Update Student
5. Delete Student
6. Save to File
7. Load from File
8. Exit
Use a structure such as:
struct Student
{
int id;
char name[50];
float marks;
};
This project can combine:
- Variables
- Input and Output
- Conditional Statements
- Loops
- Arrays
- Functions
- Pointers
- Structures
- Dynamic Memory
- File Handling
Final Practical Project 2 — Contact Management System
Create a contact management application with features such as:
1. Add Contact
2. Display Contacts
3. Search Contact
4. Update Contact
5. Delete Contact
6. Save Contacts
7. Load Contacts
8. Exit
Example structure:
struct Contact
{
int id;
char name[50];
char phone[20];
char email[100];
};
Final Practical Project 3 — Inventory Management System
Create an inventory management application.
struct Product
{
int id;
char name[50];
float price;
int quantity;
};
Suggested features:
1. Add Product
2. Display Products
3. Search Product
4. Update Product
5. Sell Product
6. Restock Product
7. Save Inventory
8. Exit
Final Practical Project 4 — Expense Tracker
Create a simple expense management application.
struct Expense
{
int id;
char category[30];
float amount;
};
Suggested features:
1. Add Expense
2. View Expenses
3. Calculate Total
4. Search by Category
5. Save Expenses
6. Exit
Final Practical Project 5 — Library Management System
struct Book
{
int id;
char title[100];
char author[100];
int available;
};
Suggested features:
1. Add Book
2. View Books
3. Search Book
4. Borrow Book
5. Return Book
6. Save Data
7. Exit
Final Practical Project 6 — Quiz Application
Create a C-based quiz application.
struct Question
{
char question[200];
char options[4][100];
int answer;
};
Suggested features:
- Start Quiz
- Display Questions
- Accept Answers
- Calculate Score
- Save Score
- Display Result
Common Dynamic Memory Mistakes
Mistake 1 — Not Checking NULL
Always check whether allocation succeeded:
int *p = malloc(100 * sizeof(*p));
if(p == NULL)
{
return 1;
}
Mistake 2 — Forgetting free()
Release dynamically allocated memory when it is no longer needed:
free(p);
p = NULL;
Mistake 3 — Unsafe realloc()
Avoid overwriting the only pointer before checking whether realloc() succeeded.
void *temp = realloc(p, new_size);
if(temp != NULL)
{
p = temp;
}
Mistake 4 — Accessing Memory After free()
Never dereference a pointer after its allocation has been freed.
Mistake 5 — Allocating the Wrong Amount
Use sizeof instead of manually assuming type sizes.
malloc(10 * sizeof(*p));
Dynamic Memory Best Practices
- Allocate only what you need.
- Check allocation results.
- Free memory when finished.
- Use temporary pointers with realloc().
- Set freed pointers to NULL when appropriate.
- Avoid use-after-free.
- Avoid double free.
- Use sizeof correctly.
Dynamic Memory Allocation Cheat Sheet
Include the Header
#include <stdlib.h>
Allocate Memory
ptr = malloc(size);
Allocate Zero-Initialized Storage
ptr = calloc(count, size);
Resize Memory
temp = realloc(ptr, new_size);
Release Memory
free(ptr);
Set Pointer to NULL
free(ptr);
ptr = NULL;
Day 12 Summary
In this final lesson, you learned how C programs can dynamically request and release memory while running.
The four most important functions are:
malloc()
calloc()
realloc()
free()
malloc()
Allocates a specified number of bytes.
int *p = malloc(10 * sizeof(*p));
calloc()
Allocates storage for multiple objects and initializes the allocated bytes to zero.
int *p = calloc(10, sizeof(*p));
realloc()
Attempts to resize an existing dynamic allocation.
int *temp = realloc(p, 20 * sizeof(*p));
free()
Releases dynamically allocated storage.
free(p);
You also learned about dynamic arrays, dynamic structures, memory leaks, dangling pointers, double free, allocation failures, and practical C projects.
C Programming Full Course — Final Roadmap
| Day | Topic |
|---|---|
| Day 1 | Introduction to C Programming |
| Day 2 | Variables, Data Types & Constants |
| Day 3 | Operators, Input & Output |
| Day 4 | Conditional Statements |
| Day 5 | Loops in C |
| Day 6 | Arrays in C |
| Day 7 | Strings in C |
| Day 8 | Functions in C |
| Day 9 | Pointers in C |
| Day 10 | Structures & Unions |
| Day 11 | File Handling in C |
| Day 12 | Dynamic Memory Allocation & Final Projects |
Conclusion
Congratulations! You have completed C Programming Day 12 and reached the end of this C Programming Full Course series.
You started with basic C syntax and gradually learned how to write increasingly powerful programs.
Throughout this course, you learned variables, data types, operators, conditions, loops, arrays, strings, functions, pointers, structures, unions, file handling, and dynamic memory allocation.
Most importantly, you now have a strong foundation for building practical C applications.
Don't simply read the examples. Type the programs yourself, compile them, modify them, find errors, fix those errors, and create your own projects.
Learn → Code → Practice → Debug → Build → Repeat.
Your C Programming journey does not end with Day 12. This is where you start building with C.

Post a Comment
Thank You For Visit My Website.
I Will Contact As Soon As Possible.