Embedded C Interview (50 Questions)
Section A – C Fundamentals (1–10)
1.
Q1. Explain the memory layout of a C program.
A C program is divided into multiple memory sections by the linker. Each section has a specific purpose.
High Address
+----------------------------------+
| Stack |
| Local Variables |
| Function Parameters |
+----------------------------------+
| |
| Heap |
| malloc(), calloc() |
+----------------------------------+
| .bss |
| Uninitialized Globals |
| Static Variables |
+----------------------------------+
| .data |
| Initialized Globals |
| Static Variables |
+----------------------------------+
| .rodata |
| const Variables |
| String Literals |
+----------------------------------+
| .text |
| Program Instructions |
| Functions |
+----------------------------------+
Low Address
The startup code initializes these sections before calling main().
Q2. What is the .text section?
Answer:
The .text section contains the executable machine instructions of the program.
Contains:- Functions
- Interrupt Service Routines (ISR)
- Application code
- Bootloader code
void add()
{
printf("Hello");
}
The function add() is stored in the .text section.
Characteristics:
- Read Only
- Executable
- Stored in Flash/ROM
- Cannot be modified during runtime
Q3. What is the .data section?
Answer:
The .data section stores initialized global and static variables.
Example:int count = 10; static int flag = 5;Both variables are placed in the .data section. Characteristics:
- Read / Write
- Stored in Flash initially
- Copied to RAM during startup
- Modified only in RAM
Flash count = 10 ↓ Startup Code ↓ RAM count = 10
Q4. What is the .bss section?
Answer:
The .bss section contains uninitialized global and static variables.
Example:int counter; static int errorFlag;Both variables are stored in the .bss section. Characteristics:
- Stored only in RAM
- Automatically initialized to zero
- Consumes no Flash space for initial values
memset(__bss_start__, 0, __bss_size__);This clears all variables in the .bss section before
main().
Q5. What is the Heap?
Answer:
The Heap is used for dynamic memory allocation.
Example:int *ptr; ptr = malloc(100);The memory allocated by
malloc() comes from the Heap.
Characteristics:
- Allocated during runtime
- Managed using malloc(), calloc(), realloc(), free()
- Grows upward (architecture dependent)
- Memory Fragmentation
- Memory Leaks
- Non-deterministic Allocation Time
- Difficult Safety Certification
- Static Allocation
- Memory Pools
Q6. What is the Stack?
Answer:
The Stack stores temporary data required during function execution.
Contains:- Local Variables
- Function Parameters
- Return Address
- Saved Registers
void fun()
{
int a;
int b;
}
Variables a and b are stored on the Stack.
Characteristics:
- Automatic Allocation
- Automatic Deallocation
- Very Fast Access
- Usually Grows Downward
Q7. Explain the startup sequence of a C program.
Answer:
Power ON ↓ Reset ↓ Reset Handler ↓ Copy .data (Flash → RAM) ↓ Clear .bss ↓ Initialize C Runtime ↓ Call main()This initialization happens before executing the application.
Q8. Explain the memory layout using a simple C program.
Answer:
int g = 10; // .data
int x; // .bss
const int c = 20; // .rodata
int main()
{
int a = 5; // Stack
int *p = malloc(20); // Heap
return 0;
}
| Variable | Memory Section |
|---|---|
| g | .data |
| x | .bss |
| c | .rodata |
| a | Stack |
| p | Stack (pointer) |
| malloc() memory | Heap |
| main() | .text |
Q9. Why is the .data section copied from Flash to RAM?
Answer:
Initialized global and static variables must be writable during program execution. Their initial values are stored in Flash, but the startup code copies them to RAM so the application can modify them.
Q10. Why is the .bss section not stored in Flash?
Answer:
All variables in the .bss section are initialized to zero. Instead of storing zeros in Flash, the startup code clears the RAM region using memset(), which saves Flash memory.
Q11. Why is the .text section stored in Flash?
Answer:
The .text section contains executable program instructions. Since code normally does not change during execution, storing it in non-volatile Flash memory is efficient and preserves the program across power cycles.
Q12. Why do automotive projects avoid malloc()?
Answer:
- Memory Fragmentation
- Memory Leaks
- Non-deterministic execution time
- Reduced reliability in safety-critical systems
For these reasons, automotive software typically relies on static memory allocation or fixed-size memory pools instead of dynamic allocation.
2.
What is the difference between:
Q1. What is the difference between const int *p, int *const p, and const int *const p?
Answer
There are two things to remember:- Can the data be modified?
- Can the pointer point to another location?
| Declaration | Modify Data? | Change Pointer? |
|---|---|---|
const int *p |
❌ No | ✅ Yes |
int *const p |
✅ Yes | ❌ No |
const int *const p |
❌ No | ❌ No |
Q2. Explain const int *p.
Answer
This means: Pointer to Constant Integer The pointer can point to another variable, but the value being pointed to cannot be modified through the pointer. Exampleint a = 10; int b = 20; const int *p = &a; p = &b; // ✔ Allowed *p = 30; // ✘ ErrorDiagram
p
│
▼
+-----------+
| a = 10 |
+-----------+
Pointer can move.
Data cannot be modified.
Q3. Explain int *const p.
Answer
This means: Constant Pointer to Integer The pointer always points to the same memory location, but the value at that location can be modified. Exampleint a = 10; int b = 20; int *const p = &a; *p = 30; // ✔ Allowed p = &b; // ✘ ErrorDiagram
p (Fixed)
│
▼
+-----------+
| a = 10 |
+-----------+
Pointer cannot move.
Data can be modified.
Q4. Explain const int *const p.
Answer
This means: Constant Pointer to Constant Integer Neither the pointer nor the data can be modified. Exampleint a = 10; int b = 20; const int *const p = &a; *p = 30; // ✘ Error p = &b; // ✘ ErrorDiagram
p (Fixed)
│
▼
+-----------+
| a = 10 |
+-----------+
Pointer cannot move.
Data cannot be modified.
Q5. How can you easily read these declarations?
Answer
Read from the variable name outward.Example 1
const int *p;Read as:
p ↓ Pointer ↓ to const intMeaning: Pointer is variable. Data is constant.
Example 2
int *const p;Read as:
p ↓ const Pointer ↓ to intMeaning: Pointer is constant. Data is variable.
Example 3
const int *const p;Read as:
p ↓ const Pointer ↓ to const intMeaning: Both pointer and data are constant.
Q6. Where are these used in Embedded Systems?
Answer
const int *p Used when reading data that must not be modified. Examples:- Flash memory
- Configuration tables
- Calibration data
- String literals
const uint8_t *FirmwareImage;
int *const p Used when a pointer must always reference the same hardware register. Example:
#define GPIO ((volatile uint32_t *)0x40020000) volatile uint32_t *const GPIOA = GPIO;The register address never changes, but the register value changes.
const int *const p Used for fixed lookup tables or secure configuration data. Example:
const uint8_t *const BootSignature;Neither the address nor the contents should change.
Q7. Interview Trick: What is the difference between these two declarations? const int *p; int const *p;
Answer
There is no difference. Both declarations mean:Pointer to Constant IntegerBoth prevent modification of the data through the pointer.
Q8. Quick Interview Summary
| Declaration | Pointer Constant? | Data Constant? |
|---|---|---|
int *p |
❌ No | ❌ No |
const int *p |
❌ No | ✅ Yes |
int *const p |
✅ Yes | ❌ No |
const int *const p |
✅ Yes | ✅ Yes |
3.
Explain the difference between
- static variable
- global variable
- local variable
- extern variable
4.
When should the volatile keyword be used?
Give practical embedded examples.
Q. When should the volatile keyword be used? Give practical embedded examples.
Answer
The volatile keyword tells the compiler that the value of a variable can change at any time without the compiler's knowledge. Therefore, the compiler must always read the value from memory instead of using an optimized copy stored in a CPU register.
Without volatile, the compiler may optimize the code, leading to incorrect behavior in embedded systems.
Syntax
volatile int flag;
This tells the compiler that flag can change unexpectedly.
Q. Why is volatile required?
Answer
Modern compilers perform aggressive optimizations. If a variable appears unchanged within a function, the compiler may read it once and reuse the value from a CPU register instead of accessing memory repeatedly.
However, in embedded systems, hardware or another execution context can modify the variable at any time.
Example (Without volatile)
int flag = 0;
while(flag == 0)
{
// Wait
}
The compiler may optimize this as:
int temp = flag;
while(temp == 0)
{
// Infinite loop
}
If an interrupt changes flag, the loop never exits because the compiler continues using temp.
volatile int flag = 0;
while(flag == 0)
{
// Wait
}
Now the compiler reads flag from memory on every iteration.
Q. When should volatile be used?
Answer
The volatile keyword should be used whenever a variable can be modified outside the normal program flow.
- Hardware registers
- Interrupt Service Routine (ISR) variables
- Shared variables between tasks (with proper synchronization)
- DMA buffers or DMA status flags
- Memory-mapped peripheral registers
- Watchdog registers
- Status registers updated by hardware
Q. Give an example of volatile with an Interrupt Service Routine (ISR).
Answer
Incorrect Code
int dataReady = 0;
void ISR(void)
{
dataReady = 1;
}
int main()
{
while(dataReady == 0)
{
}
}
The compiler may optimize the loop and never detect the update.
Correct Code
volatile int dataReady = 0;
void ISR(void)
{
dataReady = 1;
}
int main()
{
while(dataReady == 0)
{
}
}
Each iteration reads the latest value from memory, allowing the main loop to detect the ISR update.
Q. Give an example of volatile with memory-mapped hardware registers.
Answer
Peripheral registers are updated directly by hardware, so they must always be declared as volatile.
#define GPIO_STATUS (*(volatile uint32_t *)0x40020010)
if(GPIO_STATUS & 0x01)
{
// Button Pressed
}
Every read accesses the actual hardware register instead of a cached value.
Q. Give an example of volatile with DMA.
Answer
DMA controllers update memory independently of the CPU.
volatile uint8_t dmaComplete = 0;
void DMA_IRQHandler(void)
{
dmaComplete = 1;
}
int main()
{
while(dmaComplete == 0)
{
}
// Process received data
}
Without volatile, the CPU may never observe the DMA completion flag.
Q. Give an example of volatile with UART communication.
Answer
volatile uint8_t rxFlag = 0;
volatile uint8_t rxData;
void UART_IRQHandler(void)
{
rxData = UART_DR;
rxFlag = 1;
}
int main()
{
while(rxFlag == 0)
{
}
printf("%c", rxData);
}
The interrupt updates the variables, so both should be declared as volatile.
Q. Should every global variable be declared as volatile?
Answer
No. Only variables that can change unexpectedly should be declared as volatile.
Using volatile unnecessarily prevents compiler optimizations and can reduce performance.
Q. Does volatile make code thread-safe?
Answer
No.
volatile only prevents compiler optimization. It does not provide:
- Atomic operations
- Mutual exclusion
- Memory synchronization
- Race condition protection
For shared data between multiple tasks or threads, use synchronization mechanisms such as:
- Mutex
- Semaphore
- Critical Section
- Spinlock (where appropriate)
- Atomic operations
Q. What is the difference between const and volatile?
Answer
| Feature | const | volatile |
|---|---|---|
| Purpose | Prevents modification by software | Prevents compiler optimization |
| Can value change? | No (through that variable) | Yes |
| Compiler optimization | Allowed | Restricted |
| Typical use | Configuration tables, constants | Hardware registers, ISRs, DMA |
Q. Can const and volatile be used together?
Answer
Yes. A variable can be both const and volatile.
const volatile uint32_t STATUS_REGISTER =
*(volatile uint32_t *)0x40000000;
Here:
volatileindicates the hardware may update the register at any time.constprevents software from writing to the register.
This combination is commonly used for read-only hardware status registers.
Q. What are some real-time automotive examples where volatile is required?
Answer
- CAN receive flag updated by a CAN ISR.
- UART receive buffer updated by an interrupt.
- DMA completion flag for SPI or ADC transfers.
- Watchdog status register.
- GPIO input status register.
- Timer counter register.
- ADC conversion complete flag.
- Bootloader communication status flag.
- Flash controller busy register.
- Ethernet MAC status registers.
Q. Quick Interview Summary
| Use Case | volatile Required? |
|---|---|
| Hardware Registers | ✔ Yes |
| ISR Shared Variables | ✔ Yes |
| DMA Buffers/Flags | ✔ Yes |
| Memory-Mapped I/O | ✔ Yes |
| Normal Local Variables | ✘ No |
| Normal Global Variables | ✘ No (unless updated externally) |
| Thread Safety | ✘ No |
5.
What is the difference between
malloc()
calloc()
realloc()
When are they avoided in embedded systems?
Q. What is the difference between malloc(), calloc(), and realloc()? When are they avoided in embedded systems?
Answer
These are standard C library functions used for dynamic memory allocation from the Heap.
| Function | Purpose | Initializes Memory? | Can Resize Memory? |
|---|---|---|---|
malloc() |
Allocates a block of memory | ❌ No | ❌ No |
calloc() |
Allocates memory for multiple elements | ✔ Yes (Zero-initialized) | ❌ No |
realloc() |
Resizes an existing memory block | Existing data preserved (up to the smaller of old/new sizes) | ✔ Yes |
All three functions allocate memory from the Heap.
Q. What is malloc()?
Answer
malloc() allocates a specified number of bytes from the Heap.
void *malloc(size_t size);Example
int *ptr;
ptr = (int *)malloc(sizeof(int) * 5);
if(ptr != NULL)
{
// Use memory
}
Characteristics
- Allocates a contiguous block of memory.
- Memory contents are uninitialized (contain indeterminate values).
- Returns
NULLif allocation fails. - Memory must be released using
free().
Q. What is calloc()?
Answer
calloc() allocates memory for an array of elements and initializes all bytes to zero.
void *calloc(size_t num, size_t size);Example
int *ptr;
ptr = (int *)calloc(5, sizeof(int));
if(ptr != NULL)
{
// All elements are initialized to 0
}
Characteristics
- Allocates contiguous memory.
- Initializes allocated memory to zero.
- Returns
NULLon failure. - Memory must be released using
free().
Q. What is realloc()?
Answer
realloc() changes the size of an existing memory block.
void *realloc(void *ptr, size_t new_size);Example
int *ptr;
ptr = (int *)malloc(5 * sizeof(int));
int *temp = (int *)realloc(ptr, 10 * sizeof(int));
if(temp != NULL)
{
ptr = temp;
}
else
{
// Original memory pointed to by ptr is still valid
}
Characteristics
- Can increase or decrease the allocated memory size.
- May move the allocation to a new location if needed.
- Preserves existing data up to the smaller of the old and new sizes.
- If
realloc()fails, it returnsNULLand the original memory block remains allocated.
Q. What is the difference between malloc(), calloc(), and realloc()?
Answer
| Feature | malloc() |
calloc() |
realloc() |
|---|---|---|---|
| Purpose | Allocate memory | Allocate and zero-initialize memory | Resize existing memory |
| Initialization | No | Yes (Zero) | Existing data preserved |
| Arguments | 1 | 2 | 2 |
| May move memory | No | No | Yes |
| Returns | Pointer or NULL | Pointer or NULL | Pointer or NULL |
Q. Why are these functions often avoided in embedded systems?
Answer
Many embedded systems, especially automotive and safety-critical applications, avoid dynamic memory allocation because it introduces unpredictability and reliability risks.
Main reasons:- Memory fragmentation over time.
- Memory leaks if allocated memory is not freed.
- Non-deterministic allocation time.
- Heap exhaustion.
- Difficult debugging and validation.
- Harder to meet real-time requirements.
- Challenges in safety certification (e.g., ISO 26262).
Q. What is memory fragmentation?
Answer
Memory fragmentation occurs when free memory is split into many small blocks, making it impossible to allocate a larger contiguous block even though the total free memory is sufficient.
ExampleInitial Heap +-----------------------------+ | Free 100 Bytes | +-----------------------------+ After several allocations/frees +-----+----+------+----+------+ |Free |Used|Free |Used|Free | +-----+----+------+----+------+ Total Free = 60 Bytes Largest Continuous Block = 20 Bytes
A request for 30 bytes may fail because no single contiguous block is large enough.
Q. What alternatives are commonly used instead of dynamic memory allocation?
Answer
Embedded systems typically prefer deterministic memory management techniques.
- Static memory allocation.
- Global variables.
- Fixed-size buffers.
- Memory pools.
- Static RTOS object allocation.
static uint8_t rxBuffer[256];
The memory is allocated at compile time and remains available throughout program execution.
Q. Can malloc() be used in embedded systems?
Answer
Yes, but only when appropriate for the application.
Examples include:
- Linux applications.
- Linux device drivers (using kernel allocation APIs such as
kmalloc(), not standardmalloc()). - Embedded Linux systems with memory management.
- Systems where dynamic allocation is controlled and predictable.
In bare-metal and safety-critical firmware, dynamic allocation is generally minimized or avoided after system initialization.
Q. What precautions should be taken when using realloc()?
Answer
Never assign the result of realloc() directly to the original pointer because the original memory would be lost if the reallocation fails.
ptr = realloc(ptr, new_size);Correct
int *temp = realloc(ptr, new_size);
if(temp != NULL)
{
ptr = temp;
}
else
{
// Original pointer is still valid
}
Q. Quick Interview Summary
| Function | Purpose | Memory Initialized? |
|---|---|---|
malloc() |
Allocate memory | ❌ No |
calloc() |
Allocate and zero-initialize memory | ✔ Yes |
realloc() |
Resize allocated memory | Existing data preserved |
Why avoided in embedded systems?
- Memory fragmentation
- Memory leaks
- Non-deterministic execution time
- Heap exhaustion
- Reduced reliability in real-time and safety-critical systems
6.
What are lvalues and rvalues?
Q. What are lvalues and rvalues?
Answer
In C, every expression is classified as either an lvalue or an rvalue.
- lvalue (Left Value): Represents an object that has a memory location (address). It can appear on the left-hand side of an assignment.
- rvalue (Right Value): Represents a temporary value or constant that does not have a persistent memory location. It typically appears on the right-hand side of an assignment.
int a = 10;
ais an lvalue.10is an rvalue.
Q. What is an lvalue?
Answer
An lvalue is an expression that refers to an object with a valid memory location.
Characteristics- Has an address in memory.
- Can appear on the left-hand side of an assignment.
- Can also be used on the right-hand side.
int x = 5; x = 20;Here,
x is an lvalue because it occupies a memory location and its value can be modified.
Q. What is an rvalue?
Answer
An rvalue is an expression that represents a value but does not refer to a persistent memory location that you can assign to.
Characteristics- Usually appears on the right-hand side of an assignment.
- Cannot be assigned a new value.
- Often represents literals, temporary values, or the result of an expression.
10 x + y func()These expressions produce values but cannot be assigned to.
Q. Give examples of lvalues and rvalues.
Answer
Example 1int a = 10;
a→ lvalue10→ rvalue
Example 2
int x = 5; int y = x;
xis an lvalue.- Its value is used as an rvalue during the assignment to
y.
Example 3
int sum = x + y;
x→ lvaluey→ lvaluex + y→ rvalue
Q. Which expressions are invalid because they are not lvalues?
Answer
Invalid Example 110 = x;Error:
Left operand must be an lvalue.
Invalid Example 2
(x + y) = 100;Error:
The result of x + y is a temporary value (rvalue), so it cannot be assigned to.
Valid Example
x = y + 10;Here:
xis an lvalue.y + 10is an rvalue.
Q. Are array elements lvalues?
Answer
Yes.
Exampleint arr[5]; arr[0] = 100;
arr[0] refers to a memory location, so it is an lvalue.
Q. Is a pointer dereference an lvalue?
Answer
Yes.
Exampleint x = 10; int *p = &x; *p = 50;
*p refers to the memory location of x, making it an lvalue.
Q. Is a function return value an lvalue?
Answer
In most cases, no. If a function returns by value, the returned expression is an rvalue.
Example
int getValue(void)
{
return 10;
}
getValue() = 20;
This is invalid because the function returns a temporary value.
Q. What is the difference between lvalues and rvalues?
Answer
| Feature | lvalue | rvalue |
|---|---|---|
| Has a memory location | ✔ Yes | ❌ Usually No |
| Can appear on left side of assignment | ✔ Yes | ❌ No |
| Can appear on right side of assignment | ✔ Yes | ✔ Yes |
| Represents a temporary value | ❌ No | ✔ Yes |
| Examples | Variables, array elements, *pointer | Literals, expressions, function return values |
Q. Give practical embedded examples of lvalues and rvalues.
Answer
Example 1: GPIO Register#define GPIO_OUT (*(volatile uint32_t *)0x40020000) GPIO_OUT = 0x01;
GPIO_OUT is an lvalue because it refers to a hardware register with a fixed memory address.
Example 2: ADC Result
uint16_t adcValue; adcValue = ADC_DATA_REGISTER;
adcValue→ lvalueADC_DATA_REGISTER→ lvalue (memory-mapped register)- The value read from the register is used as an rvalue during the assignment.
Example 3: Bootloader Status Flag
volatile uint8_t bootStatus; bootStatus = 1;
bootStatus is an lvalue because it occupies a memory location that can be updated by software or hardware.
Q. Quick Interview Summary
| Expression | lvalue / rvalue |
|---|---|
int x; → x |
lvalue |
100 |
rvalue |
x + y |
rvalue |
arr[0] |
lvalue |
*ptr |
lvalue |
func() (returns by value) |
rvalue |
7.
What is undefined behavior?
Give five examples.
Q. What is undefined behavior? Give five examples.
Answer
Undefined Behavior (UB) is a situation where the C language standard does not define what the program should do. When undefined behavior occurs, the compiler is free to produce any result, including:
- Correct output
- Incorrect output
- Program crash
- Unexpected behavior
- Different results with different compilers or optimization levels
Since the behavior is undefined, developers must avoid writing code that relies on it.
Example 1: Using an Uninitialized Variable
int x;
printf("%d", x);
Why? The variable x contains an indeterminate value because it was never initialized.
Example 2: Dereferencing a NULL Pointer
int *ptr = NULL; *ptr = 10;
Why? Dereferencing a NULL pointer results in undefined behavior and often causes a program crash.
Example 3: Accessing an Array Out of Bounds
int arr[5]; arr[5] = 100;
Why? Valid indices are 0 to 4. Accessing arr[5] writes outside the array boundaries.
Example 4: Modifying a Variable Multiple Times Without a Sequence Point
int i = 5; i = i++;
Why? The expression modifies i and also uses its value in the same expression without a well-defined sequencing, resulting in undefined behavior.
Example 5: Signed Integer Overflow
int x = INT_MAX; x = x + 1;
Why? Overflowing a signed integer is undefined behavior according to the C standard.
Why is Undefined Behavior Dangerous in Embedded Systems?
- May cause random system crashes.
- Can produce different behavior with different compiler optimization levels.
- Makes debugging difficult.
- May lead to unpredictable ECU behavior in safety-critical systems.
- Can introduce intermittent field failures.
Quick Interview Tip
Interviewers often ask:
"Can undefined behavior produce the correct output?"
Answer: Yes. Undefined behavior may appear to work correctly during testing, but it is not reliable because the C standard provides no guarantee about its behavior. The same code may fail after recompilation, optimization changes, or when run on different hardware.
8.
Difference between
char *p = "Hello";
char p[] = "Hello";
Q. What is the difference between char *p = "Hello"; and char p[] = "Hello";?
Answer
Although both declarations appear similar, they are fundamentally different in terms of memory allocation, modifiability, and storage.
Example 1
char *p = "Hello";
pis a pointer.- The string literal
"Hello"is typically stored in the.rodata(read-only data) section. pstores the address of the first character.- The pointer can point to another string.
- Modifying the string through
presults in undefined behavior.
+-----+
p ----> | 'H' |
| 'e' |
| 'l' |
| 'l' |
| 'o' |
| '\0'|
+-----+
Example
char *p = "Hello"; p = "World"; // ✔ Allowed p[0] = 'h'; // ✘ Undefined Behavior
Example 2
char p[] = "Hello";
pis an array.- A copy of the string is stored in the array.
- The array elements can be modified.
- The array name cannot be reassigned.
+-----+-----+-----+-----+-----+------+
| 'H' | 'e' | 'l' | 'l' | 'o' | '\0' |
+-----+-----+-----+-----+-----+------+
p
Example
char p[] = "Hello"; p[0] = 'h'; // ✔ Allowed p = "World"; // ✘ Error
Comparison
| Feature | char *p = "Hello" |
char p[] = "Hello" |
|---|---|---|
| Type | Pointer | Character Array |
| Memory | Points to string literal | Stores its own copy |
| Can modify characters? | ❌ No (Undefined Behavior) | ✔ Yes |
| Can point elsewhere? | ✔ Yes | ❌ No |
| Typical Storage | .rodata (string literal) | Stack (local) or .data (global/static) |
Embedded Interview Tip
For constant strings that never change (e.g., firmware version, diagnostic messages), a pointer to a string literal is commonly used. If the string needs to be modified, use a character array.
9.
Why is C preferred over C++ in embedded firmware?
Q. Why is C preferred over C++ in embedded firmware?
Answer
C has been the dominant programming language for embedded firmware for decades because it provides direct hardware access, deterministic execution, minimal runtime overhead, and excellent portability. While modern embedded systems also use C++, C is still widely preferred for bare-metal and safety-critical firmware.
Reasons C is Preferred
-
Direct Hardware Access
C provides direct access to memory-mapped hardware registers using pointers.
#define GPIO_OUT (*(volatile uint32_t *)0x40020000) GPIO_OUT = 0x01;
This makes it ideal for writing device drivers, bootloaders, and peripheral control software.
-
Minimal Runtime Overhead
C has no hidden runtime features such as constructors, destructors, virtual tables (vtables), Runtime Type Information (RTTI), or exception handling.
This results in predictable execution and efficient use of CPU and memory resources.
-
Deterministic Execution
Embedded systems, especially real-time applications, require predictable execution times. C generates straightforward machine code with minimal hidden operations.
-
Smaller Code Size
C programs generally produce smaller executables, which is important for microcontrollers with limited Flash and RAM.
-
Easy Startup and Bootloader Development
Reset handlers, startup code, interrupt vector tables, linker scripts, and bootloaders are traditionally implemented in C because they interact directly with the hardware and processor initialization.
-
Wide Compiler and Hardware Support
Almost every microcontroller vendor provides C compilers, SDKs, BSPs, and peripheral libraries.
-
Simpler Certification
Safety-critical industries such as automotive, aerospace, and medical devices often prefer C because its behavior is easier to analyze and certify under standards such as ISO 26262 and IEC 61508.
C vs C++ Comparison
| Feature | C | C++ |
|---|---|---|
| Direct Hardware Access | ✔ Excellent | ✔ Excellent |
| Runtime Overhead | Very Low | Higher (depends on features used) |
| Code Size | Smaller | Can be larger |
| Deterministic Execution | ✔ High | Depends on language features |
| Object-Oriented Programming | ❌ No | ✔ Yes |
| Templates | ❌ No | ✔ Yes |
| Exceptions | ❌ No | ✔ Yes (often disabled in embedded projects) |
| RTTI | ❌ No | ✔ Yes (often disabled in embedded projects) |
Is C++ Used in Embedded Systems?
Yes. Modern embedded systems increasingly use C++, especially in:
- Embedded Linux
- Automotive AUTOSAR Adaptive
- ADAS and Autonomous Driving
- Industrial Automation
- Robotics
- IoT Gateways
However, many projects use only a controlled subset of C++ and avoid features such as exceptions, RTTI, and unrestricted dynamic memory allocation to maintain deterministic behavior.
Senior Interview Answer
C is preferred in embedded firmware because it provides direct hardware access, deterministic execution, low runtime overhead, and small code size. It integrates well with startup code, linker scripts, bootloaders, and device drivers. Although modern embedded systems also use C++, especially for complex applications, many safety-critical firmware projects continue to use C because of its simplicity, predictability, and easier certification. In practice, the choice depends on the application requirements rather than the language alone.
10.
Explain the compilation process.
.c
↓
Preprocessor
↓
Compiler
↓
Assembler
↓
Linker
↓
ELF/BIN
Q. Explain the compilation process in C.
Answer
The compilation process converts a C source file into an executable program through multiple stages. Each stage performs a specific task before generating the final executable (ELF) or binary (BIN/HEX) file.
Source File (.c)
│
▼
+----------------+
| Preprocessor |
+----------------+
│
▼
Expanded Source (.i)
│
▼
+----------------+
| Compiler |
+----------------+
│
▼
Assembly File (.s)
│
▼
+----------------+
| Assembler |
+----------------+
│
▼
Object File (.o)
│
▼
+----------------+
| Linker |
+----------------+
│
▼
Executable (.elf)
│
▼
Objcopy
│
▼
Binary (.bin/.hex)
1. Preprocessor
The preprocessor processes all preprocessor directives before compilation.
Tasks Performed- Expands
#includefiles. - Expands
#definemacros. - Removes comments.
- Processes conditional compilation (
#ifdef,#ifndef,#if).
#define PI 3.14
printf("%f", PI);
After preprocessing:
printf("%f", 3.14);
Output File
main.i
2. Compiler
The compiler converts the preprocessed C code into assembly language.
Tasks Performed- Syntax checking.
- Type checking.
- Optimization.
- Generates assembly code.
x = a + b;
May become:
LDR R0, [a] LDR R1, [b] ADD R2, R0, R1 STR R2, [x]Output File
main.s
3. Assembler
The assembler converts assembly instructions into machine code.
Tasks Performed- Converts assembly instructions into binary instructions.
- Creates symbol tables.
- Generates relocation information.
main.o
The object file contains machine code but is not yet executable because external symbols may still be unresolved.
4. Linker
The linker combines one or more object files and libraries to create the final executable.
Tasks Performed- Combines object files.
- Links static libraries.
- Resolves external function and variable references.
- Assigns memory addresses using the linker script.
- Generates the memory map.
main.o driver.o startup.o ↓ Application.elfOutput File
Application.elf
5. ELF File
The ELF (Executable and Linkable Format) file contains:
- Executable machine code.
- Debug information.
- Symbol table.
- Memory section information (.text, .data, .bss, etc.).
The ELF file is mainly used for debugging and programming during development.
6. BIN / HEX File
The ELF file is converted into a binary or Intel HEX file before programming the microcontroller.
Application.elf ↓ objcopy ↓ Application.bin or Application.hex
These files contain only the information required to program the Flash memory.
Compilation Flow Summary
| Stage | Input | Output | Main Purpose |
|---|---|---|---|
| Preprocessor | .c | .i | Expand macros and headers |
| Compiler | .i | .s | Generate assembly code |
| Assembler | .s | .o | Generate machine code |
| Linker | .o | .elf | Resolve symbols and create executable |
| Objcopy | .elf | .bin / .hex | Create Flash programming image |
Practical Automotive Example
Consider a bootloader project:
main.c flash_driver.c can_driver.c uds_server.c startup.s linker.ld
Build Process:
.c files
│
▼
Preprocessor
│
▼
Compiler
│
▼
Assembler
│
▼
main.o
flash_driver.o
can_driver.o
uds_server.o
startup.o
│
▼
Linker
│
▼
Bootloader.elf
│
▼
objcopy
│
▼
Bootloader.bin
The generated .bin file is programmed into the ECU's Flash memory using tools such as CANoe, Lauterbach, UDS Flash Bootloader, or production programming tools.
Senior Interview Answer
The compilation process consists of five stages: Preprocessor, Compiler, Assembler, Linker, and Objcopy. The preprocessor expands macros and header files, the compiler converts C code into assembly, the assembler generates object files containing machine code, and the linker combines all object files and libraries into an ELF executable while assigning memory addresses according to the linker script. Finally, the ELF is converted into a BIN or HEX file, which is programmed into the microcontroller's Flash memory.
Section B – Pointers (11–20)
11.
Explain every type of pointer in C.
- NULL pointer
- Void pointer
- Wild pointer
- Dangling pointer
- Function pointer
Q. Explain the different types of pointers in C.
Answer
Pointers are variables that store memory addresses. Based on their state and usage, pointers are commonly classified into the following types:
- NULL Pointer
- Void Pointer (Generic Pointer)
- Wild Pointer
- Dangling Pointer
- Function Pointer
1. NULL Pointer
A NULL pointer is a pointer that does not point to any valid memory location. It is initialized with NULL.
int *ptr = NULL;
if(ptr == NULL)
{
printf("Pointer is NULL");
}
Characteristics
- Points to no valid object.
- Used to indicate an invalid or uninitialized reference.
- Dereferencing a NULL pointer results in Undefined Behavior.
2. Void Pointer (Generic Pointer)
A void pointer can store the address of any data type. Since it has no associated data type, it must be cast before dereferencing.
Example
int x = 100;
void *ptr = &x;
printf("%d", *(int *)ptr);
Characteristics
- Can point to any data type.
- Cannot be dereferenced directly.
- Requires explicit type casting.
- Widely used in generic APIs such as
malloc()and callback interfaces.
3. Wild Pointer
A wild pointer is a pointer that has been declared but not initialized. It contains an indeterminate address.
Exampleint *ptr; *ptr = 10; // Undefined BehaviorCharacteristics
- Contains a random memory address.
- Dereferencing it may crash the program or corrupt memory.
- Always initialize pointers to
NULLor a valid address.
4. Dangling Pointer
A dangling pointer points to memory that is no longer valid.
Exampleint *ptr = (int *)malloc(sizeof(int)); free(ptr); *ptr = 20; // Undefined Behavior
After free(), the memory is released, but ptr still holds the old address.
free(ptr); ptr = NULL;Characteristics
- Points to deallocated or invalid memory.
- May cause crashes or data corruption.
- Set pointers to
NULLafter freeing memory.
5. Function Pointer
A function pointer stores the address of a function and allows the function to be called indirectly.
Example
int add(int a, int b)
{
return a + b;
}
int (*fp)(int, int);
fp = add;
printf("%d", fp(10, 20));
Characteristics
- Stores the address of a function.
- Used for callbacks.
- Commonly used in RTOS, interrupt vector tables, bootloaders, and driver frameworks.
Comparison
| Pointer Type | Description | Safe to Dereference? |
|---|---|---|
| NULL Pointer | Points to no valid object | ❌ No |
| Void Pointer | Generic pointer to any data type | ✔ Yes (after type casting) |
| Wild Pointer | Uninitialized pointer | ❌ No |
| Dangling Pointer | Points to freed or invalid memory | ❌ No |
| Function Pointer | Stores the address of a function | ✔ Yes |
Practical Embedded Examples
- NULL Pointer: Used to indicate that a peripheral driver has not been initialized.
- Void Pointer: Used in generic APIs such as
memcpy(), RTOS task parameters, andmalloc(). - Wild Pointer: Can occur if a driver pointer is declared but never initialized before accessing a hardware register.
- Dangling Pointer: May occur in Embedded Linux applications after calling
free(). - Function Pointer: Used in interrupt vector tables, callback registration, state machines, bootloaders, and communication stacks.
Quick Interview Summary
| Pointer Type | Definition |
|---|---|
| NULL Pointer | Points to no valid memory location |
| Void Pointer | Generic pointer that can hold any address |
| Wild Pointer | Declared but not initialized |
| Dangling Pointer | Points to memory that is no longer valid |
| Function Pointer | Stores the address of a function |
Senior Interview Tip
A common follow-up question is:
"What is the difference between a Wild Pointer and a Dangling Pointer?"
Answer:
- A Wild Pointer has never been initialized and contains an indeterminate address.
- A Dangling Pointer was once valid, but the memory it pointed to has been freed or has gone out of scope.
12.
Output?
int a=5;
int *p=&a;
printf("%d",*p++);
Explain carefully.
Q. What is the output of the following program? Explain carefully.
int a = 5;
int *p = &a;
printf("%d", *p++);
Answer
Output5
The expression *p++ is often confusing because both the dereference operator (*) and the post-increment operator (++) are used together.
Step 1: Understand Operator Precedence
The post-increment operator (++) has higher precedence than the dereference operator (*).
*(p++)
Not as:
(*p)++
Step 2: Initial State
a = 5
+-----+
0x100 -->| 5 |
+-----+
p = 0x100
The pointer p points to variable a.
Step 3: Evaluate p++
The post-increment operator returns the current value of the pointer and then increments it.
Current p = 0x100 Return 0x100 Increment p
Step 4: Dereference
The returned pointer (0x100) is dereferenced.
*(0x100) ↓ 5
Therefore, printf() prints:
5
Step 5: Final State
After the expression completes:
p = p + 1;
If p is an int *, it advances by sizeof(int) bytes.
Before p ----> a After p ----> Next int location
In this example, p no longer points to a. Dereferencing it afterwards would result in undefined behavior because it points past the only object.
Difference Between Similar Expressions
| Expression | Meaning |
|---|---|
*p++ |
*(p++) → Read value first, then increment the pointer. |
(*p)++ |
Increment the value pointed to by p. |
*++p |
Increment the pointer first, then dereference the new location. |
++*p |
Increment the value pointed to by p, then use the incremented value. |
Example Comparison
int a = 5; int *p = &a;
| Statement | Output | Value of a | Pointer Changed? |
|---|---|---|---|
printf("%d", *p++); |
5 | 5 | ✔ Yes |
printf("%d", (*p)++); |
5 | 6 | ❌ No |
printf("%d", ++*p); |
6 | 6 | ❌ No |
Senior Interview Tip
This is one of the most frequently asked pointer questions in Embedded C interviews.
A simple way to remember it is:
*p++→ Move the pointer after reading the value.(*p)++→ Increment the value, not the pointer.*++p→ Move the pointer first, then read the value.++*p→ Increment the value first, then use it.
13.
Difference
*p++
(*p)++
*(p++)
14.
Write a function to swap two numbers
- without third variable
- using pointers
15.
Why are function pointers heavily used in embedded systems?
Give AUTOSAR examples.
16.
Explain pointer arithmetic.
17.
Can pointer arithmetic be done on
void *
Why?
18.
Difference between
int (*fp)(void);
int *fp(void);
19.
What happens if
free(ptr);
free(ptr);
20.
Difference between
memcpy()
memmove()
Section C – Structures & Memory (21–28)
21.
Difference
struct
union
22.
Explain structure padding.
Why does it happen?
23.
How do you reduce padding?
24.
What is packing?
#pragma pack
When should it NOT be used?
25.
Output?
struct A
{
char c;
int i;
short s;
};
Predict structure size on a 32-bit ARM.
26.
Bitfields
Advantages and disadvantages?
27.
Why are bitfields generally avoided in automotive firmware?
28.
Explain endianess.
Little vs Big Endian.
Section D – Embedded Programming (29–38)
29.
Explain memory-mapped I/O.
30.
Why is
volatile
mandatory for peripheral registers?
31.
Difference between
volatile
const
volatile const
32.
Write a macro to SET, CLEAR and TOGGLE bits.
33.
Difference between
#define
const
enum
34.
Write a delay routine.
Why is software delay a bad idea?
35.
What happens if an interrupt modifies a shared variable?
36.
How do you protect shared resources?
37.
Why shouldn't
printf()
be used inside an ISR?
38.
What coding guidelines do you follow?
Explain
- MISRA C
- CERT C
Section E – Advanced C (39–45)
39.
Difference
#define MAX 100
const int max=100;
40.
Difference
inline
macro
41.
Recursive function
Advantages
Disadvantages
42.
Explain linker scripts.
43.
What is a startup file?
What happens before
main()
is executed?
44.
Explain weak symbols.
45.
Explain how static libraries differ from shared libraries.
Section F – Scenario Questions (46–50)
46.
Your firmware suddenly crashes after running for 3 days.
How will you debug it?
47.
CPU utilization suddenly reaches 100%.
Possible reasons?
48.
Firmware hangs after enabling interrupts.
How will you debug?
49.
Bootloader jumps to the application, but the application never starts.
Possible reasons?
50.
An ECU works perfectly in Debug mode but fails in Release mode.
What are the possible reasons?
Interviewer's Bonus Questions (Very Common)
These are frequently asked as follow-ups:
-
What is the difference between
volatileandatomic? -
Why is
volatilenot thread-safe? - Explain sequence points.
- Explain strict aliasing.
- Why should dynamic memory allocation be avoided in embedded systems?
- How does the linker resolve multiple definitions?
- Explain symbol tables.
- What is a linker map file?
-
Explain the difference between
.elf,.hex, and.bin. - Explain startup code in ARM Cortex-M.
- What happens when you dereference a NULL pointer?
- What is memory fragmentation?
- Why are function pointers useful in state machines?
-
Explain
restrictin C99. - What are common causes of stack overflow in embedded systems?
Comments
Post a Comment