When writing in C, do you often encounter these challenges? Want to implement a function to calculate the average of an arbitrary number of values but get stuck on “the number of parameters is uncertain”; After defining an array, you find that the memory is insufficient and have to redo the code; Even though the code runs, strange errors like “memory leak” and “wild pointer” keep appearing?
In fact, as long as you master the two core skills of variable arguments and dynamic memory management, these problems can be easily solved. Today, we will break down the principles and provide practical code examples to help you thoroughly understand these two advanced C programming concepts.
1. Variable Arguments: Allowing Functions to Accept “Any Number” of Parameters
First, consider a scenario: you need to write a function to calculate the average of multiple integers, which could be 3, 4, or even 10. You can’t write a separate function for each case, right? This is where variable arguments come into play.
1.1 What are Variable Arguments?
Variable arguments are a feature provided by C that allows a function to have a variable number of parameters at declaration, indicated by an ellipsis (…), which signifies that there are “any number of additional parameters”.
The declaration format is quite fixed and must satisfy:there is a fixed parameter before the ellipsis (used to indicate the total number or type of variable parameters), for example:
// num is the fixed parameter (indicating the total number of variable parameters), ... indicates the variable parameter listint average(int num, ...);
1.2 Three Core Steps for Variable Arguments (Must Remember)
To use variable arguments, you must rely on the three macros and one type in the <span><span><stdarg.h></span></span> header file. The steps are as follows:
| Tool | Function |
|---|---|
<span><span>va_list</span></span> |
A special pointer type used to “point to” the starting position of the variable argument list |
<span><span>va_start(ap, last)</span></span> |
Initializes the <span><span>va_list</span></span> variable <span><span>ap</span></span>, making it point to the next parameter after the last fixed parameter (last). |
<span><span>va_arg(ap, type)</span></span> |
From the position pointed to by <span><span>ap</span></span>, retrieves a parameter of type <span><span>type</span></span> and moves <span><span>ap</span></span> to the next parameter |
<span><span>va_end(ap)</span></span> |
Ends access to variable arguments, setting <span><span>ap</span></span> to NULL to avoid wild pointers |
1.3 Practical Example: Calculating the Average of Multiple Integers
#include <stdio.h>#include <stdarg.h> // Must include this header// Calculate the average of num integers: num is the fixed parameter (total number of variable parameters)double average(int num, ...) { va_list valist; // 1. Define va_list variable to traverse variable arguments double sum = 0.0; int i; // 2. Initialize valist: make it point to the next parameter after the fixed parameter num va_start(valist, num); // 3. Traverse all variable parameters (total of num) for (i = 0; i < num; i++) { // Each time retrieve an int type parameter and accumulate to sum sum += va_arg(valist, int); } // 4. End access, release resources va_end(valist); return sum / num; // Return average}int main() { // When calling: the first parameter 4 indicates "there are 4 variable parameters following" printf("Average of 2,3,4,5 = %f\n", average(4, 2, 3, 4, 5)); // The first parameter 3 indicates "there are 3 variable parameters following" printf("Average of 5,10,15 = %f\n", average(3, 5, 10, 15)); return 0;}

Key Reminder: <span><span>va_arg(ap, type)</span></span> must be accurate (for example, if the parameter is <span><span>int</span></span>, write <span><span>int</span></span>, not <span><span>short</span></span> or <span><span>long</span></span>), otherwise it will lead to memory access errors.
2. Dynamic Memory Management: Flexibly Control Memory and Say Goodbye to “Array Size Anxiety”
In C, the size of a regular array is fixed at the time of definition (for example, <span><span>char name[100]</span></span>), and if you later need to store longer content, you will encounter the problem of “insufficient memory”. However, dynamic memory management allows us to allocate/release memory at runtime based on demand, completely solving this pain point.
2.1 Four Core Functions: The “Toolbox” for Dynamic Memory
All dynamic memory functions are in the <span><span><stdlib.h></span></span> header file. The four functions cover the entire process of “allocation, initialization, adjustment, and release”, which is clearer when compared in a table:
| Function Name | Function Description | Characteristics | Notes |
|---|---|---|---|
<span><span>malloc(num)</span></span> |
Allocates <span><span>num</span></span> bytes of contiguous memory |
Does not initialize, memory value is random | Returns <span><span>NULL</span></span> on allocation failure, must check the result |
<span><span>calloc(n, size)</span></span> |
Allocates <span><span>n</span></span> blocks of memory, each of size <span><span>size</span></span>, all initialized to 0 |
Automatically initializes, suitable for storing arrays | Total memory = n × size, must also check for NULL |
<span><span>realloc(p, new_num)</span></span> |
Adjusts the size of memory pointed to by <span><span>p</span></span> to <span><span>new_num</span></span> bytes |
Can expand/shrink memory, original data is preserved | Returns <span><span>NULL</span></span> on adjustment failure, original memory is not lost |
<span><span>free(p)</span></span> |
Releases the dynamic memory pointed to by <span><span>p</span></span> (returns it to the system) |
Key to avoiding memory leaks | Can only release dynamic memory, cannot release it again |
2.2 Practical Example: Dynamically Storing Strings
For example, if you want to store a public account’s “name” and “detailed description”, the name length is fixed, but the description length is uncertain—dynamic memory can flexibly handle this:
#include <stdio.h>#include <stdlib.h>#include <string.h>int main() { char name[100]; // Name: fixed size (assumed max 100 characters) char *description; // Description: dynamic memory (length uncertain) strcpy(name, "PLC"); // Assign value to name // 1. First memory allocation: 30 bytes (for short description) description = (char *)malloc(30 * sizeof(char)); if (description == NULL) { // Key: check if memory allocation was successful fprintf(stderr, "Error: Unable to allocate memory\n"); return 1; // Allocation failed, exit directly } else { strcpy(description, "PLC a Programming Learning Community."); } // 2. Reallocate memory: expand to 100 bytes (for longer description) description = (char *)realloc(description, 100 * sizeof(char)); if (description == NULL) { // Check again to avoid crashes fprintf(stderr, "Error: Unable to reallocate memory\n"); free(description); // Even if it fails, release original memory return 1; } else { strcat(description, " It is very crazy!"); // Append content } // Output results printf("Name = %s\n", name); printf("Description = %s\n", description); // 3. Free memory: must release when no longer in use (to avoid memory leaks) free(description); description = NULL; // Set pointer to NULL to avoid wild pointer return 0;}

Key Breakdown:
<span><span>(char *)malloc(...)</span></span>
<span><span>malloc</span></span><span><span> returns a </span></span><code><span><span>void *</span></span> type, which needs to be cast to <span><span>char *</span></span> (matching the type of <span><span>description</span></span>);
<span><span>sizeof(char)</span></span>
Gets the size of the character type (1 byte), using <span><span>sizeof</span></span> is more universal than writing 1 directly (for example, if you change to <span><span>int</span></span> type, you don’t need to change the number);
<span><span>free</span></span>
After freeing, set the pointer to <span><span>NULL</span></span>: to avoid subsequent operations on “freed memory” (wild pointer issue).
2.3 Pitfall Guide: Three Common Mistakes for Beginners
Dynamic memory management seems simple, but if you’re not careful, problems can arise. Avoid these three mistakes:
-
Forgetting to free memory (memory leak) Dynamic memory will not be automatically released (unless the program exits). If you keep allocating without releasing, long-term operation will exhaust system memory. Remember:Allocate as much dynamic memory as you free.
-
Double freeing memory Calling
<span><span>free(p)</span></span>on memory that has already been freed will cause the program to crash. The solution: immediately set<span><span>p</span></span>to<span><span>NULL</span></span>after freeing (<span><span>free(NULL)</span></span><code><span><span> is safe and will not throw an error).</span></span>
-
Using uninitialized
<span><span>malloc</span></span>memory The memory allocated by<span><span>malloc</span></span><span><span> has random values, using it directly may lead to logical errors. If you need "clean" memory, prefer using </span></span><code><span><span>calloc</span></span><span><span> (automatically initialized to 0), or manually initialize with </span></span><code><span><span>memset</span></span><span><span>.</span></span>
Finally: Quick Review of Core Knowledge Points
To help everyone remember, I have organized the “core points” of the two knowledge areas, which I recommend saving:
| Knowledge Point | Core Points |
|---|---|
| Variable Arguments | Depends on <span><span><stdarg.h></span></span>; <span><span>va_list</span></span> initialization → <span><span>va_arg</span></span><span><span> to get parameters → </span></span><code><span><span>va_end</span></span><span><span> to finish; need to know total number of parameters</span></span> |
| Dynamic Memory Management | Four functions: <span><span>malloc</span></span> (allocation), <span><span>calloc</span></span><span><span> (allocation + initialization), </span></span><code><span><span>realloc</span></span><span><span> (adjustment), </span></span><code><span><span>free</span></span><span><span> (release); </span></span><strong><span><span>Checking for NULL and releasing are essential steps</span></span></strong> |
Have you encountered issues with variable arguments or dynamic memory while learning C? For example, “how to troubleshoot wild pointers” or “can variable arguments accept different types?” Feel free to share your questions or experiences in the comments section, and let’s communicate and improve together!