C Programming with DSA Interview Questions for 5 Years Experience
40 questions with answers for candidates with 5 years of experience — 27 medium, 13 hard.
What interviewers expect: 5 years experience
At five years, interviews focus on design of larger C systems: module architecture, memory ownership rules, portability, error handling strategy and testing approach. Embedded roles discuss RTOS design, drivers and power optimisation. You should explain trade-offs you made, how you reviewed others' code and how you mentored junior engineers on safe C practices.
This set is weighted towards Structures, Unions and File Handling (8), Sorting, Searching and Dynamic Programming (6), Stacks, Queues and Hashing (6), Trees and Graphs (6).
System architecture in C Code quality and static analysis RTOS and drivers Mentoring and reviews
Solved at this levelStart practicing
3 QUESTIONS FREESign up to unlock the full question bank and track your progress.Unlock every question and answer, then continue practising company-wise and package-wise.
From low to high address, a typical process has the text segment holding code, the initialised data segment for globals and statics with values, BSS for uninitialised or zero globals, the heap growing upward for malloc memory, and the stack growing downward for function frames and local variables. Knowing this helps explain stack overflow, memory leaks and where variables live.
02
You need to update one record in the middle of a large binary file without rewriting it all. How?
Use fixed-size records so each one starts at index times record size. Open the file in r+b mode, fseek to that offset, fwrite the new record and fflush. Read it back to verify if needed. For crash safety, write to a temporary file and rename, or keep a simple journal. Databases like SQLite solve this robustly if requirements grow.
03
How do you set, clear and toggle a particular bit in C?
Use bit masks with shift operators.
x |= (1u << n); // set bit n
x &= ~(1u << n); // clear bit n
x ^= (1u << n); // toggle bit n
int bit = (x >> n) & 1u; // read bit n
Use unsigned types so shifts are well defined. These operations are common in embedded code for configuring hardware registers.
04
Describe how you approach a coding problem you have never seen before.
Situation: in a coding round I got an unfamiliar matrix problem. Task: solve it within 30 minutes. Action: I restated the problem, worked through a small example by hand, explained a brute force first, then spotted that a prefix sum would remove repeated work and coded it in C while talking through edge cases like empty rows. Result: I finished in 25 minutes and the interviewer praised my clear reasoning.
05
Insert a node at the end of a singly linked list using a double pointer.
A pointer to pointer handles the empty list without special cases.
int append(Node **head, int value) {
Node *n = malloc(sizeof *n);
if (!n) return -1;
n->data = value;
n->next = NULL;
while (*head) head = &(*head)->next;
*head = n;
return 0;
}
Call it as append(&head, 5). Keep a tail pointer if you append often.
06
What is the difference between greedy algorithms and dynamic programming?
A greedy algorithm makes the best local choice at each step and never revisits it, which works only when the greedy choice property holds, as in activity selection, Huffman coding and Dijkstra. Dynamic programming considers all relevant subproblem choices and combines them, so it works where greedy fails, such as 0/1 knapsack or coin change with arbitrary denominations. Greedy is faster when provably correct.
07
Tell me about a time you debugged a hard crash in C code.
Situation: our data logger crashed randomly after hours of running. Task: find the cause before a client demo. Action: I rebuilt with AddressSanitizer, ran a long stress test and found a buffer overflow in a string copy that corrupted a nearby pointer. I replaced it with a bounded snprintf and added a length check. Result: the logger ran for a week without a crash and we added sanitizer builds to CI.
08
After calling realloc, the program sometimes crashes. What could be wrong?
realloc may move the block and returns the new address, so old pointers to the block become invalid. Writing ptr = realloc(ptr, n) also leaks the original memory if realloc fails and returns NULL. Store the result in a temporary pointer, check for NULL, then assign. Make sure no other pointers still refer to the old location after a successful move.
09
Find the length of the longest common subsequence of two strings.
Backtracking builds a solution step by step and abandons a path as soon as it cannot lead to a valid answer, then tries the next option. It explores a search tree with pruning. Examples are N-Queens, Sudoku, generating permutations and subsets, and maze path finding. Time can be exponential, so good pruning and ordering choices matter a lot.
11
A UART receive buffer in firmware sometimes loses or duplicates bytes. How would you fix it?
The interrupt writes and the main loop reads the same ring buffer, so check for race conditions. Use a single-producer, single-consumer design where only the ISR updates head and only the main loop updates tail, declare the indexes volatile or use C11 atomics, and update the index after writing data. Detect overflow when the buffer is full and size the buffer for the maximum burst rate.
12
Implement a circular queue with enqueue and dequeue in C.
Many grid DPs only need the previous row to compute the current one, so replace the 2D table with two 1D arrays or even one array updated in place, cutting memory from O(rows times cols) to O(cols). For knapsack, iterate capacity backwards in a single array. If you also need to reconstruct the path, store compact choice bits or use Hirschberg's divide and conquer technique.
Bit fields let struct members use a specified number of bits, such as unsigned int ready : 1; unsigned int mode : 3;, which saves memory for flags. They are common in embedded code and protocol headers. However, bit ordering, padding and whether plain int bit fields are signed are implementation defined, so for portable hardware register or wire format access, explicit masks and shifts are safer.
15
Why do linked lists perform poorly compared to arrays on modern CPUs?
Each node is allocated separately and may sit anywhere in memory, so traversing a list causes frequent cache misses and the CPU cannot prefetch well. Arrays store elements contiguously, so a cache line brings several elements at once. Even with O(1) insertion, linked lists are often slower in practice. Use them when frequent insertions in the middle with stable node addresses really matter.
16
Why is it risky to write structs directly to a file with fwrite?
A raw struct includes padding bytes, platform specific sizes and byte order, so a file written on one compiler or CPU may not read correctly on another. Pointers inside the struct are meaningless after reload. For portable formats, serialise each field explicitly with fixed-width types and a defined byte order, or use a format like JSON or Protocol Buffers.
17
How do you check whether a linked list is a palindrome in O(1) extra space?
Find the middle with slow and fast pointers, reverse the second half in place, then compare the first half and the reversed second half node by node. Finally reverse the second half again to restore the list, which interviewers appreciate. Time is O(n) and extra space is O(1). A simpler O(n) space approach copies values into an array or stack first.
A good hash function is fast, deterministic and spreads keys evenly across buckets so collisions are rare, even for similar keys like user1 and user2. For strings, functions like FNV-1a or djb2 are simple choices. Using a table size that is a power of two needs good mixing of low bits, while prime sizes help weaker hashes. For untrusted input, use a seeded hash like SipHash to resist collision attacks.
19
How do you convert an infix expression to postfix?
Use the shunting yard algorithm with an operator stack. Output operands directly. For an operator, pop operators of higher precedence, or equal precedence if left associative, to the output, then push it. Push '(' and on ')' pop until the matching '('. At the end, pop all remaining operators. For example a + b * c becomes a b c * +. Postfix is easy to evaluate with a stack.
An adjacency matrix is a V by V array where cell [u][v] marks an edge; it gives O(1) edge checks but uses O(V squared) memory, suiting dense graphs. An adjacency list stores for each vertex a list or dynamic array of neighbours, using O(V + E) memory, which suits sparse real world graphs like road or social networks. Weighted graphs store the weight with each edge.
Strict aliasing says an object may be accessed only through an lvalue of its own type, a compatible type or a character type. Reading a float through an int pointer breaks the rule and is undefined behaviour, and optimisers at -O2 may reorder or drop such accesses. To reinterpret bytes safely, use memcpy into a variable of the other type, or a union, which GCC supports for type punning.
22
A build tool must compile modules in the correct dependency order and report circular dependencies. How would you design it?
Model modules as vertices and dependencies as directed edges, then run Kahn's topological sort. Start with modules that have zero in-degree, process them in a queue and reduce in-degrees of dependants. Modules left unprocessed form a cycle, which you report by tracing them with DFS. Independent modules at the same stage can be compiled in parallel.
23
Write a C program that counts lines, words and characters in a file.
Read character by character with fgetc.
FILE *fp = fopen(path, "r");
if (!fp) { perror("fopen"); return 1; }
long lines = 0, words = 0, chars = 0;
int c, inWord = 0;
while ((c = fgetc(fp)) != EOF) {
chars++;
if (c == '\n') lines++;
if (isspace(c)) inWord = 0;
else if (!inWord) { inWord = 1; words++; }
}
fclose(fp);
printf("%ld %ld %ld\n", lines, words, chars);
c is int so EOF can be distinguished from valid bytes.
24
A recursive tree traversal crashes on a very large, skewed tree. How do you fix it?
A skewed tree has height close to n, so recursion depth becomes huge and overflows the call stack. Convert the traversal to an iterative version using an explicit stack allocated on the heap, or use Morris traversal for in-order with O(1) extra space. Long term, use a self-balancing tree so height stays around log n.
25
Write code to check whether the machine is little endian or big endian.
Look at the first byte of a known integer.
int isLittleEndian(void) {
uint32_t x = 1;
unsigned char *p = (unsigned char *)&x;
return p[0] == 1;
}
// using a union
union { uint32_t i; unsigned char c[4]; } u = { 1 };
int little = (u.c[0] == 1);
x86 and most ARM systems are little endian; network byte order is big endian.
26
What is structure padding and how can you reduce it?
The compiler inserts padding bytes between members so each one is aligned, which can make a struct larger than the sum of its members. struct { char a; int b; char c; } is usually 12 bytes instead of 6. Reduce padding by ordering members from largest to smallest alignment. Packing with __attribute__((packed)) removes padding but can slow access or fault on some CPUs.
Dijkstra finds shortest paths from a source in a graph with non-negative edge weights. It keeps tentative distances, repeatedly takes the unvisited vertex with the smallest distance from a min heap and relaxes its edges, updating neighbours if a shorter path is found. With a binary heap it runs in O((V + E) log V). It fails with negative weights, where Bellman-Ford is used instead.
28
Your embedded device must store 10,000 sensor records in limited RAM. How would you design the struct?
Use fixed-width types sized to the real range, such as uint16_t for readings and uint32_t for timestamps, order members from largest to smallest to cut padding and pack boolean flags into bit fields or a single bitmask byte. Consider storing time as an offset from a base to save bytes. Check sizeof in a static assertion so the size stays as designed.
BFS explores level by level using a queue and finds the shortest path in unweighted graphs. DFS goes deep along one path using recursion or a stack, and suits cycle detection, topological sort, connected components and backtracking. Both run in O(V + E). BFS may use more memory on wide graphs, while recursive DFS can overflow the call stack on very deep graphs.
30
Why is a dummy or sentinel node useful in linked list code?
A dummy node placed before the real head removes special cases for an empty list or for inserting and deleting at the head, because every real node then has a previous node. Code for merging lists, removing elements and partitioning becomes shorter and less error prone. At the end, return dummy.next as the new head. It costs one extra node.
Undefined behaviour means the C standard places no requirement on what happens, so the program may crash, give wrong results or appear to work. Examples are signed integer overflow, dereferencing a null or freed pointer, reading an uninitialised variable, accessing an array out of bounds and modifying a variable twice without a sequence point, such as i = i++. Compilers optimise assuming it never happens.
32
Find the majority element that appears more than n/2 times in O(n) time and O(1) space.
Boyer-Moore voting algorithm.
int majority(const int *a, int n) {
int cand = 0, count = 0;
for (int i = 0; i < n; i++) {
if (count == 0) cand = a[i];
count += (a[i] == cand) ? 1 : -1;
}
return cand;
}
If a majority is not guaranteed, make a second pass to count cand and confirm it exceeds n/2.
33
Solve the 0/1 knapsack problem in C with a 1D DP array.
Iterate capacity backwards so each item is used once.
int knapsack(const int *wt, const int *val, int n, int W) {
int dp[W + 1];
for (int c = 0; c <= W; c++) dp[c] = 0;
for (int i = 0; i < n; i++) {
for (int c = W; c >= wt[i]; c--) {
int take = dp[c - wt[i]] + val[i];
if (take > dp[c]) dp[c] = take;
}
}
return dp[W];
}
Time O(nW), space O(W).
34
Count the number of islands in a grid of land and water cells. What approach do you take?
Treat each land cell as a graph node connected to its four neighbours. Scan the grid, and when you find unvisited land, increment the count and flood fill with DFS or BFS to mark the whole island visited. Time and space are O(rows times cols). For very large grids, use iterative BFS to avoid recursion depth issues, or union-find.
Keep indexes of elements still waiting for a greater value.
void nextGreater(const int *a, int n, int *res) {
int st[n], top = -1;
for (int i = 0; i < n; i++) {
res[i] = -1;
while (top >= 0 && a[st[top]] < a[i]) {
res[st[top--]] = a[i];
}
st[++top] = i;
}
}
For 4 5 2 25 the result is 5 25 25 -1, in O(n) time.
Quicksort is O(n log n) on average and very fast in practice with in-place partitioning, but O(n squared) in the worst case with poor pivots and not stable. Merge sort is always O(n log n) and stable but needs O(n) extra memory. Heap sort is O(n log n) worst case and in place but not stable and slower due to poor cache use. C's qsort is usually a tuned hybrid.
A prefix sum array stores pre[i] = sum of elements from index 0 to i-1. Then the sum of any range from l to r is pre[r + 1] - pre[l] in O(1) after O(n) preprocessing. It is used for range sum queries, counting subarrays with a given sum together with a hash map, and 2D versions for matrix region sums.
A monotonic stack keeps elements in increasing or decreasing order by popping elements that break the order before pushing a new one. Each element is pushed and popped at most once, so problems run in O(n). It solves next greater element, daily temperatures, stock span and largest rectangle in a histogram, which would otherwise need nested loops.
40
How would you detect if two linked lists intersect and find the intersection node?
Two lists intersect if they share a node by address, not just value. Compute both lengths, advance the longer list by the difference, then move both together until the pointers are equal. A neat trick is to walk pointer a through list A then B, and pointer b through B then A; they meet at the intersection or both reach NULL. Both are O(m + n) time and O(1) space.
Frequently asked questions
What is asked in C Programming with DSA interviews for 5 years of experience?
At five years, interviews focus on design of larger C systems: module architecture, memory ownership rules, portability, error handling strategy and testing approach. Embedded roles discuss RTOS design, drivers and power optimisation. You should explain trade-offs you made, how you reviewed others' code and how you mentored junior engineers on safe C practices.
How many questions should I practise before a C Programming with DSA interview?
Work through all 40 questions here, then the company-wise sets for the employers you are targeting. Quality matters more than count: be able to explain why each answer is right.
Which topics matter most for 5 years of experience?
Focus first on System architecture in C, Code quality and static analysis, RTOS and drivers, Mentoring and reviews.