10 Must Ask C Interview Questions For Hiring Managers

Posted on March 21 2024 by Interview Zen Team

Introduction

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:

  1. Code review: Present C code for bug identification and optimization
  2. Memory debugging: Test knowledge of tools like Valgrind, AddressSanitizer
  3. Systems knowledge: Assess understanding of operating system concepts
  4. Embedded experience: For embedded roles, test hardware interaction knowledge
  5. 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.