THE SYLLABUS TASK
What you need to solve
Write a program to implement queue 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 queue follows FIFO: first in, first out. Enqueue joins the rear; dequeue removes the front. If 10 enters before 20, it also leaves first.
This array implementation is circular. front identifies the first element and count records how many are present. The next insertion index is (front + count) % CAPACITY. Dequeue advances front with modulo arithmetic.
CAPACITY is 5. Removed slots can be reused after indices wrap around. count == 0 means empty and count == CAPACITY means full, avoiding the ambiguous front == rear rule.
PLAN BEFORE CODING
Algorithm
- Initialize the empty queue.
- 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 | Enqueue at the rear. |
2 | Dequeue and print the front value. |
3 | Peek at the front without removing it. |
4 | Display from front to rear. |
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 enqueue(int queue[], int *front, int *count, int value) {
if (*count == CAPACITY) return 0;
int rear = (*front + *count) % CAPACITY;
queue[rear] = value;
++(*count);
return 1;
}
int dequeue(int queue[], int *front, int *count, int *value) {
if (*count == 0) return 0;
*value = queue[*front];
*front = (*front + 1) % CAPACITY;
--(*count);
return 1;
}
void display(const int queue[], int front, int count) {
printf("Queue (front to rear):");
for (int i = 0; i < count; ++i) printf(" %d", queue[(front + i) % CAPACITY]);
putchar('\n');
}
/* 1 value: enqueue; 2: dequeue; 3: peek; 4: display; 0: exit. */
int main(void) {
int queue[CAPACITY], front = 0, count = 0, command, value;
while (readInt(&command)) {
if (command == 0) return 0;
if (command == 1) {
if (!readInt(&value)) break;
puts(enqueue(queue, &front, &count, value) ? "Enqueued." : "Overflow: queue is full.");
} else if (command == 2) {
if (dequeue(queue, &front, &count, &value)) printf("Dequeued: %d\n", value);
else puts("Underflow: queue is empty.");
} else if (command == 3) {
if (count) printf("Front: %d\n", queue[front]);
else puts("Queue is empty.");
} else if (command == 4) display(queue, front, count);
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 queue-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 circular queue capacity
#define CAPACITY 5This example reserves five array slots. A count distinguishes a full queue from an empty queue, so all five slots can hold items.
Lines 32–33Define enqueue and reject a full queue
int enqueue(int queue[], int *front, int *count, int value) {
if (*count == CAPACITY) return 0;front and count point to the caller’s state. If count equals CAPACITY, return 0 before changing any data. This prevents overwriting an item that is still waiting to be removed.
Line 34Find the next insertion slot with wraparound
int rear = (*front + *count) % CAPACITY;The logical rear position is front plus count, reduced modulo CAPACITY. For front = 4 and count = 2, (4 + 2) % 5 gives index 1.
Lines 35–38Store the new value and increase the item count
queue[rear] = value;
++(*count);
return 1;
}Write into the calculated rear slot, then increment the caller’s count and return 1. front stays unchanged because enqueue adds after existing items.
Lines 39–40Define dequeue and reject an empty queue
int dequeue(int queue[], int *front, int *count, int *value) {
if (*count == 0) return 0;A zero count means no front item can be returned. Leave the output variable and queue state unchanged when returning failure.
Lines 41–45Return the oldest item and advance front
*value = queue[*front];
*front = (*front + 1) % CAPACITY;
--(*count);
return 1;
}Copy queue[*front] into the caller’s output, advance front modulo the capacity, then reduce count. Return success after these fixed changes. No elements are shifted through the array.
Lines 46–50Print logical queue order rather than raw array order
void display(const int queue[], int front, int count) {
printf("Queue (front to rear):");
for (int i = 0; i < count; ++i) printf(" %d", queue[(front + i) % CAPACITY]);
putchar('\n');
}For each used logical position i, calculate (front + i) % CAPACITY. This starts at the oldest item and wraps as needed. Printing physical indexes 0 through count - 1 would be wrong after wraparound.
Lines 51–52Document queue commands and start main
/* 1 value: enqueue; 2: dequeue; 3: peek; 4: display; 0: exit. */
int main(void) {The commands request enqueue, dequeue, front inspection, display or exit. The integer return from main is an execution status.
Line 53Create an empty queue state
int queue[CAPACITY], front = 0, count = 0, command, value;front starts at index 0 and count starts at 0. The array’s unused bytes do not need initial values because operations read only counted items.
Lines 54–55Read a command or exit successfully
while (readInt(&command)) {
if (command == 0) return 0;The loop accepts checked command tokens. A zero command ends the array-based program without any node cleanup.
Lines 56–58Read a value and enqueue it
if (command == 1) {
if (!readInt(&value)) break;
puts(enqueue(queue, &front, &count, value) ? "Enqueued." : "Overflow: queue is full.");Command 1 requires the next value. The call supplies addresses of front and count so the helper can update queue state. A full queue reports overflow rather than overwriting an old item.
Lines 59–61Remove and print a value only on success
} else if (command == 2) {
if (dequeue(queue, &front, &count, &value)) printf("Dequeued: %d\n", value);
else puts("Underflow: queue is empty.");Command 2 passes &value to dequeue. If count was zero, print underflow and do not read an output value that was not assigned by the helper.
Lines 62–64Inspect the current front without removing it
} else if (command == 3) {
if (count) printf("Front: %d\n", queue[front]);
else puts("Queue is empty.");Command 3 reads queue[front] only when count is nonzero. Peek leaves both front and count unchanged.
Lines 65–67Display the queue or leave the loop on an unsupported command
} else if (command == 4) display(queue, front, count);
else break;
}Command 4 uses the logical-order display function. Any other command breaks out to the error report. The brace closes the loop body.
Lines 68–70Report missing or invalid commands
puts("Invalid or incomplete input. End with command 0.");
return 1;
}If normal exit command 0 was not read, print the incomplete-input message and return failure. No heap storage is owned by this array queue.
FOLLOW THE STATE
Dry run
| Step | Operation / state | Result |
|---|---|---|
| Enqueue 10 | insert at rear | Front → 10 ← Rear |
| Enqueue 20 | advance/link rear | Front → 10 → 20 ← Rear |
| Dequeue | remove 10 | Front → 20 ← Rear |
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
Enqueued.
Enqueued.
Front: 10
Queue (front to rear): 10 20
Dequeued: 10
Queue (front to rear): 20
Sample 2
2
3
4
1 -5
2
2
0
Underflow: queue is empty.
Queue is empty.
Queue (front to rear):
Enqueued.
Dequeued: -5
Underflow: queue is empty.
Sample 3
1 1
1 2
1 3
1 4
1 5
1 6
2
2
1 7
1 8
4
0
Enqueued.
Enqueued.
Enqueued.
Enqueued.
Enqueued.
Overflow: queue is full.
Dequeued: 1
Dequeued: 2
Enqueued.
Enqueued.
Queue (front to rear): 3 4 5 7 8
Common mistakes
- Check emptiness before removal or peek.
- Do not confuse stack LIFO with queue FIFO.
- Keep the modulo operation when wrapping queue indices.
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. Enqueue, dequeue and peek use a fixed number of index/pointer updates, with no traversal through the queue. 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 is the enqueue index calculated as (front + count) modulo CAPACITY?
Front identifies the oldest value and count gives the number already queued. Their sum identifies the next logical slot; modulo wraps that slot back to the beginning of the fixed array.
How can this circular array distinguish an empty queue from a full one?
It stores count explicitly. Count zero means empty and count equal to CAPACITY means full, so it does not rely on two indexes that might coincide in both situations.
Why does dequeue advance front using modulo instead of shifting every remaining element?
Moving front changes which stored value is logically first. Modulo handles wraparound, and no values need to be copied, so dequeue remains O(1).
Can a freed slot near the beginning be reused while newer elements remain near the end?
Yes. For capacity 5, after removing indexes 0 and 1 from a full queue, new values can occupy those indexes. Logical order is maintained by front and count rather than physical index order.
Why must queue display calculate each index from front plus an offset?
Occupied elements may cross the array boundary. Using (front + i) % CAPACITY visits them in arrival order; simply printing indexes 0 through count - 1 could display the wrong values.
Why can front remain nonzero when count becomes zero?
Count alone determines emptiness. The next enqueue writes at the current front, which is valid after modulo advancement; resetting front to zero is optional and unnecessary for correctness.