C for Systems & Embedded Programming
Pointers, memory corruption, and the qualifiers that matter once nothing is managed for you.
Questions
Easy / Med / Hard
Your accuracy
Embedded and systems C is the same language as application C, but the rules that keep application code safe -- a garbage collector, a huge heap, an OS that kills a misbehaving process -- are mostly absent. The bugs that are merely annoying elsewhere are the ones interviewers probe for here.
Pointers are addresses, not magic. A pointer is a number that names a location in memory; *p reads or writes what lives there, &x asks for where x lives. Pointer arithmetic moves by the size of the pointed-to type, not by one byte -- p + 1 on an int* moves four bytes forward, not one. A dangling pointer (one that still holds an address whose memory has since been freed or gone out of scope) reads or writes memory that is no longer yours; the read might look fine for a while, which is exactly what makes it dangerous.
Memory corruption is the category interviewers keep coming back to. A buffer overflow writes past the end of an array -- classically strcpy into a fixed buffer with no length check -- and depending on what sits next in memory, that can silently corrupt an adjacent variable, a return address, or nothing at all until it does. A use-after-free reads or writes through a pointer whose memory has already been released. Both are "the code compiles and often runs" bugs, which is what makes them worse than a crash: a crash tells you immediately, corruption tells you eventually and somewhere else.
`volatile` tells the compiler to stop optimising a read or write away. It exists for exactly one reason: some memory changes for a cause the compiler cannot see -- a hardware register, a value another thread writes, a signal handler. Without volatile, the compiler is free to assume a variable that your code never writes to inside a loop can't change, cache it in a register, and never re-read it -- which is correct C semantics and wrong for a status register that's changing under it. const says "not written through this name," a compile-time promise the compiler checks, not a runtime protection. static at file scope limits a name to that translation unit; on a local variable, it makes the variable persist across calls instead of living on the stack.
Header files declare, they do not define. A .h file tells the compiler a function's or a struct's shape so other files can call it or use it correctly; the actual code lives in a .c file compiled separately and linked together. Getting a declaration wrong across files is a linker error waiting to happen, and it is why header guards (#ifndef/#define or #pragma once) exist -- to stop the same declarations being read twice when headers include each other.