Skip to content

Embedded Software Engineer Interview Questions & Answers

40 questions for Embedded Software Engineer interviews — 4 easy, 28 medium, 8 hard.

About the Embedded Software Engineer role

An embedded software engineer writes C code for microcontrollers and devices in automotive, consumer electronics, medical and industrial products. Work includes device drivers, interrupt handlers, ring buffers, communication protocols like UART, SPI and I2C, and tight memory and timing limits. Automotive and semiconductor companies in Bengaluru, Pune and Hyderabad hire many embedded engineers every year. Good debugging skills with oscilloscopes and logic analysers are a plus.

Embedded C Microcontrollers RTOS basics Bit manipulation Communication protocols
Solved for this roleStart 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.
3 questions free Unlock the rest in seconds
01

Explain the memory layout of a C program.

Save to ▾
Show answer
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

What are the main stages of compiling a C program?

Save to ▾
Show answer
Preprocessing expands #include files and macros and removes comments. Compilation turns the preprocessed code into assembly. Assembly converts it into an object file of machine code. Linking combines object files with libraries such as libc to produce the executable. With gcc you can stop at each stage using -E, -S and -c, which helps when debugging macro or linker errors.
03

What is an enum in C and what are its advantages?

Save to ▾
Show answer
An enum defines a set of named integer constants, such as enum Color { RED, GREEN, BLUE };, where values start at 0 unless set. It makes code readable and lets compilers warn about missing cases in a switch with -Wswitch. Enum values are int sized in practice, and C23 allows specifying the underlying type. Use them for states, commands and error codes.
04

Find the next greater element for every array element using a monotonic stack.

Save to ▾
Show answer
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.
05

What is a ring buffer and why is it common in embedded systems?

Save to ▾
Show answer
A ring buffer is a fixed-size circular queue in a static array. It needs no dynamic allocation, has O(1) operations and predictable memory, which suits microcontrollers. A single producer, such as a UART interrupt, writes at head while a single consumer, such as the main loop, reads at tail. With power of two sizes, index masking is cheap, and with one writer and one reader it can be lock free.
06

What is topological sorting and where is it used?

Save to ▾
Show answer
A topological sort orders the vertices of a directed acyclic graph so every edge goes from earlier to later. Kahn's algorithm repeatedly removes vertices with zero in-degree using a queue; DFS adds vertices to the order after finishing their descendants. If not all vertices are processed, the graph has a cycle. It is used for build systems, course prerequisites and task scheduling.
07

Valgrind reports invalid read of size 4 after free. How do you fix it?

Save to ▾
Show answer
This is a use-after-free: code reads memory that was already released. Valgrind shows where it was freed and where it was read. Fix the ownership logic so nothing uses the object after free, and set pointers to NULL right after freeing so accidental use fails fast. If multiple parts share the object, use reference counting or free only at one clear owner.
08

Memory use of a long running C service grows every hour. How do you investigate?

Save to ▾
Show answer
Run it under Valgrind or with AddressSanitizer and LeakSanitizer in a test environment and reproduce typical traffic, then read the leak report with allocation stack traces. In production, compare heap usage over time with tools like heaptrack. Check error paths that return early without free, lost pointers after realloc, and caches without size limits. Fix ownership and add a leak check to CI.
09

Two source files both define a global variable named count and the build fails. How do you fix it?

Save to ▾
Show answer
Each global may be defined only once. Define it in one .c file, such as int count = 0;, and declare it in a shared header as extern int count;, which other files include. If the variable should be private to each file, mark it static in both files instead. Since GCC 10, -fno-common is default, so duplicate tentative definitions also fail to link.
10

Which gcc flags would you use to build and debug a C program safely?

Save to ▾
Show answer
A good development build is: gcc -std=c17 -Wall -Wextra -Wpedantic -g -O1 \ -fsanitize=address,undefined \ -o app main.c utils.c -Wall and -Wextra turn on useful warnings, -g adds debug symbols for gdb, and sanitizers catch memory errors and undefined behaviour at runtime. For release, drop sanitizers and use -O2.
11

Implement a circular queue with enqueue and dequeue in C.

Save to ▾
Show answer
Use modulo arithmetic and a count. #define CAP 8 typedef struct { int a[CAP]; int front, rear, count; } Queue; int enqueue(Queue *q, int x) { if (q->count == CAP) return -1; q->a[q->rear] = x; q->rear = (q->rear + 1) % CAP; q->count++; return 0; } int dequeue(Queue *q, int *x) { if (q->count == 0) return -1; *x = q->a[q->front]; q->front = (q->front + 1) % CAP; q->count--; return 0; }
12

You need the maximum of every sliding window of size k in an array. How would you solve it efficiently?

Save to ▾
Show answer
Use a deque storing indexes in decreasing order of value. For each element, remove indexes outside the window from the front, pop smaller values from the back, then push the current index. The front is always the maximum of the current window. Each index enters and leaves once, so the total time is O(n) instead of O(nk).
13

What is the difference between text and binary file modes?

Save to ▾
Show answer
In text mode, the C library may translate line endings, for example \r\n on Windows to \n, and a Ctrl+Z byte can mean end of file. Binary mode reads and writes bytes exactly as stored. Use text mode with fprintf and fgets for human readable files and binary mode with fread and fwrite for images, structs and any exact byte format. On Linux both behave the same.
14

Code works with -O0 but fails with -O2. How do you approach this?

Save to ▾
Show answer
This usually means the code relies on undefined behaviour that the optimiser exploits, such as uninitialised variables, out-of-bounds access, strict aliasing violations or missing volatile on shared hardware variables. Build with -Wall -Wextra and -fsanitize=address,undefined, then fix each report. If a variable is changed by an interrupt or another thread, add volatile or proper atomics.
15

What is a function pointer and where is it used?

Save to ▾
Show answer
A function pointer stores the address of a function, declared like int (*op)(int, int) = add;, and called with op(2, 3). It enables callbacks such as the comparator in qsort, dispatch tables replacing long switch statements, plugin interfaces and state machines in embedded code. Using typedef makes function pointer types much more readable.
16

Write a C program that counts lines, words and characters in a file.

Save to ▾
Show answer
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.
17

Use qsort with a function pointer to sort an array of integers in descending order.

Save to ▾
Show answer
Write a comparator that returns negative, zero or positive. int cmpDesc(const void *a, const void *b) { int x = *(const int *)a; int y = *(const int *)b; return (y > x) - (y < x); } int arr[] = {4, 1, 9, 3}; qsort(arr, 4, sizeof arr[0], cmpDesc); Returning y - x can overflow, so the comparison form is safer.
18

How do you convert an infix expression to postfix?

Save to ▾
Show answer
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.
19

A function returns the address of a local array and the caller gets wrong data. Why?

Save to ▾
Show answer
Local arrays live on the stack and are destroyed when the function returns, so the returned pointer is dangling and later calls overwrite that memory. Fix it by having the caller pass a buffer and its size, allocating with malloc and documenting that the caller must free it, or using a static buffer, which is not thread safe. Compilers warn about this with -Wreturn-local-addr.
20

Copy a file in C using fread and fwrite.

Save to ▾
Show answer
Use a buffer and binary mode. int copyFile(const char *src, const char *dst) { FILE *in = fopen(src, "rb"); if (!in) return -1; FILE *out = fopen(dst, "wb"); if (!out) { fclose(in); return -1; } char buf[8192]; size_t n; while ((n = fread(buf, 1, sizeof buf, in)) > 0) { if (fwrite(buf, 1, n, out) != n) break; } fclose(in); return fclose(out); }
21

A hash table becomes slow as more data is inserted. What is wrong?

Save to ▾
Show answer
The load factor has grown, so chains get long or open addressing probes become long, turning O(1) into nearly O(n). Another cause is a poor hash function clustering keys into few buckets. Fix it by resizing to roughly double capacity and rehashing when the load factor crosses a threshold, and by using a better hash function. Measure the longest chain to confirm.
22

Why is a circular queue better than a simple array queue?

Save to ▾
Show answer
In a simple array queue, the front moves forward as elements are removed, so the space before it is wasted and the queue appears full even when there is room. A circular queue wraps indexes using modulo, so rear = (rear + 1) % size reuses freed slots. You track fullness with a count or by keeping one slot empty to distinguish full from empty.
23

Tell me about a time you explained a complex data structure to your team.

Save to ▾
Show answer
Situation: our team needed an LRU cache in a C service and several members had not built one. Task: align everyone before coding. Action: I drew the hash table and doubly linked list on a whiteboard, walked through get and put with a small example and shared a short test suite covering edge cases. Result: the team implemented it in two days with no major review issues.
24

Tell me about a time you chose the right data structure under pressure.

Save to ▾
Show answer
Situation: during a hackathon our C route planner was too slow to answer queries live. Task: fix it within a few hours. Action: I profiled it, found repeated linear searches of a list of stops and replaced them with a hash table keyed by stop id, plus a queue for BFS. Result: queries went from 4 seconds to 30 ms and we won second place.
25

What is a double pointer and when do you need it?

Save to ▾
Show answer
A double pointer, such as int **pp, stores the address of a pointer. It is needed when a function must change the caller's pointer itself, for example inserting at the head of a linked list or allocating memory inside a function and returning it through a parameter. It is also used for dynamic 2D arrays and arrays of strings, like char **argv.
26

Tell me about a project where you worked with files or binary data in C.

Save to ▾
Show answer
Situation: in my final year project we logged GPS data on a microcontroller SD card. Task: store records reliably with small memory. Action: I designed a packed 16 byte record with fixed-width types, wrote it with a checksum, flushed every ten records and wrote a PC tool to decode the file. Result: we logged a full day with no corruption and could replay any trip.
27

You need to update one record in the middle of a large binary file without rewriting it all. How?

Save to ▾
Show answer
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.
28

After calling realloc, the program sometimes crashes. What could be wrong?

Save to ▾
Show answer
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.
29

You need to implement a queue using two stacks. How does it work and what is its cost?

Save to ▾
Show answer
Use an in-stack for enqueue and an out-stack for dequeue. Enqueue always pushes to the in-stack. Dequeue pops from the out-stack, and only when it is empty moves all elements from the in-stack to it, which reverses their order so the oldest comes out first. Each element moves at most twice, so operations are O(1) amortised even though a single dequeue can be O(n).
30

Implement quicksort in C using Lomuto partition.

Save to ▾
Show answer
Partition around the last element, then sort both sides. void swap(int *x, int *y) { int t = *x; *x = *y; *y = t; } int partition(int *a, int lo, int hi) { int pivot = a[hi], i = lo; for (int j = lo; j < hi; j++) if (a[j] < pivot) swap(&a[i++], &a[j]); swap(&a[i], &a[hi]); return i; } void quickSort(int *a, int lo, int hi) { if (lo < hi) { int p = partition(a, lo, hi); quickSort(a, lo, p - 1); quickSort(a, p + 1, hi); } }
31

What does the static keyword do in different contexts?

Save to ▾
Show answer
Inside a function, a static local variable keeps its value between calls and is initialised only once. At file level, a static global variable or function has internal linkage, so it is visible only in that source file, which helps hide implementation details. Static variables are stored in the data or BSS segment, not the stack, and default to zero.
32

What is a memory leak and how do you detect it?

Save to ▾
Show answer
A memory leak happens when dynamically allocated memory is never freed and the last pointer to it is lost, so the program keeps growing in memory use. Long running services and embedded systems eventually fail. Detect leaks with Valgrind memcheck, AddressSanitizer with LeakSanitizer or allocation tracking wrappers. Prevent them with clear ownership rules and a single cleanup path per function.
33

What is the strict aliasing rule?

Save to ▾
Show answer
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.
34

Tell me about a time you fixed a memory leak.

Save to ▾
Show answer
Situation: a C based telemetry agent grew by 200 MB a day on customer servers. Task: find and fix the leak quickly. Action: I reproduced it with recorded traffic under Valgrind and found that an error path returned early without freeing a parsed message buffer. I restructured the function with a single cleanup label and added a leak check job to CI. Result: memory stayed flat for weeks after the patch.
35

What is the volatile qualifier used for in C?

Save to ▾
Show answer
volatile tells the compiler that a variable can change outside normal program flow, for example a hardware register, a variable changed in an interrupt handler or memory shared with another device. The compiler must read it from memory every time and cannot cache it in a register or remove repeated reads. It does not provide atomicity or thread safety on its own.
36

What is a deque and how can it be implemented in C?

Save to ▾
Show answer
A deque, or double ended queue, supports insertion and removal at both the front and the rear in O(1). In C it is commonly implemented as a circular array with front and rear indexes that wrap using modulo, or as a doubly linked list. It is the core of the sliding window maximum algorithm and can act as both a stack and a queue.
37

What is the difference between a macro and an inline function?

Save to ▾
Show answer
A macro is plain text substitution by the preprocessor, with no type checking, and arguments may be evaluated more than once, so SQUARE(i++) misbehaves. An inline function is a real function with type checks and single evaluation of arguments, which the compiler may expand in place for speed. Prefer static inline functions and use macros only for things like conditional compilation.
38

What are bit fields in C?

Save to ▾
Show answer
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.
39

What is a heap and how does it implement a priority queue?

Save to ▾
Show answer
A binary heap is a complete binary tree stored in an array where every parent is smaller than its children for a min heap, or larger for a max heap. For index i, children are at 2i + 1 and 2i + 2. Insert adds at the end and sifts up, and extracting the top swaps it with the last element and sifts down, both O(log n). Building a heap from an array takes O(n).
40

What is memory alignment and why does it matter?

Save to ▾
Show answer
Alignment means placing data at addresses that are multiples of its size or required boundary, such as 4 for int. CPUs read aligned data faster, and some architectures fault on misaligned access. Compilers add padding in structs to align members. You can check alignment with _Alignof and request it with _Alignas or aligned_alloc in C11.

Frequently asked questions

What does a Embedded Software Engineer do?

An embedded software engineer writes C code for microcontrollers and devices in automotive, consumer electronics, medical and industrial products. Work includes device drivers, interrupt handlers, ring buffers, communication protocols like UART, SPI and I2C, and tight memory and timing limits. Automotive and semiconductor companies in Bengaluru, Pune and Hyderabad hire many embedded engineers every year. Good debugging skills with oscilloscopes and logic analysers are a plus.

Which skills are tested in a Embedded Software Engineer interview?

Expect questions on Embedded C, Microcontrollers, RTOS basics, Bit manipulation, Communication protocols. Most rounds also ask you to explain a project and solve one practical problem live.

How do I prepare for a Embedded Software Engineer interview?

Revise C Basics, Data Types and Operators, Pointers and Memory Management, Structures, Unions and File Handling, Stacks, Queues and Hashing first using the topic sheets, practise the questions on this page aloud, then do one full mock interview to practise timing.