Hello, everyone! I am Xiaokang.
Today, let’s talk about a seemingly ordinary yet exceptionally powerful keyword in C language—<span>static</span>. It acts like an “invisible cloak” in your code, subtly altering the behavior of your program.
Many beginners have a vague understanding of it; they either hesitate to use it or misuse it without knowing why. Today, I will help you thoroughly understand it in simple terms!
What is static? Isn’t it just a keyword?
Don’t underestimate this small keyword; it is a versatile tool in C language! Depending on the context, static has completely different functionalities:
- When used in front of a local variable: it gives the variable “memory”.
- When used in front of a global variable/function: it makes them “shy” (visible only within the same file).
- When used in front of a class/struct member: it allows all objects to share a variable.
Wow, one word can do three jobs; no wonder many people find it confusing! But don’t worry, we will explain each one step by step to ensure you fully understand!
Scenario 1: Giving Local Variables “Memory”
Normally, local variables we define are “goodbye” after the function call ends; when the function is called again, the variable is reinitialized. However, by adding <span>static</span>, this variable gains “memory” and can remember the value from the last function call!
Let’s look at an example:
#include <stdio.h>
// Normal function without static
void normalCounter() {
int count = 0; // Resets to 0 every time it is called
count++;
printf("Normal counter: %d\n", count);
}
// Function using static
void staticCounter() {
static int count = 0; // Initialized to 0 only on the first call
count++; // Increments based on the last call
printf("Static counter: %d\n", count);
}
int main() {
// Call 3 times to see the effect
for (int i = 0; i < 3; i++) {
normalCounter();
staticCounter();
printf("-------------------\n");
}
return 0;
}
Output:
Normal counter: 1
Static counter: 1
-------------------
Normal counter: 1
Static counter: 2
-------------------
Normal counter: 1
Static counter: 3
-------------------
Do you see the difference? The <span>count</span> without static starts from 0 each time, becoming 1 after incrementing; while the <span>count</span> with static remembers its last value: 1 on the first call, 2 on the second, and 3 on the third!
What’s the use of this? Consider the following scenarios:
- Need to record how many times a function has been called.
- Need to cache computation results to avoid redundant calculations.
- Need to check if it is the first time the function is called.
Characteristics of Static Local Variables:
- Value is retained: The variable’s value does not disappear after the function call ends.
- Initialized only once:
<span>static int x = 10;</span>is initialized only when the program first executes this line. - Memory location: Stored in the static storage area, not on the stack.
- Default value is 0: If not initialized, it is automatically set to 0 (ordinary local variables without initialization have random values).
Scenario 2: Making Global Variables/Functions “Shy” (Limiting Visibility)
Without <span>static</span>, global variables and functions defined in a C file (also known as a translation unit) are visible to other files by default. However, sometimes we want certain functions and variables to be “internal implementation details” that we don’t want other files to see or use.
This is where static comes into play! It can make global variables and functions “shy”, visible only within the file they are defined.
Suppose we have two files:
file1.c
#include <stdio.h>
// Normal global variable: accessible by other files
int globalCounter = 0;
// Static global variable: accessible only within this file
static int privateCounter = 0;
// Normal function: callable by other files
void increaseGlobal() {
globalCounter++;
printf("Global counter: %d\n", globalCounter);
}
// Static function: callable only within this file
static void increasePrivate() {
privateCounter++;
printf("Private counter: %d\n", privateCounter);
}
// Public function that uses the private counter
void accessPrivate() {
increasePrivate(); // Can call static function
printf("Accessing private counter through interface\n");
}
file2.c
#include <stdio.h>
// Declare external global variable
extern int globalCounter;
// Cannot access privateCounter because it is static
// Declare external function
void increaseGlobal();
void accessPrivate();
int main() {
increaseGlobal(); // Can call
// increasePrivate(); // Error! Cannot call static function
accessPrivate(); // Can use indirectly through public interface
printf("Accessing global counter in main: %d\n", globalCounter);
// printf("Accessing private counter in main: %d\n", privateCounter); // Error! Cannot access
return 0;
}
Output:
Global counter: 1
Private counter: 1
Accessing private counter through interface
Accessing global counter in main: 1
This example illustrates:
<span>globalCounter</span>and<span>increaseGlobal()</span>are shared between the two files.<span>privateCounter</span>and<span>increasePrivate()</span>are only visible in file1.c.- To use private functionality, it must be accessed through a public “interface function” like
<span>accessPrivate()</span>.
Characteristics of Static Global Variables/Functions:
- Limits scope: Visible only within the defining file.
- Avoids naming conflicts: Different files can use static variables/functions with the same name.
- Hides implementation details: Set internal helper functions as static.
- Improves compilation efficiency: The compiler can optimize static functions more effectively.
Scenario 3: Creating Shared Variables in Structs/Classes
In C++, you can define static member variables in a class, allowing all objects to share the same variable. Although C does not have classes, this concept is also used in some C code styled like C++:
#include <stdio.h>
// Assume this is a simple "class"
typedef struct {
int id;
char* name;
// Note: In C, you cannot directly define static variables inside a struct
} Person;
// In C, we define static variables outside the struct
static int Person_count = 0;
// "Constructor"
Person createPerson(char* name) {
Person p;
p.id = ++Person_count; // Increment counter for each Person created
p.name = name;
return p;
}
// Get total number of created Persons
int getPersonCount() {
return Person_count;
}
int main() {
Person p1 = createPerson("Zhang San");
Person p2 = createPerson("Li Si");
Person p3 = createPerson("Wang Wu");
printf("Total created Person objects: %d\n", getPersonCount());
printf("IDs of each Person: %d, %d, %d\n", p1.id, p2.id, p3.id);
return 0;
}
Output:
Total created Person objects: 3
IDs of each Person: 1, 2, 3
In this example, <span>Person_count</span> is shared among all <span>Person</span> objects to keep track of how many objects have been created and assign a unique ID to each.
Deep Understanding of Static: Lifecycle vs Scope
Many people confuse the two roles of static: changing lifecycle and changing scope.
Lifecycle: The duration for which a variable exists.
- Ordinary local variables: exist during the function call.
- Static local variables: exist throughout the program’s execution.
Scope: The range within which a variable is visible.
- Ordinary global variables: visible to all files.
- Static global variables: visible only to the defining file.
Common Static Usage Scenarios
1. Implementing Singleton Pattern (Non-thread-safe)
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int data;
} Singleton;
Singleton* getInstance() {
static Singleton* instance = NULL;
if (instance == NULL) {
// Create object on first call
instance = (Singleton*)malloc(sizeof(Singleton));
instance->data = 42;
printf("Created singleton object\n");
}
return instance;
}
int main() {
Singleton* s1 = getInstance();
Singleton* s2 = getInstance();
printf("s1 address: %p, data: %d\n", (void*)s1, s1->data);
printf("s2 address: %p, data: %d\n", (void*)s2, s2->data);
// Modify s1's data
s1->data = 100;
// Check if s2 has also changed
printf("After modification, s2 data: %d\n", s2->data);
// Note: In actual applications, memory release issues need to be handled
return 0;
}
Output:
Created singleton object
s1 address: 00C37F28, data: 42
s2 address: 00C37F28, data: 42
After modification, s2 data: 100
This example implements the singleton pattern: no matter how many times you call <span>getInstance()</span>, only one <span>Singleton</span> object will be created.
However, be aware that this implementation may have issues in a multi-threaded environment. Imagine two threads calling <span>getInstance()</span> for the first time simultaneously; they both find <span>instance == NULL</span> and create an object! Solving this problem requires locking or using atomic operations, which is known as the “thread-safe singleton pattern”.
2. Calculating Fibonacci Sequence (Using Caching)
#include <stdio.h>
// Using static array to cache results, avoiding redundant calculations
unsigned long long fibonacci(int n) {
static unsigned long long cache[100] = {0}; // Cache results
static int initialized = 0;
// Initialize the first two numbers
if (!initialized) {
cache[0] = 0;
cache[1] = 1;
initialized = 1;
}
// If the result has been calculated, return it directly
if (cache[n] != 0 || n <= 1) {
return cache[n];
}
// Calculate and cache the result
cache[n] = fibonacci(n-1) + fibonacci(n-2);
return cache[n];
}
int main() {
printf("The 40th Fibonacci number: %llu\n", fibonacci(39));
// Recalculate, will directly use cache
printf("Recalculating, the 40th Fibonacci number: %llu\n", fibonacci(39));
return 0;
}
This example uses a static array to cache computed results, significantly improving efficiency.
Common Pitfalls and Considerations with Static
-
Do not define static global variables/functions in header files: If you define
<span>static</span>variables/functions in a header file, each source file that includes that header will create its own copy, which may lead to unexpected results. -
Initialization order: Static variable initialization occurs at program startup, but the initialization order of static variables in different files is uncertain.
-
Multi-thread safety issues: Concurrent access to static variables by multiple threads may lead to race conditions, requiring mutex protection.
Summary: The Three “Superpowers” of Static
- Memory: Static local variables remember the state between calls.
- Invisibility: Static global variables/functions hide implementation details.
- Sharing Spirit: Static is used for shared data in “classes”.
By mastering these three points, you can flexibly use <span>static</span> in programming, making your programs more efficient, safer, and elegant!
Thought Questions
- How to use static to create a counter function that returns an incrementing value each time it is called?
- How to ensure each file has its own “file ID counter” but shares it among all functions within the file?
Feel free to share your solutions in the comments! See you next time~
Static is just the tip of the iceberg; the journey of programming is still long…
If you enjoy this in-depth yet straightforward technical analysis, this is just one of the many fascinating features of C language. If you want to continue this programming exploration journey, follow “Learning Programming with Xiaokang“, where you will discover:
- Practical programming skills that textbooks won’t teach you.
- Linux C/C++ practical: a complete link from principles to applications.
- Decoding underlying mechanisms: explaining the most complex concepts in the simplest language.
- Interview question analysis: mastering the thought process of major company interviewers in advance.
My teaching philosophy is: No grand theories, just straightforward explanations. Whether you are a beginner just starting with programming or an experienced coder looking to advance, you will find content suitable for you.

END
Author:xiaokang1998
Source:Learning Programming with XiaokangCopyright belongs to the original author. If there is any infringement, please contact for deletion..▍Recommended ReadingHow much does a genuine Keil cost?Microcontrollers can output PWM like thisSharing an embedded development debugging tool!→ Follow for more updates ←