Pointers and Functions
Master this concept with detailed explanations and interactive coding examples.
Interview Questions & Answers
Technical InterviewPointers and Functions in C
If functions make your program structured, pointers make it powerful. Once you understand how pointers work with functions, you start thinking in terms of memory, not just variables.
This topic is extremely important for interviews, system programming, and real-world C development.
1. What is a Pointer?
A pointer is a variable that stores the address of another variable.
Basic Example
#include <stdio.h>
int main() {
int a = 10;
int *ptr = &a;
printf("Value of a = %d\n", a);
printf("Address of a = %p\n", &a);
printf("Pointer value (address) = %p\n", ptr);
printf("Value using pointer = %d\n", *ptr);
return 0;
}
Explanation:
&agives the address of variableaptrstores that address*ptrgives the value stored at that address
2. Why Use Pointers with Functions?
Normally, C uses call by value. That means the function receives a copy of the variable.
If you want the function to modify the original variable, you must pass its address using pointers.
3. Call by Value vs Call by Reference
Call by Value (Original Value Not Changed)
#include <stdio.h>
void change(int x) {
x = 50;
}
int main() {
int a = 10;
change(a);
printf("Value of a = %d", a);
return 0;
}
Output: 10
Call by Reference (Using Pointers)
#include <stdio.h>
void change(int *x) {
*x = 50;
}
int main() {
int a = 10;
change(&a);
printf("Value of a = %d", a);
return 0;
}
Output: 50
Memory Explanation:
- We pass the address of
a - The function modifies the value stored at that address
- The original variable changes
4. Swapping Two Numbers Using Pointers
This is a classic interview question.
#include <stdio.h>
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int x = 5, y = 10;
swap(&x, &y);
printf("x = %d, y = %d", x, y);
return 0;
}
5. Passing Pointer to an Array
Arrays and pointers are closely related. The array name itself acts as a pointer to its first element.
Example
#include <stdio.h>
void printArray(int *arr, int size) {
for(int i = 0; i < size; i++) {
printf("%d ", *(arr + i));
}
}
int main() {
int arr[] = {10, 20, 30, 40};
printArray(arr, 4);
return 0;
}
6. Returning Pointer from a Function
You must be careful while returning pointers. Never return a pointer to a local variable.
Wrong Way
int* wrongFunction() {
int a = 10;
return &a; // Dangerous (local variable)
}
This leads to undefined behavior.
Correct Way (Using Static Variable)
#include <stdio.h>
int* correctFunction() {
static int a = 10;
return &a;
}
int main() {
int *ptr = correctFunction();
printf("%d", *ptr);
return 0;
}
7. Pointer to Pointer
A pointer that stores the address of another pointer.
#include <stdio.h>
int main() {
int a = 5;
int *p = &a;
int **pp = &p;
printf("Value of a = %d\n", **pp);
return 0;
}
8. Function Pointer
A function pointer stores the address of a function.
Example
#include <stdio.h>
int add(int a, int b) {
return a + b;
}
int main() {
int (*ptr)(int, int);
ptr = add;
printf("Result = %d", ptr(3, 7));
return 0;
}
9. Dynamic Memory Allocation with Functions
When you want flexible memory, you use malloc() from stdlib.
#include <stdio.h>
#include <stdlib.h>
int* createArray(int size) {
int *arr = (int*) malloc(size * sizeof(int));
for(int i = 0; i < size; i++) {
arr[i] = i + 1;
}
return arr;
}
int main() {
int size = 5;
int *ptr = createArray(size);
for(int i = 0; i < size; i++) {
printf("%d ", ptr[i]);
}
free(ptr);
return 0;
}
Important Interview Questions
- What is the difference between pointer and array?
- Why canβt we return address of local variable?
- Explain double pointer with real example.
- What is function pointer and where is it used?
- Explain memory allocation using malloc and free.
Memory Understanding (Very Important)
- Stack memory stores local variables
- Heap memory is allocated using malloc
- Static variables stay for entire program execution
- Pointers help manipulate memory directly
Interactive Code Playground
Live CodingTips:
- Use Ctrl + Enter to run code
- Include <stdio.h> for printf/scanf
- main() function must return int
Sample Snippets:
Detailed Explanation
Pointers and FunctionsUnderstanding Pointers and Functions in C
Pointers and functions together form the backbone of advanced C programming. Pointers can be used as function parameters (to modify variables), as return values, and even to create function pointers that allow dynamic function calls. This powerful combination enables callbacks, dynamic dispatch, and efficient memory management.
- Pointer Parameters - Passing pointers to functions allows modification of original variables (simulated pass-by-reference).
- Function Pointers - Variables that store the address of functions, enabling dynamic function calls and callbacks.
- Pointer Return Types - Functions can return pointers to dynamically allocated memory, static data, or structures.
- Pointer to Function as Parameter - Functions can accept function pointers as arguments, enabling flexible algorithms (e.g., qsort comparator).
- Array of Function Pointers - Creating jump tables for implementing state machines or command dispatchers.
- Null Pointer Checks - Always validate pointer parameters and return values to prevent crashes.
Key Points to Remember:
Pointers allow functions to modify caller's variables by passing addresses rather than values.
return_type (*pointer_name)(parameter_types) - parentheses matter!
Never return pointers to local variables - they cease to exist after function returns.
Function pointers enable callbacks - passing one function to be called by another.
Expert Interview Tips:
When explaining pointer parameters, emphasize: 'The function receives a copy of the pointer, but both copies point to the same memory.'
Common mistake: Forgetting to dereference pointer parameters. *ptr = value modifies the pointed-to variable; ptr = value modifies the pointer itself.
Pro tip: Use typedef for function pointers to improve readability: typedef int (*Comparator)(const void*, const void*);
Common Follow-up Questions:
Pointers as function parameters allow functions to modify variables in the calling scope. This simulates pass-by-reference in C. The function receives the address of the variable, then uses the dereference operator * to access and modify the original value.
#include <stdio.h>
// Function that modifies variables via pointers
void swap(int *a, int *b) {
int temp = *a; // Dereference to get values
*a = *b;
*b = temp;
}
void increment(int *ptr) {
(*ptr)++; // Parentheses needed because ++ has higher precedence than *
}
int main() {
int x = 10, y = 20;
printf("Before swap: x = %d, y = %d\n", x, y);
swap(&x, &y); // Pass addresses
printf("After swap: x = %d, y = %d\n", x, y);
increment(&x);
printf("After increment: x = %d\n", x);
return 0;
}
Function pointers store the address of a function, allowing functions to be called indirectly, passed as arguments, or stored in data structures.
Declaration syntax: return_type (*pointer_name)(parameter_types)
Key points:
- Parentheses around
*pointer_nameare crucial - Can point to any function with matching signature
- Use pointer name like a regular function to call
#include <stdio.h>
// Some functions with same signature
int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }
int multiply(int a, int b) { return a * b; }
int main() {
// Declare function pointer
int (*operation)(int, int);
// Point to add function
operation = &add; // & is optional, function name decays to pointer
printf("Add: %d\n", operation(10, 5));
// Re-point to subtract
operation = subtract; // Without &
printf("Subtract: %d\n", operation(10, 5));
// Array of function pointers
int (*operations[])(int, int) = {add, subtract, multiply};
char *op_names[] = {"Add", "Subtract", "Multiply"};
for(int i = 0; i < 3; i++) {
printf("%s: %d\n", op_names[i], operations[i](10, 5));
}
return 0;
}
Callbacks are functions passed as arguments to other functions, which then 'call back' at appropriate times. Common in event handling, sorting, and state machines.
Example: The C standard library's qsort function accepts a comparator callback.
#include <stdio.h>
#include <stdlib.h>
// Callback function type definition
typedef int (*Comparator)(const void*, const void*);
// Generic sort function that accepts callback
void sortAndPrint(int arr[], int size, Comparator cmp, const char* order) {
qsort(arr, size, sizeof(int), cmp);
printf("%s order: ", order);
for(int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
// Comparator callbacks
int ascending(const void *a, const void *b) {
return (*(int*)a - *(int*)b);
}
int descending(const void *a, const void *b) {
return (*(int*)b - *(int*)a);
}
// Event simulation
typedef void (*EventHandler)(const char*);
void onButtonClick(EventHandler handler) {
printf("Button clicked! ");
handler("Button event");
}
void handleEvent(const char *event) {
printf("Handling: %s\n", event);
}
int main() {
int numbers[] = {42, 13, 7, 89, 23, 56};
int size = sizeof(numbers)/sizeof(numbers[0]);
// Use callbacks for sorting
sortAndPrint(numbers, size, ascending, "Ascending");
sortAndPrint(numbers, size, descending, "Descending");
// Event callback
onButtonClick(handleEvent);
return 0;
}
Risks of returning pointers:
- Dangling pointers: Returning pointer to local variable (invalid after function returns)
- Memory leaks: Returning pointer to dynamically allocated memory without clear ownership
- Null pointers: Returning NULL without proper error handling
- Shared data issues: Returning pointer to static data (not thread-safe)
Best practices:
- Never return pointers to local variables
- Document ownership: who is responsible for freeing?
- Check for NULL before using returned pointers
- Consider returning by value for small structures
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// WRONG: Returns pointer to local variable
int* badFunction() {
int local = 42;
return &local; // Dangling pointer!
}
// RIGHT: Returns pointer to dynamic memory
int* createArray(int size) {
int *arr = (int*)malloc(size * sizeof(int));
if(arr == NULL) {
return NULL; // Indicate failure
}
for(int i = 0; i < size; i++) {
arr[i] = i * 10;
}
return arr; // Caller must free()
}
// RIGHT: Returns pointer to static data (but not thread-safe)
char* getErrorMessage(int code) {
static char msg[100]; // Static duration
sprintf(msg, "Error code: %d", code);
return msg; // Safe, but subsequent calls overwrite
}
// RIGHT: Caller provides buffer
void getString(char *buffer, int size) {
strncpy(buffer, "Hello World", size - 1);
buffer[size - 1] = '\0';
}
int main() {
// int *p = badFunction(); // Never use this!
int *arr = createArray(5);
if(arr != NULL) {
for(int i = 0; i < 5; i++) {
printf("%d ", arr[i]);
}
printf("\n");
free(arr); // MUST free
}
char *msg = getErrorMessage(404);
printf("%s\n", msg);
char buffer[50];
getString(buffer, 50);
printf("%s\n", buffer);
return 0;
}
Pointer parameter (int *ptr): Allows modification of the pointed-to data, but not the pointer itself.
Pointer to pointer parameter (int **ptr): Allows modification of the pointer itself (what it points to). Used when:
- Function needs to allocate memory and return it via parameter
- Function needs to modify a pointer variable in caller's scope
- Working with arrays of pointers
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Can modify data, but not the pointer
void modifyData(int *ptr) {
*ptr = 100; // OK - modifies pointed-to data
// ptr = NULL; // Only affects local copy, not caller's pointer
}
// Can modify the pointer itself
void modifyPointer(int **ptr_ptr) {
static int new_value = 999;
*ptr_ptr = &new_value; // Changes what caller's pointer points to
}
// Allocate memory and return via pointer-to-pointer
void createString(char **str_ptr, int size) {
*str_ptr = (char*)malloc(size * sizeof(char));
if(*str_ptr != NULL) {
strcpy(*str_ptr, "Hello");
}
}
int main() {
int x = 42;
int *p = &x;
printf("Before modifyData: *p = %d\n", *p);
modifyData(p);
printf("After modifyData: *p = %d\n", *p);
printf("Before modifyPointer: p points to %d\n", *p);
modifyPointer(&p); // Pass address of pointer
printf("After modifyPointer: p points to %d\n", *p);
char *str = NULL;
createString(&str, 20);
if(str != NULL) {
printf("String: %s\n", str);
free(str);
}
return 0;
}
Array of function pointers (jump table) allows selecting and calling functions based on indices, useful for:
- State machines
- Command dispatchers (menu systems)
- Operation tables (calculators)
- Plugin architectures
#include <stdio.h>
// Calculator operations
typedef double (*MathOp)(double, double);
double add(double a, double b) { return a + b; }
double subtract(double a, double b) { return a - b; }
double multiply(double a, double b) { return a * b; }
double divide(double a, double b) { return b != 0 ? a / b : 0; }
// Menu system
typedef void (*MenuFunction)(void);
void option1() { printf("You selected Option 1\n"); }
void option2() { printf("You selected Option 2\n"); }
void option3() { printf("You selected Option 3\n"); }
void option4() { printf("You selected Option 4\n"); }
int main() {
// Calculator jump table
MathOp operations[] = {add, subtract, multiply, divide};
char *op_symbols[] = {"+", "-", "*", "/"};
double a = 20, b = 5;
for(int i = 0; i < 4; i++) {
printf("%.1f %s %.1f = %.1f\n", a, op_symbols[i], b, operations[i](a, b));
}
// Menu system using function pointer array
MenuFunction menu[] = {option1, option2, option3, option4};
printf("\nMenu System:\n");
for(int choice = 0; choice < 4; choice++) {
printf("\nExecuting choice %d:\n", choice + 1);
menu[choice]();
}
// State machine example
printf("\nState Machine:\n");
typedef enum {STATE_IDLE, STATE_RUN, STATE_PAUSE, STATE_STOP} State;
void idle() { printf("Idle state\n"); }
void run() { printf("Running\n"); }
void pause() { printf("Paused\n"); }
void stop() { printf("Stopped\n"); }
void (*state_handlers[])(void) = {idle, run, pause, stop};
State current_state = STATE_RUN;
state_handlers[current_state]();
current_state = STATE_PAUSE;
state_handlers[current_state]();
return 0;
}
Four combinations of const with pointer parameters:
void func(const int *ptr)- Pointer to const int: Cannot modify pointed-to datavoid func(int *const ptr)- Const pointer to int: Cannot modify pointer itself, but can modify datavoid func(const int *const ptr)- Const pointer to const int: Cannot modify eithervoid func(int *ptr)- No const: Can modify both
#include <stdio.h>
// 1. Pointer to const data (most common for input)
void readOnly(const int *ptr) {
// *ptr = 100; // ERROR: Can't modify data
int value = *ptr; // OK: Can read
printf("Read-only: %d\n", value);
// ptr = NULL; // OK: Can modify pointer
}
// 2. Const pointer to data
void fixedPointer(int *const ptr) {
*ptr = 200; // OK: Can modify data
// ptr = NULL; // ERROR: Can't modify pointer
}
// 3. Const pointer to const data
void completelyFixed(const int *const ptr) {
// *ptr = 300; // ERROR: Can't modify data
// ptr = NULL; // ERROR: Can't modify pointer
printf("Completely fixed: %d\n", *ptr);
}
// 4. Regular pointer
void normalPointer(int *ptr) {
*ptr = 400; // OK
ptr = NULL; // OK (local copy only)
}
int main() {
int x = 10;
readOnly(&x);
printf("After readOnly: x = %d\n", x);
fixedPointer(&x);
printf("After fixedPointer: x = %d\n", x);
completelyFixed(&x);
normalPointer(&x);
printf("After normalPointer: x = %d\n", x);
return 0;
}