What a stack is actually for
Checking whether brackets are balanced is the clearest demonstration of why LIFO order matters — the most recent open bracket must be the next one closed.
After this lesson
You should be able to
- Use a stack to check whether brackets in an expression are balanced.
- Explain why a counter alone cannot detect mismatched bracket types.
- Trace postfix expression evaluation using a stack.
Try it before you read
Try it live
Stack is empty — top = -1. pop() has nothing to remove.
Why a simple counter is not enough
A first instinct is to count opening and closing brackets and check the counts match. That misses ([)] — three opens, three closes, counts equal, yet clearly wrong, because the ] closes before the ( that is still open.
What actually matters is order, specifically nesting order — and nesting is exactly what a stack tracks. The most recently opened bracket must be the next one closed, which is LIFO order stated in different words.
Push on open, match on close
Scan the expression once. On an opening bracket, push it. On a closing bracket, pop and check that it matches the type just popped — a ) must match an opening ( that came off the top, not an opening [ or {.
Two failure conditions, checked in order: popping from an empty stack means a closing bracket had no opener — too many closes. A non-empty stack when the string ends means an opener was never closed — too many opens.
int is_balanced(const char *expr)
{
char data[100];
int top = -1;
for (int i = 0; expr[i] != '\0'; i++) {
char c = expr[i];
if (c == '(' || c == '[' || c == '{') {
data[++top] = c;
} else if (c == ')' || c == ']' || c == '}') {
if (top == -1) return 0; /* too many closes */
char open = data[top--];
if ((c == ')' && open != '(') ||
(c == ']' && open != '[') ||
(c == '}' && open != '{'))
return 0; /* wrong type */
}
}
return top == -1; /* every opener must have been matched */
}Postfix evaluation: the other classic use
In postfix notation, operators come after their operands: 3 4 + means 3 + 4. Evaluating it needs a stack too: push numbers as you meet them, and on an operator, pop twice, apply the operator, and push the result back.
Trace 3 4 + 5 * by hand: push 3, push 4, see +, pop 4 and 3, push 7, push 5, see *, pop 5 and 7, push 35. One value remains on the stack at the end — the answer.
Try it yourself
Extend the balance checker into a full program that reads an expression and reports exactly which character caused the failure, if any.
Need a hint?
Return the index of the failure instead of a plain 0 or 1, and use a sentinel like -1 for success.
Check the worked solution
Returning an index rather than a boolean means the two failure paths — too many closes and a mismatched type — must each report exactly where in the string they happened, which is more useful than the plain yes/no from the lesson's version. Every opener still needing a matching closer at the end is reported by whatever position the loop had reached, not a bracket position, since nothing in the string itself marked the failure.
#include <stdio.h>
int check_balance(const char *expr)
{
char data[100];
int top = -1;
for (int i = 0; expr[i] != '\0'; i++) {
char c = expr[i];
if (c == '(' || c == '[' || c == '{') {
data[++top] = c;
} else if (c == ')' || c == ']' || c == '}') {
if (top == -1) return i;
char open = data[top--];
if ((c == ')' && open != '(') ||
(c == ']' && open != '[') ||
(c == '}' && open != '{'))
return i;
}
}
return top == -1 ? -1 : (int) top;
}
int main(void)
{
char expr[100];
printf("Enter an expression: ");
scanf("%s", expr);
int result = check_balance(expr);
if (result == -1)
printf("Balanced\n");
else
printf("Unbalanced near position %d\n", result);
return 0;
}Quick check
Why does a plain count of opening and closing brackets fail to validate "([)]"?
Why this lesson exists
Syllabus mapping
Stack applications
Maps to course outcomes CO1, CO3, CO4.