Implementing Stacks in C: A Step-by-Step Tutorial


5 min read 15-11-2024
Implementing Stacks in C: A Step-by-Step Tutorial

Stacks are an essential data structure in programming, well-known for their Last In, First Out (LIFO) behavior. They're prevalent in various applications like expression parsing, backtracking algorithms, and memory management. Understanding how to implement a stack can greatly enhance your programming skills, especially in languages like C, where manual memory management is a significant part of programming.

In this comprehensive guide, we’ll delve deep into the implementation of stacks in C. By the end of this article, you’ll not only have a clear understanding of what a stack is, but you’ll also know how to build one from scratch.

What is a Stack?

A stack is a collection of elements, where the last element added is the first one to be removed. This principle is similar to a stack of plates; you add new plates to the top and remove them from the top as well.

Basic Operations of a Stack

Before diving into the implementation, let's understand the core operations associated with stacks:

  1. Push: This operation adds an element to the top of the stack.
  2. Pop: This operation removes the top element from the stack.
  3. Peek (or Top): This operation returns the top element without removing it.
  4. IsEmpty: This checks if the stack is empty.
  5. Size: This returns the number of elements in the stack.

Use Cases of Stacks

Stacks are used in numerous applications, such as:

  • Function Call Management: Keeping track of active functions in programming languages through a call stack.
  • Expression Evaluation: Evaluating and parsing expressions in compilers.
  • Undo Mechanism in Applications: Maintaining a history of changes for undo operations.

Implementing a Stack in C

Now, let's move on to the step-by-step implementation of a stack in C. We’ll create a stack that can hold integer values. The basic structure of our stack will involve an array to store elements and a top index to track the top of the stack.

Step 1: Define the Stack Structure

First, we need to define a structure for our stack.

#define MAX 100 // Maximum size of the stack

typedef struct Stack {
    int arr[MAX]; // Array to hold the elements
    int top; // Index of the top element
} Stack;

Step 2: Initialize the Stack

We need a function to initialize our stack, setting the top index to -1 (indicating that the stack is empty).

void initStack(Stack *stack) {
    stack->top = -1; // Stack is initially empty
}

Step 3: Implement Push Operation

The push operation will add an element to the top of the stack. It is crucial to check if the stack is full before adding an element.

int push(Stack *stack, int value) {
    if (stack->top >= MAX - 1) {
        printf("Stack Overflow! Cannot push %d.\n", value);
        return -1; // Stack is full
    }
    stack->arr[++stack->top] = value; // Increment top and insert the value
    return 0; // Successful push
}

Step 4: Implement Pop Operation

The pop operation will remove and return the top element of the stack. We also need to check if the stack is empty before attempting to pop.

int pop(Stack *stack) {
    if (stack->top < 0) {
        printf("Stack Underflow! Cannot pop from an empty stack.\n");
        return -1; // Stack is empty
    }
    return stack->arr[stack->top--]; // Return the top value and decrement top
}

Step 5: Implement Peek Operation

The peek operation will allow us to see the top element without removing it.

int peek(Stack *stack) {
    if (stack->top < 0) {
        printf("Stack is empty! Cannot peek.\n");
        return -1; // Stack is empty
    }
    return stack->arr[stack->top]; // Return the top element
}

Step 6: Implement IsEmpty Function

The isEmpty function will check whether the stack is empty.

int isEmpty(Stack *stack) {
    return stack->top < 0; // Returns 1 if empty, else 0
}

Step 7: Implement Size Function

Lastly, let’s create a function to get the current size of the stack.

int size(Stack *stack) {
    return stack->top + 1; // The number of elements in the stack
}

Putting It All Together

Now that we have all the essential functions implemented, let’s compile our stack functions into a single program.

#include <stdio.h>
#include <stdlib.h>

#define MAX 100 // Maximum size of the stack

typedef struct Stack {
    int arr[MAX];
    int top;
} Stack;

void initStack(Stack *stack) {
    stack->top = -1;
}

int push(Stack *stack, int value) {
    if (stack->top >= MAX - 1) {
        printf("Stack Overflow! Cannot push %d.\n", value);
        return -1;
    }
    stack->arr[++stack->top] = value;
    return 0;
}

int pop(Stack *stack) {
    if (stack->top < 0) {
        printf("Stack Underflow! Cannot pop from an empty stack.\n");
        return -1;
    }
    return stack->arr[stack->top--];
}

int peek(Stack *stack) {
    if (stack->top < 0) {
        printf("Stack is empty! Cannot peek.\n");
        return -1;
    }
    return stack->arr[stack->top];
}

int isEmpty(Stack *stack) {
    return stack->top < 0;
}

int size(Stack *stack) {
    return stack->top + 1;
}

int main() {
    Stack stack;
    initStack(&stack);
    
    push(&stack, 10);
    push(&stack, 20);
    push(&stack, 30);

    printf("Top element is: %d\n", peek(&stack));
    printf("Stack size is: %d\n", size(&stack));

    printf("Popped element is: %d\n", pop(&stack));
    printf("Top element after pop is: %d\n", peek(&stack));

    return 0;
}

Explanation of the Program

In the above program:

  • We define the stack structure and various operations.
  • The main function initializes the stack, performs several push operations, displays the top element, checks the stack size, and then pops an element.
  • Error handling is included for stack overflow and underflow conditions.

Complexity Analysis

Time Complexity

The time complexity of all the basic stack operations (push, pop, peek) is O(1). This means they take constant time regardless of the stack size.

Space Complexity

The space complexity of the stack implementation is O(N), where N is the maximum capacity of the stack defined by the MAX constant.

Conclusion

In this tutorial, we have explored the fundamental concepts of stacks and implemented them in C. Stacks are a vital data structure that aids in solving various problems in programming.

By mastering the stack implementation, you will be well-equipped to handle more complex data structures and algorithms. Practicing with stacks will enhance your understanding of memory management and algorithm efficiency.

Now that you know how to implement a stack in C, experiment with modifying the stack operations or try implementing a dynamic stack using linked lists for further understanding.

FAQs

Q1: What are the limitations of an array-based stack?
A1: The main limitation is a fixed size; once it reaches its capacity, no further elements can be pushed (stack overflow).

Q2: Can I implement a stack using a linked list?
A2: Yes, a stack can be efficiently implemented using a linked list, allowing for dynamic memory allocation and no fixed size limitations.

Q3: What happens during stack overflow and underflow?
A3: Stack overflow occurs when trying to push an element onto a full stack, while stack underflow occurs when trying to pop from an empty stack.

Q4: How can I use a stack in expression evaluation?
A4: Stacks can hold operands and operators while parsing expressions, helping to evaluate expressions based on operator precedence.

Q5: Are there any real-world applications of stacks?
A5: Yes, stacks are used in web browsers for managing history, in programming languages for function calls, and in various algorithms like depth-first search.


This article has provided you with an extensive understanding of stack implementation in C. Happy coding!