For working professionals
For fresh graduates
More
5. Array in C
13. Boolean in C
18. Operators in C
33. Comments in C
38. Constants in C
41. Data Types in C
49. Double In C
58. For Loop in C
60. Functions in C
70. Identifiers in C
81. Linked list in C
83. Macros in C
86. Nested Loop in C
97. Pseudo-Code In C
100. Recursion in C
103. Square Root in C
104. Stack in C
106. Static function in C
107. Stdio.h in C
108. Storage Classes in C
109. strcat() in C
110. Strcmp in C
111. Strcpy in C
114. String Length in C
115. String Pointer in C
116. strlen() in C
117. Structures in C
119. Switch Case in C
120. C Ternary Operator
121. Tokens in C
125. Type Casting in C
126. Types of Error in C
127. Unary Operator in C
128. Use of C Language
How do you store data in reverse order using C—so the last item is the first out?
You use a stack in C, a classic Last-In, First-Out (LIFO) data structure.
A stack in C stores data elements sequentially and allows operations like push (add), pop (remove), peek (view top), and display. It’s commonly implemented using arrays or linked lists. Stacks are used in recursion tracking, expression evaluation, undo features, and many algorithmic problems.
In this tutorial, you’ll learn how to implement a stack in C using arrays. We'll walk through each operation, explain the logic behind it, and provide clean, testable code examples. You’ll also learn how to handle stack overflow and underflow conditions—two common issues when dealing with limited memory.
By the end, you’ll be confident in building and using stacks in your own C programs.
Want to explore more essential data structures in C? Enroll in our Software Engineering Courses for hands-on learning and real-world applications.
In C, a stack can be represented using either an array or a linked list. An array-based implementation is straightforward and useful for stacks of fixed size, while a linked list implementation is dynamic and allows for a flexible size.
Gain in-demand expertise in Cloud Computing, DevOps, and AI-powered Full Stack Development with cutting-edge programs from top institutions. Build practical skills, earn industry-recognized credentials, and accelerate your career in the evolving tech landscape.
In this representation, the stack is implemented as an array, with the top of the stack being managed by an index variable.
Here’s the basic structure:
#define MAX 100 // Maximum stack size
int stack[MAX]; // Stack array
int top = -1; // Initialize top of stack as -1
Alternatively, you can use a linked list to represent a stack. In this case, each stack element is stored in a node, with each node pointing to the next one.
struct Node {
int data;
struct Node* next;
};
struct Node* top = NULL;
Both approaches are effective, but using a linked list is more flexible as it avoids the need to predefine a size.
Also Read: What are Data Structures in C & How to Use Them?
With the stack structure in place, let’s now explore the key operations that power stack functionality.
Here’s a quick look at the basic operations performed on a stack:
Operation | Description | Time Complexity | Space Complexity |
push() | Inserts an element at the top of the stack. | O(1) | O(1) |
pop() | Removes the top element from the stack. | O(1) | O(1) |
top() | Returns the top element without removing it. | O(1) | O(1) |
isEmpty() | Checks if the stack is empty. | O(1) | O(1) |
isFull() | Check if the stack is full. | O(1) | O(1) |
The push() operation adds an element to the top of the stack. It first creates a new node and then sets the new node’s pointer to the current top. The top pointer is updated to the new node, effectively making it the new top of the stack.
Algorithm for push():
The pop() operation removes the top element from the stack. It first checks if the stack is empty. If not, the top element is removed, and the top pointer is updated to the next node in the stack.
Algorithm for pop():
The top() operation retrieves the top element without removing it. It simply returns the value of the top element without modifying the stack.
Algorithm for top():
The isEmpty() operation checks if the stack is empty. It returns true if the stack is empty (i.e., the top pointer is NULL), and false otherwise.
Algorithm for isEmpty():
The isFull() operation checks if the stack is full. This operation isn't always necessary for a dynamic stack (implemented with a linked list) because the stack can grow as needed. For a static stack (e.g., using an array), it checks if the stack has reached its maximum capacity.
Algorithm for isFull():
Also Read: Types of Operators in C: Roles, Usage & Best Practices 2025
On that note, let's look at the types of stacks and their specific applications.
In C programming, stacks can be classified into different types based on their implementation. The two main types of stacks are Static Stacks and Dynamic Stacks.
Understanding these will help you choose the right one for your application, especially when dealing with stack operations in C.
A static stack is implemented using arrays. The stack size is predefined, meaning it has a fixed size at the time of creation. This makes the static stack simple to implement and efficient for small applications where the size of the stack is predictable.
However, one limitation is that the size cannot be changed dynamically.
Example:
int top = -1; #define MAX 100 // Set stack size
int stack[MAX]; // Array for stack
// Initialize top
A dynamic stack is implemented using linked lists. Unlike the static stack, the size of the stack is not predefined. The stack grows and shrinks dynamically as elements are added or removed, making it more flexible than a static stack.
It avoids memory wastage since it only uses memory as needed.
Example:
struct Node {
int data;
struct Node* next;
};
struct Node* top = NULL;
Here’s the list of a few other types of stacks:
A circular stack wraps the end of the stack to the beginning, making better use of memory, especially in applications with fixed memory limits. Ideal for cases where stack overflow could occur in a linear stack, as it reuses the memory space.
Example:
struct Node {
int data;
struct Node* next;
};
A double-ended stack (deque) allows inserting and removing elements from both ends, making it a versatile data structure. It's commonly used when you need efficient access to both ends of the stack.
Example:
struct Node {
int data;
struct Node* next;
struct Node* prev;
};
A multi-stack implementation uses one array or linked list to manage multiple stacks. This method is useful in systems where different tasks need isolated memory spaces, such as in function calls or memory management systems.
Example:
int stack[MAX_SIZE];
int top[NUM_STACKS];
A persistent stack preserves the history of its operations, enabling users to access previous states. This is perfect for applications like text editors, where users can undo actions and revert to previous versions of data.
Example:
struct Node {
char action[100];
struct Node* next;
};
Why Choose the Right Stack?
The type of stack you choose affects your program's performance, memory usage, and complexity.
For example, a static stack is faster but has a fixed size, whereas a dynamic stack offers flexibility but may incur extra memory overhead. A circular stack can be more efficient when memory is reused, and a double-ended stack can optimize tasks that require access from both ends.
Understanding the types of stacks and their use cases can help you make better decisions and build more optimized solutions when performing stack operations in C.
A real-life example of a stack is the browser history. When you visit websites, your browser stores the pages in a stack. The most recent page is always on top. If you press the "Back" button, the top page is popped off the stack, and the browser takes you to the previous page.
For this example, we will implement a simple stack that mimics this functionality with basic stack operations like push(), pop(), top(), isEmpty(), and isFull().
Let’s look at the algorithm:
Let’s see how the code works out:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Define a node structure for the stack
struct Node {
char url[100]; // Holds the website address
struct Node* next; // Points to the next node in the stack
};
// Define the top of the stack (initially NULL)
struct Node* top = NULL;
// Push operation to insert a page (URL) at the top of the stack
void push(char url[]) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node)); // Allocate memory for a new node
if (newNode == NULL) {
printf("Memory overflow\n");
return;
}
strcpy(newNode->url, url); // Copy the URL into the new node's data field
newNode->next = top; // Link the new node to the current top node
top = newNode; // Move the top pointer to the new node
printf("Visited: %s\n", url); // Print the visited page URL
}
// Pop operation to remove the top page from the stack (back functionality)
void pop() {
if (top == NULL) { // Check if the stack is empty (no pages to go back to)
printf("No history to go back\n");
return;
}
struct Node* temp = top; // Temporarily store the top node
printf("Going back to: %s\n", temp->url); // Print the page we are going back to
top = top->next; // Move the top pointer to the next node
free(temp); // Free the memory of the old top node
}
// Top operation to view the current page (without popping it)
void topPage() {
if (top == NULL) {
printf("No page in history\n");
return;
}
printf("Currently on: %s\n", top->url); // Print the current top page URL
}
// Check if the stack is empty
int isEmpty() {
return top == NULL;
}
// Main function to simulate stack operations (browser history)
int main() {
push("www.google.com"); // Visit Google
push("www.youtube.com"); // Visit YouTube
push("www.stackoverflow.com"); // Visit StackOverflow
topPage(); // Check the current page
pop(); // Go back to previous page
pop(); // Go back to previous page
topPage(); // Check the current page after going back
return 0;
}
Output:
Visited: www.google.com
Visited: www.youtube.com
Visited: www.stackoverflow.com
Currently on: www.stackoverflow.com
Going back to: www.stackoverflow.com
Going back to: www.youtube.com
Currently on: www.google.com
Explanation:
In this example, we simulate the browser's "Back" functionality using stack operations in C. The stack holds a history of pages, and you can easily go back through the pages as needed using the pop() operation.
1. What is the condition to check for stack overflow in an array-based stack?
a) top < 0
b) top == 0
c) top == MAX - 1
d) top != -1
2. What does the pop() operation do in a stack?
a) Removes top element
b) Adds element to the bottom
c) Sorts the stack
d) Returns the middle element
3. What is the time complexity of push() and pop() in a stack using an array?
a) O(n)
b) O(log n)
c) O(1)
d) O(n²)
4. In an array-based stack, which variable always tracks the topmost element?
a) size
b) count
c) head
d) top
5. What will happen if you pop from an empty stack?
a) Returns garbage value
b) Stack resets
c) Underflow error
d) Overflow error
6. Which of the following correctly implements the push() operation?
a) stack[++top] = value;
b) stack[top++] = value;
c) stack[top] = value; top++;
d) All of the above can work
7. Which of the following is not a valid application of stacks?
a) Expression evaluation
b) Backtracking
c) Page navigation in browser
d) Level-order traversal of a tree
8. What is the output of this stack-based code snippet?
int stack[3], top = -1;
stack[++top] = 10;
stack[++top] = 20;
stack[++top] = 30;
printf("%d", stack[top--]);
a) 30
b) 20
c) 10
d) Stack Overflow
9. A student implements a stack using array, but data overwrites on push(). What is likely missing?
a) MAX check for overflow
b) Memory allocation
c) Decrement of `top` in `pop()`
d) Stack header file
10. You’re designing a stack-based undo feature. Why is stack preferred here?
a) LIFO matches last action undo
b) Easy to loop through
c) Fast insertions
d) It sorts operations
11. You use `top = -1` to initialize your stack. Why?
a) Indexing from 0
b) To avoid garbage values
c) It simplifies boundary checks
d) All of the above
In C programming, a stack is a versatile data structure that allows efficient data management through the Last-In, First-Out (LIFO) principle. Understanding stack operations in C, such as push, pop, top, isEmpty, and isFull, is essential for implementing algorithms, managing recursion, and handling real-world scenarios like browser history or undo functionalities.
Learning these operations ensures optimized performance and memory usage. By practicing the implementation of a stack in C, you develop the skills to manage sequential data effectively and solve complex programming problems efficiently.
upGrad’s courses offer a comprehensive, hands-on approach to mastering C programming, including concepts like stacks, recursion, and data structures. Whether you’re learning how stacks operate or optimizing your stack-based algorithms, upGrad’s expert-led courses will guide you step-by-step through key C programming concepts.
Through our interactive courses, you'll gain practical experience working with stacks, from implementing basic operations to tackling more advanced applications in system-level programming and competitive coding.
Check out upGrad’s programs to advance your knowledge:
You can also get personalized career counseling with upGrad to guide your career path, or visit your nearest upGrad center and start hands-on training today!
Similar Reads:
Explore C Tutorials: From Beginner Concepts to Advanced Techniques
Array in C: Introduction, Declaration, Initialisation and More
Exploring Array of Pointers in C: A Beginner's Guide
What is C Function Call Stack: A Complete Tutorial
Binary Search in C
Constant Pointer in C: The Fundamentals and Best Practices
Find Out About Data Structures in C and How to Use Them?
A stack in C program follows the LIFO (Last-In, First-Out) principle, providing efficient access to the most recent element. This makes it ideal for use cases like recursion, undo functionalities, expression evaluation, and tracking program execution states. Its simplicity and predictable behavior make it a preferred choice for certain algorithmic operations.
Stack operations like push() and pop() in C typically operate in constant time O(1). This makes them highly efficient for managing data in a LIFO structure. The top() or peek() operation is also O(1), allowing quick access to the most recent element without removing it. Efficiency remains consistent regardless of stack size.
Yes, stack operations in C can be implemented statically using arrays. A fixed-size array provides predictable memory usage and simple implementation. However, dynamic memory allocation using linked lists offers flexibility and scalability, as the stack can grow or shrink as needed without predefined limits. Each approach has its trade-offs between simplicity and adaptability.
Stacks are widely used in real-life programming scenarios. Common examples include managing browser history, implementing undo features in text editors, evaluating mathematical expressions, handling recursive function calls, and parsing syntax in compilers. Their LIFO behavior allows applications to easily retrieve the most recent action or element first.
In dynamic stack implementations using linked lists, memory is allocated for each node when added (push()) and freed when removed (pop()). This ensures efficient memory usage, preventing wastage. For static stacks (arrays), memory is preallocated, which can lead to unused space if the stack is not full, but avoids runtime memory management overhead.
Popping from an empty stack results in a stack underflow error. This can cause program crashes or unexpected behavior if not handled. To prevent this, always check whether the stack is empty using isEmpty() before performing a pop operation. Proper error handling ensures the program remains safe and predictable.
Stack overflow occurs when attempting to push an element onto a full stack, exceeding its capacity. Stack underflow happens when trying to pop an element from an empty stack. Both conditions can cause errors, and it’s important to implement proper checks to prevent memory corruption or crashes.
Using arrays for stacks requires a fixed size defined at compile time. This limits flexibility and can lead to memory wastage if the array is larger than needed or stack overflow if the array is too small. Dynamic implementations with linked lists are more flexible but slightly more complex to manage.
The top() operation, also called peek(), returns the value at the top of the stack without removing it. This allows programs to view the most recent element efficiently. It’s an O(1) operation and is particularly useful for decision-making in algorithms where the last inserted element’s value is required without modifying the stack.
Yes, stack operations can be implemented using recursion. For example, recursive function calls naturally use the program’s call stack. Custom recursive stack functions can also simulate push and pop behaviors. However, recursive stack implementations need careful handling of stack overflow and memory limits, especially for deep recursion.
Checking for stack overflow prevents attempts to push onto a full stack. Without this check, memory corruption, crashes, or unexpected behavior may occur. In static stacks, this involves comparing the current size with the maximum limit. In dynamic stacks, proper memory allocation checks prevent runtime errors and maintain program stability.
A static stack uses a fixed-size array and cannot grow beyond its predefined size. It’s simple and fast but lacks flexibility. A dynamic stack uses a linked list, allowing flexible growth and shrinkage as needed. Dynamic stacks avoid memory wastage but require pointer management, making implementation slightly more complex.
Stacks can evaluate expressions like postfix, prefix, or infix notations. Operands are pushed onto the stack, and operators pop operands to perform calculations. This LIFO behavior ensures the correct order of operations is maintained, simplifying expression evaluation in compilers and calculators.
A circular stack connects the end of the stack to the beginning, effectively reusing memory space. It’s particularly useful for fixed-size buffers or applications where stack overflow must be minimized, such as embedded systems or real-time task scheduling. It optimizes memory but adds implementation complexity.
A double-ended stack, or deque, allows insertion and removal of elements from both ends. This provides more flexibility than a standard LIFO stack. Deques are used in scheduling, multi-threaded task management, or any scenario requiring efficient access to both the newest and oldest elements.
Yes, multi-stack implementations allow multiple stacks to share the same array or linked list. This is useful for memory optimization in systems with limited space. Each stack maintains its own top pointer or index. However, managing resizing and ensuring no overlap adds complexity to the implementation.
A persistent stack preserves previous states, enabling undo operations or historical data access. Each push or pop operation creates a new state while maintaining prior states. It’s commonly used in text editors, version control systems, and applications where history tracking is critical.
Browser history is implemented as a dynamic stack. Each visited URL is pushed onto the stack. Pressing the back button triggers a pop operation, removing the top URL and showing the previous one. top() allows viewing the current page. This simulates real-world undo and navigation functionalities efficiently.
Always check for overflow and underflow conditions to prevent crashes. Choose the right stack type (static vs dynamic) based on memory and performance needs. Free dynamically allocated memory properly and avoid pointer errors. Ensure operations maintain LIFO behavior to preserve stack integrity.
Recursion inherently uses the call stack, which operates as a stack in C. Each function call is pushed onto the call stack, and when the function completes, it’s popped. Understanding stack operations helps manage recursion depth, prevent stack overflow, and implement custom recursive algorithms efficiently.
Take a Free C Programming Quiz
Answer quick questions and assess your C programming knowledge
Author|900 articles published
Talk to our experts. We are available 7 days a week, 9 AM to 12 AM (midnight)
Indian Nationals
Foreign Nationals
The above statistics depend on various factors and individual results may vary. Past performance is no guarantee of future results.
The student assumes full responsibility for all expenses associated with visas, travel, & related costs. upGrad does not .
Recommended Programs