THE SYLLABUS TASK
What you need to solve
Write a program to implement stack and its operations using arrays.
Use the command table below; integer values are in −1,000,000 to 1,000,000. End with 0. Array capacity is 5; change CAPACITY to alter it.
UNDERSTAND THE IDEA
Explanation
A stack follows LIFO: last in, first out. If 10 is pushed and then 20, popping removes 20 first. Push adds at the top, pop removes the top, and peek reads the top without changing the stack.
The array starts with top = −1. Push checks capacity and increments top before storing. Pop checks for emptiness, reads stack[top], then decrements top. Display walks indices backward.
CAPACITY is 5 for a small, traceable demonstration. Increase the macro to allow more items. Overflow is a full stack; underflow is attempting to pop an empty stack.
PLAN BEFORE CODING
Algorithm
- Initialize the empty stack.
- Read one command and its required value.
- Check empty/full/allocation conditions before changing storage.
- Call the operation function; print its result.
- Display in LIFO order for stack or FIFO order for queue.
- Repeat until 0; clean up allocated nodes when present.
CONTROL THE PROGRAM
Commands and input
A command is an operation number followed by its arguments. Whitespace separates tokens; line breaks make your input easier to read. No interactive menu prompts are printed.
| Input | Meaning |
|---|---|
1 value | Push a value onto the top. |
2 | Pop and print the top value. |
3 | Peek at the top without removing it. |
4 | Display from top to bottom. |
0 | Exit; release nodes in the ADT version. |
SEE THE CONTROL FLOW
Flowcharts
Follow arrows from Start. Diamonds are decisions; Yes/No labels select a branch. Returning arrows repeat a loop. Function steps group the pointer/index updates explained in the code. Expand the operation charts for more detail. On a phone, scroll sideways within a diagram.
Program command loop
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
/* Read a complete integer token; reject bad or oversized input safely. */
int readInt(int *value) {
char token[64];
int ch;
size_t used = 0;
while ((ch = getchar()) != EOF && isspace((unsigned char)ch)) { }
if (ch == EOF) return 0;
do {
if (used < sizeof token - 1) token[used++] = (char)ch;
else {
while ((ch = getchar()) != EOF && !isspace((unsigned char)ch)) { }
return 0;
}
ch = getchar();
} while (ch != EOF && !isspace((unsigned char)ch));
token[used] = '\0';
char *end;
errno = 0;
long result = strtol(token, &end, 10);
if (errno || *end != '\0' || result < -1000000 || result > 1000000) return 0;
*value = (int)result;
return 1;
}
#define CAPACITY 5
int push(int stack[], int *top, int value) {
if (*top == CAPACITY - 1) return 0;
stack[++(*top)] = value;
return 1;
}
int pop(int stack[], int *top, int *value) {
if (*top == -1) return 0;
*value = stack[(*top)--];
return 1;
}
void display(const int stack[], int top) {
printf("Stack (top to bottom):");
for (int i = top; i >= 0; --i) printf(" %d", stack[i]);
putchar('\n');
}
/* 1 value: push; 2: pop; 3: peek; 4: display; 0: exit. */
int main(void) {
int stack[CAPACITY], top = -1, command, value;
while (readInt(&command)) {
if (command == 0) return 0;
if (command == 1) {
if (!readInt(&value)) break;
puts(push(stack, &top, value) ? "Pushed." : "Overflow: stack is full.");
} else if (command == 2) {
if (pop(stack, &top, &value)) printf("Popped: %d\n", value);
else puts("Underflow: stack is empty.");
} else if (command == 3) {
if (top >= 0) printf("Top: %d\n", stack[top]);
else puts("Stack is empty.");
} else if (command == 4) display(stack, top);
else break;
}
puts("Invalid or incomplete input. End with command 0.");
return 1;
}
Code loads into your existing compiler. Enter the sample input there; sign-in and execution rules stay the same.
Compile and run locally
gcc -std=c17 stack-array.c -o lab
./labOn Windows, run .\lab.exe after compiling with GCC.
UNDERSTAND THE CODE
Line-by-line explanation
These steps follow the exact source above, including its input helper, operations, command handling and cleanup. Line numbers count blank lines; related statements are grouped. Start with main to follow execution, then open the functions it calls.
Start with main → · Data structure and operations →
Line 1Include terminal input and output
#include <stdio.h>stdio.h declares printf, puts, getchar and putchar. The input helper reads characters with getchar; the main program prints operation results using the output functions.
Line 2Include conversion and memory utilities
#include <stdlib.h>stdlib.h declares strtol for converting an integer token. It also declares malloc and free, which the linked structures and tree programs use to obtain and release node storage.
Line 3Include string and memory utilities
#include <string.h>string.h declares utilities such as memset. It is part of the shared header list; merely including a header does not call any function or change stored values.
Lines 4–5Include character tests and conversion error reporting
#include <ctype.h>
#include <errno.h>ctype.h supplies isspace for recognising whitespace. errno.h supplies errno so the program can detect a range error reported by strtol.
Lines 7–8Define a checked integer-input helper
/* Read a complete integer token; reject bad or oversized input safely. */
int readInt(int *value) {The comment states the purpose of readInt. Its int *value parameter receives the address of a caller variable. The helper returns 1 after storing a valid value there, or 0 on failure; these flags are separate from main’s process exit status.
Lines 9–11Reserve a token buffer and its reading state
char token[64];
int ch;
size_t used = 0;token holds up to 63 input characters and a null terminator. ch is int so it can distinguish character input from EOF. used starts at zero and counts the stored token characters.
Lines 12–13Skip leading whitespace and handle end-of-file
while ((ch = getchar()) != EOF && isspace((unsigned char)ch)) { }
if (ch == EOF) return 0;getchar reads one character at a time. The loop discards whitespace before the token. The unsigned-char cast supplies an allowed argument to isspace. If EOF is reached before a token starts, return 0 without changing the caller variable.
Lines 14–15Store token characters while space remains
do {
if (used < sizeof token - 1) token[used++] = (char)ch;The do-while body handles the first non-whitespace character already read. The size check leaves one buffer slot for a null terminator. used++ uses the old index for storage, then advances the count.
Lines 16–19Reject a token that is too long
else {
while ((ch = getchar()) != EOF && !isspace((unsigned char)ch)) { }
return 0;
}When the buffer has no character space left, consume the rest of that token through whitespace or EOF, then return failure. The program does not silently treat a truncated prefix as the full input number.
Lines 20–21Read the next character and decide whether the token continues
ch = getchar();
} while (ch != EOF && !isspace((unsigned char)ch));Keep collecting until whitespace or EOF. The terminating whitespace has been consumed by getchar; it is not stored as part of the token.
Lines 22–24Terminate the string and prepare a checked conversion
token[used] = '\0';
char *end;
errno = 0;Write the null terminator after the stored token. end will identify where conversion stopped. Reset errno before strtol so a previous library error cannot be mistaken for this conversion’s result.
Line 25Convert the complete token as a decimal integer
long result = strtol(token, &end, 10);strtol reads token in base 10 and returns a long. Passing &end lets it write the address of the first unconverted character. A sign such as - is allowed as part of a valid integer token.
Line 26Reject conversion errors, leftover characters and out-of-range values
if (errno || *end != '\0' || result < -1000000 || result > 1000000) return 0;A nonzero errno rejects a reported conversion error. *end must be the null terminator, so input such as 12x is rejected rather than accepted as 12. The allowed value range is -1,000,000 to 1,000,000.
Lines 27–29Store the accepted integer through its address
*value = (int)result;
return 1;
}After validation, cast result to int and assign it through *value. Return 1 for success. The brace ends readInt. The caller variable remains unchanged on the earlier failure returns.
Line 31Set the stack’s fixed capacity
#define CAPACITY 5The macro gives the array five element slots, with valid indexes 0 through 4. It is a source-level constant, not a counter that changes during operations.
Lines 32–33Define push and reject overflow before writing
int push(int stack[], int *top, int value) {
if (*top == CAPACITY - 1) return 0;stack[] gives access to the caller’s array and top points to its current top index. If *top already equals CAPACITY - 1, all slots are occupied. Return 0 before incrementing top or storing a new value.
Lines 34–36Advance top and store the pushed item
stack[++(*top)] = value;
return 1;
}++(*top) increments the caller’s index before the array access. Starting from -1, the first push writes stack[0]. Return 1 after storage succeeds. The brace closes push.
Lines 37–38Define pop and reject underflow
int pop(int stack[], int *top, int *value) {
if (*top == -1) return 0;value points to the caller’s output variable. top equal to -1 means no element exists, so return 0 without reading the array or assigning an output value.
Lines 39–41Copy the top item before decreasing top
*value = stack[(*top)--];
return 1;
}The post-decrement uses the current index for the read, then reduces top by one. Copy the integer through *value and return success. The old array bytes need not be erased: top determines which elements belong to the stack.
Lines 42–46Display from the top down without changing it
void display(const int stack[], int top) {
printf("Stack (top to bottom):");
for (int i = top; i >= 0; --i) printf(" %d", stack[i]);
putchar('\n');
}The display function receives top by value. Its local loop starts at that index and walks down to zero, so the most recently pushed value prints first. Empty stacks print only the heading and newline.
Lines 47–48Document commands and start main
/* 1 value: push; 2: pop; 3: peek; 4: display; 0: exit. */
int main(void) {The comment describes push, pop, peek, display and exit requests. main is the command-reading entry function.
Line 49Create the empty array-stack state
int stack[CAPACITY], top = -1, command, value;The storage has CAPACITY slots. top = -1 represents emptiness. command and value hold operation inputs and successful pop results.
Lines 50–51Read a command and recognise normal exit
while (readInt(&command)) {
if (command == 0) return 0;Continue while readInt accepts a command. A zero command returns success from main. No heap nodes need cleanup in this array representation.
Lines 52–54Read and perform a push
if (command == 1) {
if (!readInt(&value)) break;
puts(push(stack, &top, value) ? "Pushed." : "Overflow: stack is full.");Command 1 needs one following integer value. A missing value breaks to the error path. Otherwise push receives &top so it can update the caller’s index; the conditional expression chooses success or overflow text.
Lines 55–57Perform a pop and print only a valid output
} else if (command == 2) {
if (pop(stack, &top, &value)) printf("Popped: %d\n", value);
else puts("Underflow: stack is empty.");Command 2 passes &value to pop. Print value only after a successful return, otherwise report underflow. This avoids reading an unassigned output after a failed pop.
Lines 58–60Peek without changing the stack
} else if (command == 3) {
if (top >= 0) printf("Top: %d\n", stack[top]);
else puts("Stack is empty.");Command 3 checks top >= 0, then prints stack[top]. It neither decrements top nor removes the item. If the stack is empty, print a message instead of accessing index -1.
Lines 61–63Display or reject an unsupported command
} else if (command == 4) display(stack, top);
else break;
}Command 4 calls display. Any other command breaks out of the loop. The brace closes the command-reading body.
Lines 64–66Report an abnormal end of input
puts("Invalid or incomplete input. End with command 0.");
return 1;
}If the loop ends without command 0, print the invalid/incomplete-input message and return failure. Array storage is released automatically when main ends.
FOLLOW THE STATE
Dry run
| Step | Operation / state | Result |
|---|---|---|
| Push 10 | top moves from −1 to 0 | Top → 10 |
| Push 20 | store/link at the top | Top → 20 → 10 |
| Pop | remove 20; top returns to 10 | Top → 10 |
CHECK THE BEHAVIOUR
Sample input & output
These outputs were produced by compiling and running this exact source. Inputs are provided in the stated order; the programs do not print repeated menu prompts.
Sample 1
1 10
1 20
3
4
2
4
0
Pushed.
Pushed.
Top: 20
Stack (top to bottom): 20 10
Popped: 20
Stack (top to bottom): 10
Sample 2
2
3
4
1 -5
2
2
0
Underflow: stack is empty.
Stack is empty.
Stack (top to bottom):
Pushed.
Popped: -5
Underflow: stack is empty.
Sample 3
1 1
1 2
1 3
1 4
1 5
1 6
4
0
Pushed.
Pushed.
Pushed.
Pushed.
Pushed.
Overflow: stack is full.
Stack (top to bottom): 5 4 3 2 1
Common mistakes
- Check emptiness before removal or peek.
- Do not confuse stack LIFO with queue FIFO.
- A pop must decrease/move the top.
WHY THIS GROWTH RATE?
Time and space complexity
Individual insertion/removal/peek O(1); display O(n). Array storage O(CAPACITY).
Let n be the number of stored items. Push, pop and peek use a fixed number of index/pointer updates, with no traversal through the stack. Each operation is O(1), assuming fixed-cost integer work.
Display visits all n items, so it is O(n). The array reserves CAPACITY entries, giving O(CAPACITY) allocated storage. With the shown fixed capacity 5, this allocation is constant; the algorithm description treats capacity as configurable.
The interface is not itself an algorithm with one complexity: each operation has its own cost. The whole command session depends on how many pushes/enqueues, removals and display requests you issue. Temporary counters and pointers use O(1) extra working space.
Big-O describes growth in the stated input quantity. Fixed factors and lower-order terms are omitted. Supported input bounds keep the educational examples practical; operation costs depend on the actual representation and functions shown.
PREPARE FOR YOUR LAB VIVA
Related viva questions & answers
Try answering aloud, then expand the answer to check your reasoning. These questions focus on the concepts used in this program.
Why does top start at -1 in the array stack?
Valid occupied indexes start at zero. The value -1 therefore represents no occupied elements, and the first successful push increments top to zero before storing a value.
How is the current number of array-stack elements obtained from top?
It is top + 1. An empty stack has top = -1 and count zero; a stack with top = 3 contains four values in indexes 0 through 3.
Why does push reject top equal to CAPACITY - 1 before incrementing it?
That index is already the last available slot. Incrementing and storing again would go outside the array, so the full check must happen before the write.
Why does pop read stack[top] before reducing top?
The value at the current top is the element to remove. Reducing top first would select the element below it and return the wrong value.
What should happen if pop is requested when top is -1?
The function reports underflow and does not access the array or change top. The caller can then display the failure rather than using an uninitialized output value.
Why is array-stack display top-to-bottom rather than index-zero-first?
Top is the next element that pop would remove. Displaying from top downward presents the stack in removal order and makes its last-in, first-out behavior visible.