10 Must Ask C Interview Questions For Hiring Managers
Posted on March 21 2024 by Interview Zen TeamIntroduction
Hiring the right software developers is important for the success of any organization in today’s technology-driven world. However, identifying skilled C programmers can be challenging given the language’s complexity and the depth of knowledge required for effective systems programming.
C programming remains fundamental to computer science education and systems development. According to the TIOBE Index 2024, C consistently ranks among the top programming languages, maintaining its relevance in embedded systems, operating system development, and performance-critical applications where direct hardware control is essential.
This comprehensive guide provides hiring managers with essential C programming interview questions designed to evaluate candidates’ understanding of memory management, pointer arithmetic, system-level programming, and practical application in performance-critical environments.
What is C Programming?
C is a general-purpose, procedural programming language developed by Dennis Ritchie at Bell Labs between 1969 and 1973. Known for its efficiency, portability, and close-to-hardware programming capabilities, C serves as the foundation for many modern programming languages and operating systems.
Key characteristics of C include:
- Low-level control: Direct memory management and hardware access
- Efficiency: Minimal runtime overhead and fast execution
- Portability: Code runs on various platforms with minimal changes
- Structured programming: Clear, modular code organization
- Rich library: Extensive standard library functions
Top 10 Must-Ask C Interview Questions
1. What is the difference between malloc()
and calloc()
?
Memory allocation is fundamental to C programming and systems development.
Example Answer: “Both allocate memory dynamically, but differ in initialization and parameters:
malloc(): Allocates uninitialized memory block
int *ptr = malloc(5 * sizeof(int)); // Uninitialized values
calloc(): Allocates and initializes memory to zero
int *ptr = calloc(5, sizeof(int)); // All values set to 0
Key differences:
- malloc() is faster (no initialization)
- calloc() takes two parameters (count, size)
- calloc() prevents garbage values
- Both require free() to avoid memory leaks”
2. Explain pointers and pointer arithmetic in C.
Pointers are crucial for understanding C’s memory model and efficiency.
Example Answer: “Pointers store memory addresses of variables:
Basic pointer operations:
int x = 10;
int *ptr = &x; // ptr holds address of x
int value = *ptr; // Dereference: gets value at address
// Pointer arithmetic
int arr[5] = {1, 2, 3, 4, 5};
int *p = arr; // Points to first element
p++; // Points to second element (address + sizeof(int))
int val = *(p+2); // Access third element from current position
Important concepts:
- Pointer arithmetic depends on data type size
- Array names are constant pointers
- Pointer arithmetic enables efficient array traversal
- Null pointers should be checked before dereferencing”
3. What is the difference between pass by value
and pass by reference
?
Understanding parameter passing is essential for function design.
Example Answer: “C uses pass by value by default, but can simulate pass by reference with pointers:
Pass by Value: Copy of variable passed
void increment(int x) {
x++; // Only modifies local copy
}
int main() {
int num = 5;
increment(num); // num remains 5
return 0;
}
Pass by Reference (using pointers): Address passed
void increment(int *x) {
(*x)++; // Modifies original variable
}
int main() {
int num = 5;
increment(&num); // num becomes 6
return 0;
}
Arrays are automatically passed by reference (passed as pointers).”
4. Explain the different storage classes in C.
Storage classes determine variable lifetime, scope, and memory location.
Example Answer: “C has four storage classes:
auto: Default for local variables
void function() {
auto int x = 10; // Stored on stack, destroyed when function exits
}
static: Retains value between function calls
void counter() {
static int count = 0; // Initialized only once
count++;
printf("%d\n", count);
}
extern: Declares variable defined elsewhere
extern int global_var; // Declaration (definition in another file)
register: Suggests storing in CPU register
register int i; // Hint for frequently used variables
Key points:
- auto: Automatic storage, function scope
- static: Persistent storage, file/function scope
- extern: Global linkage, file scope
- register: Fast access suggestion (compiler may ignore)”
5. What are the differences between arrays and pointers?
This tests deep understanding of C’s memory model and data structures.
Example Answer: “Arrays and pointers are related but distinct:
Arrays:
int arr[5] = {1, 2, 3, 4, 5};
// arr is a constant pointer to first element
// sizeof(arr) = 20 bytes (5 * sizeof(int))
// Cannot reassign arr to point elsewhere
Pointers:
int *ptr = arr;
// ptr is a variable that can point to different addresses
// sizeof(ptr) = 8 bytes (on 64-bit system)
// Can reassign: ptr = &other_variable;
Key differences:
- Arrays have fixed size and location
- Pointers can be reassigned and moved
- Array names decay to pointers in expressions
- Array subscripting works with both: arr[i] == *(arr+i) == *(ptr+i)”
6. Explain memory layout of a C program.
Understanding memory organization is crucial for systems programming.
Example Answer: “C program memory is organized into segments:
Text Segment: Executable code (read-only) Data Segment: Global and static initialized variables BSS Segment: Global and static uninitialized variables (zero-initialized) Heap: Dynamic memory allocation (malloc, calloc) Stack: Local variables, function parameters, return addresses
High Memory
+------------------+
| Stack | ← Grows downward
| ↓ |
+------------------+
| ↑ |
| Heap | ← Grows upward
+------------------+
| BSS Segment | ← Uninitialized data
+------------------+
| Data Segment | ← Initialized data
+------------------+
| Text Segment | ← Program code
+------------------+
Low Memory
Memory management:
- Stack: Automatic allocation/deallocation
- Heap: Manual allocation with malloc/free
- Memory leaks occur when heap memory isn’t freed”
7. What is a dangling pointer and how do you avoid it?
Pointer safety is critical for preventing crashes and security vulnerabilities.
Example Answer: “A dangling pointer points to memory that has been deallocated:
Common causes:
// 1. Freeing memory but not setting pointer to NULL
int *ptr = malloc(sizeof(int));
free(ptr);
// ptr still holds the address (now invalid)
*ptr = 10; // Undefined behavior - dangling pointer
// 2. Returning address of local variable
int* function() {
int x = 10;
return &x; // x destroyed when function exits
}
// 3. Double free
free(ptr);
free(ptr); // Second free on already freed memory
Prevention strategies:
// Set pointers to NULL after freeing
free(ptr);
ptr = NULL;
// Check for NULL before using
if (ptr != NULL) {
*ptr = value;
}
// Use static or dynamic allocation for data that outlives function scope
```"
### 8. Explain the concept of function pointers.
Function pointers enable callback mechanisms and polymorphism.
**Example Answer**: "Function pointers store addresses of functions, enabling dynamic function calls:
**Declaration and usage**:
```c
// Function pointer declaration
int (*operation)(int, int);
// Functions to point to
int add(int a, int b) { return a + b; }
int multiply(int a, int b) { return a * b; }
int main() {
operation = add;
int result = operation(5, 3); // Calls add(5, 3)
operation = multiply;
result = operation(5, 3); // Calls multiply(5, 3)
return 0;
}
Array of function pointers (useful for state machines):
int (*operations[])(int, int) = {add, subtract, multiply, divide};
int result = operations[2](10, 5); // Calls multiply(10, 5)
Benefits: Callback functions, event handling, implementing polymorphism in C.”
9. What is the difference between struct
and union
?
Understanding composite data types is essential for data organization.
Example Answer: “Both group related data, but differ in memory allocation:
struct: All members have separate memory locations
struct Person {
char name[20]; // 20 bytes
int age; // 4 bytes
float salary; // 4 bytes
}; // Total: ~28 bytes (with padding)
struct Person p;
strcpy(p.name, "John");
p.age = 30;
p.salary = 50000.0f; // All values stored simultaneously
union: All members share the same memory location
union Data {
int i; // 4 bytes
float f; // 4 bytes
char str[20]; // 20 bytes
}; // Total: 20 bytes (largest member)
union Data d;
d.i = 10; // Stores integer
d.f = 220.5f; // Overwrites integer value
Use cases:
- struct: Related data that coexists (employee records)
- union: Memory-efficient storage when only one value needed at a time”
10. Explain the volatile keyword and when to use it.
The volatile keyword is crucial for systems programming and embedded development.
Example Answer: “volatile prevents compiler optimizations on variables that may change unexpectedly:
Without volatile (compiler may optimize):
int flag = 0;
while (flag == 0) {
// Compiler might optimize this to infinite loop
// assuming flag never changes
}
With volatile:
volatile int flag = 0;
while (flag == 0) {
// Compiler will always read flag from memory
// not assume it's unchanged
}
When to use volatile:
- Hardware registers: Memory-mapped I/O
- Interrupt service routines: Variables modified by ISRs
- Multi-threaded applications: Shared variables (though atomic operations preferred)
- Embedded systems: Hardware status flags
Example in embedded programming:
c
volatile uint32_t *GPIO_STATUS = (uint32_t*)0x40020014;
while ((*GPIO_STATUS & 0x01) == 0) {
// Wait for hardware flag
}
”
Advanced C Programming Concepts
For senior positions, explore these advanced topics:
Memory Management Patterns
- RAII-style: Resource acquisition patterns
- Memory pools: Custom allocators for performance
- Stack vs heap: Trade-offs and optimization strategies
Systems Programming
- System calls: Interaction with operating system
- Inter-process communication: Pipes, shared memory, signals
- File I/O: Low-level vs standard library operations
Optimization Techniques
- Cache-friendly code: Memory access patterns
- Compiler optimizations: Understanding assembly output
- Profiling: Identifying and fixing performance bottlenecks
Practical Assessment Tips
When interviewing C candidates:
- Code review: Present C code for bug identification and optimization
- Memory debugging: Test knowledge of tools like Valgrind, AddressSanitizer
- Systems knowledge: Assess understanding of operating system concepts
- Embedded experience: For embedded roles, test hardware interaction knowledge
- Performance awareness: Discuss optimization strategies and trade-offs
Conclusion
C programming remains fundamental for systems development, embedded programming, and performance-critical applications. These interview questions help evaluate both theoretical understanding and practical application skills, enabling you to identify candidates capable of building efficient, reliable systems-level software.
The best C programmers combine deep technical knowledge with understanding of computer architecture, memory management, and performance optimization techniques essential for modern systems development.
Consider using Interview Zen’s technical interview platform to create comprehensive C programming assessments and observe candidates’ problem-solving approaches in real-time during systems programming interviews.