In embedded systems, C remains the dominant language due to its deterministic behavior and direct hardware access. That same power also makes it dangerous: small mistakes in pointer handling or memory management can crash systems, corrupt data, or create security vulnerabilities.
The following 10 classic interview questions are frequently used to assess whether a developer truly understands C at a system level.
β οΈ The Danger of gets()
#
Question: Identify the issue in the following code:
#include <stdio.h>
int main(void)
{
char buff[10];
memset(buff, 0, sizeof(buff));
gets(buff); // Problematic line
printf("The buffer entered is [%s]\n", buff);
return 0;
}
Answer:
gets() performs no bounds checking. If the input exceeds the buffer size, it causes a stack-based buffer overflow, overwriting adjacent memory. This function is so dangerous that it has been removed from the C standard.
Correct approach: Use fgets() with an explicit length limit.
π Exploiting strcpy()
#
Question: Can you bypass the password protection below without knowing the password?
#include <stdio.h>
int main(int argc, char *argv[])
{
int flag = 0;
char passwd[10];
memset(passwd, 0, sizeof(passwd));
strcpy(passwd, argv[1]);
if (strcmp("LinuxGeek", passwd) == 0)
flag = 1;
if (flag)
printf("Password cracked\n");
else
printf("Incorrect password\n");
return 0;
}
Answer:
Yes. strcpy() does not validate destination size. A carefully crafted input longer than 10 bytes can overflow passwd and overwrite the adjacent flag variable on the stack, setting it to a non-zero value and bypassing authentication.
This is a textbook stack overflow exploit.
π§© The Return Type of main()
#
Question: Is this valid C?
void main(void) { }
Answer: It may compile on some embedded toolchains, but it is non-standard. The C standard requires:
int main(void)
In hosted systems, the return value is passed to the operating system. Even in bare-metal systems, using int main() ensures portability and correctness.
π§ Memory Leaks vs Program Exit #
Question: Does this leak memory?
char *ptr = malloc(10);
return;
Answer: Yesβwhile the program is running. Once the process terminates, the OS reclaims memory.
However, in embedded systems or long-running tasks, this pattern inside loops will exhaust the heap, leading to crashes or undefined behavior.
π₯ Modifying the Base Pointer #
Question: Why does this crash for input "freeze" but not "zebra"?
while (*ptr != 'z') {
ptr++;
}
free(ptr);
Answer:
free() must receive the exact pointer returned by malloc(). Incrementing ptr changes its value. Passing a modified pointer to free() causes undefined behavior, often a segmentation fault.
Always preserve the original base pointer.
πͺ _exit() vs exit()
#
Question: Why is the cleanup handler not called?
atexit(func);
_exit(0);
Answer:
_exit() terminates the process immediately, bypassing:
atexit()handlers- I/O buffer flushing
- Standard cleanup routines
Use exit() or return from main() if cleanup is required.
𧬠void* and Generic Interfaces
#
Question: How can a function accept arguments of any type?
Answer:
Use a void * pointer. For multiple parameters, encapsulate them in a structure.
int my_function(void *args);
This technique underpins APIs like pthread_create() and callback interfaces.
β Operator Precedence: *ptr++
#
Question: What does this print if ptr points to "Linux"?
printf("%c", *ptr++);
Answer:
- First call prints:
L - Second call prints:
i
Postfix ++ has higher precedence than *, but the dereference happens before the increment due to postfix semantics.
Equivalent to:
*(ptr++);
π§± Read-Only Memory Segments #
Question: Why does this crash?
char *ptr = "Linux";
*ptr = 'T';
Answer: String literals are typically stored in read-only memory (RODATA). Writing to them triggers a memory protection fault.
Correct usage:
char ptr[] = "Linux";
ptr[0] = 'T';
π€ Returning Addresses of Local Variables #
Question: Why is this incorrect?
int *func(void)
{
int a = 10;
return &a;
}
Answer:
a resides on the stack. Once the function returns, its stack frame is destroyed. The returned pointer references invalid memory, leading to undefined behavior.
Only return:
- Heap-allocated memory
- Static variables
- Caller-provided buffers
β Conclusion #
These questions expose the sharp edges of Cβpointer arithmetic, memory ownership, stack lifetime, and undefined behavior. Mastery of these concepts is essential for building reliable, secure embedded systems, and they remain a reliable litmus test in technical interviews.