Back to Home

C Questions for Microcontrollers

The article contains key questions on C programming for microcontrollers, build systems, interrupts, and RTOS. The selection is based on real interviews for middle/senior specialists. Code examples and terminology explanations.

C for Microcontrollers: 60+ Key Questions
Advertisement 728x90

Embedded C Essentials: Key Microcontroller Programming Questions

Microcontroller programmers must master C specifics under tight resource constraints. Below is a curated list of practical questions focusing on compilation, memory, and optimization. These topics frequently come up in interviews at embedded systems companies.

Key Keywords and Their Usage

Static limits variable visibility to the current file or function, preserving values between calls for local statics. Volatile stops the compiler from optimizing variables changed externally (registers, memory, interrupts).

Example of problematic code:

Google AdInline article slot
int square(volatile int *ptr) {
    return *ptr * *ptr;
}

Here, volatile only applies to the first dereference; the second might get optimized away. Fix: (ptr) (*ptr).

Const volatile works for read-only external data like status registers. Register suggests storing a variable in a CPU register (deprecated in C11). Restrict tells the compiler pointers don't alias, enabling vectorization.

Memory Management and Structures

Global const variables go in Flash memory. For a 32 KB constant array filled with 0xFF, use a linker script section with fill(0xFF).

Google AdInline article slot

Bit operations: check bit with n & (1 << k), clear with n & ~(1 << k). Float comparison: fabs(a - b) < EPSILON.

Structure sizes depend on alignment. For

struct Foo {
    int iiii;
    char c;
};

size is 8 bytes (4 bytes padding). GCC packing: __attribute__((packed)).

Google AdInline article slot

String declaration examples:

char str5[]={'s','t','r','i','n','g'};  // size:7 len:6
char *str1="string";                    // size:4 len:6 (pointer)
char str3[]="string";                   // size:7 len:6
char str4[10]="string";                 // size:10 len:6

Build Systems and Toolchains

Build systems automate compilation, linking, and dependencies. Make handles file changes, while Ninja is faster thanks to parallelism and simplicity.

VPATH in GNU Make searches for sources in specified directories.

Code pipeline: .c → .i (preprocessor) → .s (assembly) → .o → .elf → .bin/.hex.

Disable GCC warning: #pragma GCC diagnostic ignored "-Wrestrict".

Shrink firmware: -ffunction-sections -fdata-sections, ld --gc-sections, and -Os optimization.

Build Artifacts

  • .o — object files
  • .elf — executable with symbols
  • .hex/.bin — for flashing (hex includes addresses)

Binutils: objdump (disassembler), objcopy (converter), readelf (ELF analyzer).

Verify build: make V=1 or -n (dry-run).

Data Structures and Algorithms

Circular buffer — ring array with head/tail indices, unlike FIFO which shifts elements.

Linked list removal: set prev->next = node->next (needs prev) or node->next->prev = node->prev (doubly-linked).

Polymorphism in C: function pointers in structs (vtable-style).

No if/switch task:

void print_num(int n) {
    const char *nums[] = {"","One","Two"};
    printf("%s", nums[n]);
}

do-while(0) for macros — single iteration with multiple statements.

Interrupts and RTOS

Interrupt — hardware mechanism jumping to an ISR on an event. Vector table holds ISR addresses. Cortex-M4: 12–16 cycles for entry (context save).

Flow: hardware saves PC/LR, loads vector, jumps to ISR, returns via POP.

Reentrant function — safe for concurrent calls (no statics/locals without sync).

In RTOS:

  • Thread — execution unit with its own stack
  • Mutex — mutual exclusion
  • Semaphore — event counter
  • Spinlock — busy-wait lock

Code issue: deadlock from lock order (TaskA: ma→mb, TaskB: mb→mc→ma).

Priority inversion: high-priority task waits for low-priority. Fix: priority inheritance.

Atomic Operations

Example: __atomic_exchange_n(&x, &y, __ATOMIC_SEQ_CST).

Bit-banding — direct bit access without masks (Cortex-M3/4).

Toolchain and Debugging

ABI — binary interface (calling convention).

Segments: .text (Flash code), .data (RAM initialized), .bss (RAM zeros), .rodata (Flash constants).

Expand preprocessor: gcc -E file.c > expanded.i.

Key docs: datasheet, reference manual, programming manual, schematics.

Key Takeaways

  • Volatile is essential for peripheral registers
  • Packed structs save RAM but slow access
  • RTOS needs thread-safe code and critical section protection
  • Build optimization: sections + gc-sections
  • Interrupts use the current thread's stack

For 0xFF padding in structs: linker script with fill or __attribute__((packed)) + manual fill.

— Editorial Team

Advertisement 728x90

Read Next