Infix to Postfix Program in C

A complete, compilable C program that converts an infix expression to postfix using a stack — with the precedence and associativity rules handled correctly, including the two cases most classroom versions get wrong.

The complete C program

Save this as infix_to_postfix.c. It is standard C99 with no external libraries, it compiles clean under -Wall -Wextra, and it handles single-character operands, all six operators and nested brackets.

infix_to_postfix.c
/* infix_to_postfix.c — convert an infix expression to postfix using a stack */

#include <stdio.h>
#include <string.h>
#include <ctype.h>

#define MAX 100

char stack[MAX];
int  top = -1;

void push(char c)  { if (top < MAX - 1) stack[++top] = c; }
char pop(void)     { return (top < 0) ? '\0' : stack[top--]; }
char peek(void)    { return (top < 0) ? '\0' : stack[top]; }
int  isEmpty(void) { return top == -1; }
void resetStack(void) { top = -1; }

/* Higher number = binds more tightly. 0 means "not an operator". */
int precedence(char op)
{
    switch (op) {
        case '^':              return 3;
        case '*': case '/':
        case '%':              return 2;
        case '+': case '-':    return 1;
        default:               return 0;
    }
}

/* Only exponentiation groups right to left. */
int isRightAssociative(char op) { return op == '^'; }

/* Returns 1 on success, 0 if the expression is invalid. */
int infixToPostfix(const char *infix, char *postfix)
{
    int i, j = 0;
    resetStack();

    for (i = 0; infix[i] != '\0'; i++) {
        char c = infix[i];

        if (isspace((unsigned char)c))
            continue;

        /* 1. Operand: straight to the output. */
        if (isalnum((unsigned char)c)) {
            postfix[j++] = c;
        }
        /* 2. Opening bracket: push as a marker. */
        else if (c == '(') {
            push(c);
        }
        /* 3. Closing bracket: pop until the matching '('. */
        else if (c == ')') {
            while (!isEmpty() && peek() != '(')
                postfix[j++] = pop();
            if (isEmpty()) {
                printf("Error: unbalanced brackets - extra ')'\n");
                return 0;
            }
            pop();                       /* discard the '(' */
        }
        /* 4. Operator: pop everything that outranks it, then push. */
        else if (precedence(c) > 0) {
            while (!isEmpty() && peek() != '(' &&
                   (precedence(peek()) >  precedence(c) ||
                   (precedence(peek()) == precedence(c) && !isRightAssociative(c))))
                postfix[j++] = pop();
            push(c);
        }
        else {
            printf("Error: invalid character '%c'\n", c);
            return 0;
        }
    }

    /* 5. Flush whatever is left on the stack. */
    while (!isEmpty()) {
        char t = pop();
        if (t == '(') {
            printf("Error: unbalanced brackets - missing ')'\n");
            return 0;
        }
        postfix[j++] = t;
    }

    postfix[j] = '\0';
    return 1;
}

int main(void)
{
    char infix[MAX], postfix[MAX];

    printf("Enter an infix expression: ");
    if (fgets(infix, sizeof infix, stdin) == NULL)
        return 1;
    infix[strcspn(infix, "\n")] = '\0';   /* strip the newline */

    if (infixToPostfix(infix, postfix))
        printf("Postfix expression: %s\n", postfix);

    return 0;
}

Compile and run it

gcc -Wall -Wextra -std=c99 -o infix_to_postfix infix_to_postfix.c
./infix_to_postfix

On Windows with MinGW the command is the same but the output file needs the extension: gcc -o infix_to_postfix.exe infix_to_postfix.c, then run infix_to_postfix.exe. In Turbo C or an online compiler, just paste and press run — nothing in the program is platform specific.

Sample output

Enter an infix expression: A+B*C
Postfix expression: ABC*+

Enter an infix expression: (A+B)*C-D
Postfix expression: AB+C*D-

Enter an infix expression: K+L-M*N+(O^P)*W/U/V*T+Q
Postfix expression: KL+MN*-OP^W*U/V/T*+Q+

Enter an infix expression: (A+B
Error: unbalanced brackets - missing ')'

Those are the real outputs of the program above. Paste any of the same expressions into the converter on the homepage to see the stack trace that produced them.

How the program works

The program is four small stack functions, one precedence function and one conversion loop. Nothing else.

Functions in the infix to postfix C program
Function Purpose
push(c)Adds an operator or ( to the top of the stack, guarding against overflow
pop()Removes and returns the top, or '\0' when the stack is empty
peek()Reads the top without removing it, so precedence can be compared first
isEmpty()Guards every peek and pop
resetStack()Clears the stack before a conversion, so the function can be called more than once
precedence(op)Returns 3, 2, 1 or 0 — and the 0 doubles as an "is this an operator?" test
isRightAssociative(op)True only for ^
infixToPostfix()The single left-to-right scan that applies the five rules

The conversion loop mirrors the pseudocode line for line. isalnum() from <ctype.h> identifies operands, isspace() lets you type spaces in the input, and strcspn(infix, "\n") strips the newline that fgets leaves behind.

An array stack with an integer top index, as used in the C programchar stack[MAX]; int top;top = 2+0(1/23456push → stack[++top] = cpop → return stack[top--]empty → top == -1
The array stack the program uses. top is an index, not a pointer: it starts at -1 for an empty stack and always names the cell holding the most recently pushed operator.
Why fgets and not scanf

scanf("%s", infix) stops at the first space, so A + B would only read A. fgets reads the whole line and takes a buffer size, so it cannot overflow. It is the right choice in every program that reads a line of text.

The precedence function

This is where the two classic bugs live, so it is worth looking at on its own.

the two rules that matter
while (!isEmpty() && peek() != '(' &&
       (precedence(peek()) >  precedence(c) ||
       (precedence(peek()) == precedence(c) && !isRightAssociative(c))))
    postfix[j++] = pop();
push(c);
  • The == clause. Equal precedence must also pop, for left-associative operators. Drop it and A-B-C becomes ABC--, which means A-(B-C).
  • The !isRightAssociative(c) guard. Without it, a^b^c becomes ab^c^ instead of abc^^.
  • The peek() != '(' guard. An opening bracket is a floor — popping must never go past it.

Multi-digit numbers and variable names

The program above stores one character per operand, so 12+3 converts to 123+ — the digits run together and the result is unreadable. This version copies whole operands and separates the output with spaces, which is what you want for anything beyond a textbook exercise.

infix_to_postfix_multichar.c
/* Handles multi-digit numbers and multi-letter variable names. */

#include <stdio.h>
#include <string.h>
#include <ctype.h>

#define MAX 256

char stack[MAX];
int  top = -1;

void push(char c)  { if (top < MAX - 1) stack[++top] = c; }
char pop(void)     { return (top < 0) ? '\0' : stack[top--]; }
char peek(void)    { return (top < 0) ? '\0' : stack[top]; }
int  isEmpty(void) { return top == -1; }
void resetStack(void) { top = -1; }

int precedence(char op)
{
    switch (op) {
        case '^':                       return 3;
        case '*': case '/': case '%':   return 2;
        case '+': case '-':             return 1;
        default:                        return 0;
    }
}

int isRightAssociative(char op) { return op == '^'; }

int infixToPostfix(const char *infix, char *postfix)
{
    int i = 0, j = 0;
    resetStack();

    while (infix[i] != '\0') {
        char c = infix[i];

        if (isspace((unsigned char)c)) { i++; continue; }

        /* Copy a whole operand - "rate", "3.5", "250" - then one space. */
        if (isalnum((unsigned char)c) || c == '_' || c == '.') {
            while (infix[i] != '\0' &&
                   (isalnum((unsigned char)infix[i]) || infix[i] == '_' || infix[i] == '.'))
                postfix[j++] = infix[i++];
            postfix[j++] = ' ';
            continue;
        }

        if (c == '(') {
            push(c);
        }
        else if (c == ')') {
            while (!isEmpty() && peek() != '(') {
                postfix[j++] = pop();
                postfix[j++] = ' ';
            }
            if (isEmpty()) {
                printf("Error: unbalanced brackets - extra ')'\n");
                return 0;
            }
            pop();
        }
        else if (precedence(c) > 0) {
            while (!isEmpty() && peek() != '(' &&
                   (precedence(peek()) >  precedence(c) ||
                   (precedence(peek()) == precedence(c) && !isRightAssociative(c)))) {
                postfix[j++] = pop();
                postfix[j++] = ' ';
            }
            push(c);
        }
        else {
            printf("Error: invalid character '%c'\n", c);
            return 0;
        }
        i++;
    }

    while (!isEmpty()) {
        char t = pop();
        if (t == '(') {
            printf("Error: unbalanced brackets - missing ')'\n");
            return 0;
        }
        postfix[j++] = t;
        postfix[j++] = ' ';
    }

    if (j > 0 && postfix[j - 1] == ' ') j--;   /* drop the trailing space */
    postfix[j] = '\0';
    return 1;
}

int main(void)
{
    char infix[MAX], postfix[MAX * 2];

    printf("Enter an infix expression: ");
    if (fgets(infix, sizeof infix, stdin) == NULL)
        return 1;
    infix[strcspn(infix, "\n")] = '\0';

    if (infixToPostfix(infix, postfix))
        printf("Postfix expression: %s\n", postfix);

    return 0;
}
Output of the multi-character version
InputOutput
rate * 12 + 250rate 12 * 250 +
(price + tax) * qtyprice tax + qty *
3.5 * 2 + 13.5 2 * 1 +
x1 + y2 * z3x1 y2 z3 * +

The only structural change is the operand branch: instead of copying one character it runs a small inner loop that copies the entire run of letters, digits, underscores and dots, then writes a single separating space. Every pop writes a space too, and the trailing one is trimmed at the end.

Using a linked-list stack

If your assignment asks for a dynamic stack rather than a fixed array, replace the five stack functions with these. The names and signatures are unchanged, so infixToPostfix() itself needs no edits at all — only <stdlib.h> has to be included.

linked_stack.c
/* Drop-in replacement: a linked-list stack instead of a fixed array.
   The four functions keep the same names and signatures, so the rest of
   infixToPostfix() does not change at all. */

#include <stdlib.h>

typedef struct Node {
    char         data;
    struct Node *next;
} Node;

Node *head = NULL;                       /* NULL means "empty stack" */

void push(char c)
{
    Node *n = (Node *)malloc(sizeof(Node));
    if (n == NULL) return;               /* out of memory */
    n->data = c;
    n->next = head;
    head = n;
}

char pop(void)
{
    Node *n;
    char  c;
    if (head == NULL) return '\0';
    n = head;
    c = n->data;
    head = n->next;
    free(n);
    return c;
}

char peek(void)    { return (head == NULL) ? '\0' : head->data; }
int  isEmpty(void) { return head == NULL; }

/* Free every remaining node so the stack can be reused without leaking. */
void resetStack(void)
{
    while (head != NULL) pop();
}
This is why resetStack() exists

A version that writes top = -1; directly inside infixToPostfix() will not compile once the array is gone — there is no top any more. Hiding that one line behind resetStack() is what makes the two stacks genuinely interchangeable, and in the linked-list version it also frees the leftover nodes instead of leaking them.

Bugs to watch for

Common problems in an infix to postfix C program
Symptom Cause Fix
A-B-C gives ABC-- Equal precedence pushes instead of popping Add the == clause to the while condition
a^b^c gives ab^c^ ^ treated as left associative Add the !isRightAssociative(c) guard
Only the first character is read scanf("%s", …) stops at whitespace Use fgets and strip the newline
A stray character at the end of the output The newline from fgets was never removed infix[strcspn(infix, "\n")] = '\0';
Segfault on a long expression MAX too small, or no bounds check in push Size the stack at the input length and guard push
Garbage output on unbalanced input A ( gets flushed into the output at the end Check for '(' in the final pop loop and report an error
12+3 gives 123+ One character per operand Use the multi-character version

Frequently asked questions

How do you write an infix to postfix program in C?

Implement a character stack with push, pop, peek and isEmpty, plus a precedence() function returning 3 for ^, 2 for * / % and 1 for + -. Then scan the input string once: copy operands to the output, push (, pop to the output until ( on a ), and for an operator pop while the top outranks it before pushing. Finally pop whatever is left on the stack.

Which header files does the program need?

<stdio.h> for printf and fgets, <string.h> for strcspn, and <ctype.h> for isalnum and isspace. The linked-list version also needs <stdlib.h> for malloc and free.

Why does my program print 123+ for 12+3?

Because it stores one character per operand, so the digits 1 and 2 are written separately and nothing separates them from the 3. Copy the whole run of alphanumeric characters as a single operand and write a space after each one — that is what the multi-character version does.

How do I handle the ^ operator correctly in C?

Give it precedence 3 and mark it right associative. In the popping condition, an incoming right-associative operator must not pop an operator of equal precedence — that single guard is the difference between abc^^ (correct) and ab^c^ (wrong). Note that ^ here is a notation for exponentiation, not C's bitwise XOR.

How large should the stack array be?

The length of the input expression is always sufficient, because in the worst case every token is on the stack at once. Sizing the stack and the input buffer to the same MAX is the simplest safe choice, and push should still bounds-check.

Can the same program evaluate the postfix expression too?

Yes, with a second stack that holds values instead of characters. Scan the postfix output left to right: push each operand, and on each operator pop two values, apply it, and push the result. One value is left at the end and that is the answer. The converter on this site does exactly this whenever every operand is numeric.