In C programming, pointers and dynamic memory allocation are core concepts that provide direct memory access and control, making C both powerful and flexible. Pointers allow you to reference memory locations, while dynamic memory functions let you allocate and manage memory during runtime.
1. What are pointers and when should we use pointers?
Pointers are used to store the address of the variable or a memory location. Pointer can also be used to refer to another pointer or a function. The main uses of pointers are,
- To get address of a variable
- For achieving pass by reference in C: Pointers allow different functions to share and modify their local variables.
- Dynamic memory allocation
- To implement “linked" data structures like linked lists and binary trees.
- To pass large structures so that complete copy of the structure can be avoided.
For more information, refer to the article - Pointer Uses in C.
#include <stdio.h>
void square(int *n) {
*n = (*n) * (*n);
}
int main() {
int a = 5;
int *p = &a;
square(p); // pass by reference using pointer
printf("Square: %d\n", a);
return 0;
}
2. What are static and dynamic memory allocations?
- Static memory allocation: Done at compile time. It saves running time and is faster than dynamic memory allocation as memory allocation is done from the stack. It avoids fragmentation, but less flexible. Dynamic allocation provides flexibility at runtime but adds overhead.
- Dynamic memory allocation: Done at execution or run time. It is slower than static memory allocation as memory allocation is done from the heap. It gives flexibility to programmers to allocate and deallocate memory as per the need. It is useful for implementing data structures liked Linked List, Dynamic Sized Arrays and Trees.
For more information, refer to the article - Static and Dynamic Memory Allocation in C
#include <stdio.h>
#include <stdlib.h>
int main() {
// Static allocation
int arr[5] = {1, 2, 3, 4, 5};
// Dynamic allocation
int *dynArr = (int*)malloc(5 * sizeof(int));
for (int i = 0; i < 5; i++) {
dynArr[i] = i * 10;
}
printf("Static: ");
for (int i = 0; i < 5; i++) printf("%d ", arr[i]);
printf("\nDynamic: ");
for (int i = 0; i < 5; i++) printf("%d ", dynArr[i]);
free(dynArr);
return 0;
}
3. What is the difference between malloc() and calloc() in the C programming language?
calloc() and malloc() library functions are used to allocate dynamic memory. Dynamic memory is the memory that is allocated during the runtime of the program from the heap segment. "stdlib.h" is the header file that is used to facilitate dynamic memory allocation.
| Parameter | malloc() | calloc() |
|---|---|---|
| Definition | Allocates uninitialized memory block | Allocates zero-initialized memory block |
| Arguments | One (size) | Two (num, size) |
| Initialization | Garbage values | All bytes set to 0 |
| Speed | Slightly faster | Slightly slower |
| Header File | <stdlib.h> | <stdlib.h> |
For more information, refer to the article - Dynamic Memory Allocation in C using malloc(), calloc(), free(), and realloc()
#include <stdio.h>
#include <stdlib.h>
int main() {
int i;
int *a = (int*)malloc(5 * sizeof(int)); // malloc doesn't initialize
int *b = (int*)calloc(5, sizeof(int)); // calloc initializes to zero
printf("malloc initialized:\n");
for (i = 0; i < 5; i++) {
printf("%d ", a[i]); // Garbage values
}
printf("\ncalloc initialized:\n");
for (i = 0; i < 5; i++) {
printf("%d ", b[i]); // Zeros
}
free(a);
free(b);
return 0;
}
4. What do you mean by dangling pointers and how are dangling pointers different from memory leaks in C programming?
Pointers pointing to deallocated memory blocks in C Programming are known as dangling pointers i.e, whenever a pointer is pointing to a memory location and In case the variable is freed or goes out of scope. Accessing it results in undefined behavior.
int *p = (int*)malloc(sizeof(int));
free(p);
*p = 100; // undefined behavior
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p = (int*)malloc(sizeof(int));
*p = 50;
printf("Before free: %d\n", *p);
free(p); // memory freed
// Dangling pointer usage (unsafe)
printf("After free (undefined behavior): %d\n", *p); // May crash or garbage
// Memory leak example
int *q = (int*)malloc(100 * sizeof(int));
// forgot to free(q); // This causes memory leak
return 0;
}
5. What is a memory leak and how to avoid it?
Memory leak occurs when we allocate memory with the help of the malloc() or calloc() library function, but we forget to free the allocated memory with the help of the free() library function. Memory leaks are particularly serious issues for programs like daemons and servers which by definition never terminate.
- The Performance of the program is reduced since the amount of available memory was reduced and it might cause a crash if memory is freed in a loop or multiple calls to a function.
- To avoid memory leaks, memory allocated on the heap should always be cleared when it is no longer needed.
- Please note that regular local variables (stack) do not cause memory leak as these are freed when they go out of scope.
For more information, refer to the article - Memory Leak
#include <stdio.h>
#include <stdlib.h>
int main() {
int *data = (int*)malloc(5 * sizeof(int));
for (int i = 0; i < 5; i++)
data[i] = i + 1;
for (int i = 0; i < 5; i++)
printf("%d ", data[i]);
free(data); // avoid memory leak
return 0;
}
6. What is near, far, and huge pointers in C?
- Near Pointers: Near pointers are used to store 16-bit addresses only. Using the near pointer, we can not store the address with a size greater than 16 bits.
- Far Pointers: A far pointer is a pointer of 32 bits size. However, information outside the computer's memory from the current segment can also be accessed.
- Huge Pointers: Huge pointer is typically considered a pointer of 32 bits size. But bits located outside or stored outside the segments can also be accessed.
Note: Near, far, and huge pointers were used in 16-bit MS-DOS compilers to handle segmented memory. They are not part of modern C or standard C compilers like GCC/Clang.
7. What is the use of the dereference (*) operator?
The * operator is used to access the value at the address stored in a pointer. This is called dereferencing.
Note: Dereferencing NULL is undefined behavior. Most systems crash with a segmentation fault, but behavior is not portable.
#include <stdio.h>
int main() {
int x = 10;
int *ptr = &x;
printf("Value of x using pointer: %d\n", *ptr);
return 0;
}
8. What are NULL pointers in C?
NULL is used to indicate that the pointer doesn't point to a valid location. Ideally, we should initialize pointers as NULL if we don't know their value at the time of declaration. Also, we should make a pointer NULL when memory pointed by it is deallocated in the middle of a program.
#include <stdio.h>
int main() {
int *p = NULL;
if (p == NULL)
printf("Pointer is NULL\n");
return 0;
}
9. What is the difference between & and * in C?
&is the address-of operator (gets the memory address of a variable).*is the dereference operator (accesses the value at a memory address).
#include <stdio.h>
int main() {
int x = 20;
int *ptr = &x;
printf("Address of x: %p\n", &x); // &
printf("Address stored in ptr: %p\n", ptr);
printf("Value pointed by ptr: %d\n", *ptr); // *
return 0;
}
10. What are the four dynamic memory functions in C?
| Function | Description |
|---|---|
malloc() | Allocates a block of memory (uninitialized). |
calloc() | Allocates memory and initializes it to zero. |
realloc() | Changes size of previously allocated memory. |
free() | Frees dynamically allocated memory. |
#include <stdio.h>
#include <stdlib.h>
int main() {
int *arr = (int*)malloc(3 * sizeof(int));
arr[0] = 10; arr[1] = 20; arr[2] = 30;
arr = (int*)realloc(arr, 5 * sizeof(int));
arr[3] = 40; arr[4] = 50;
printf("Array after realloc: ");
for (int i = 0; i < 5; i++)
printf("%d ", arr[i]);
free(arr);
return 0;
}
11. What happens if you don't free() dynamically allocated memory?
If free() is not called, the memory remains allocated until the program ends, leading to memory leaks, which are dangerous in long-running programs or systems.
#include <stdio.h>
#include <stdlib.h>
int main() {
int *temp = (int*)malloc(100 * sizeof(int));
// Not freeing: memory leak
return 0;
}
12. What is a function pointer in C? How is it used?
A function pointer is a pointer that points to the address of a function. It allows functions to be passed as arguments, enabling callback mechanisms.
#include <stdio.h>
void greet() { printf("Hello!\n"); }
int main() {
void (*func_ptr)() = greet;
func_ptr(); // Calls greet()
return 0;
}
Pointers and dynamic memory allocation give C helps in handling complex data structures like linked lists, trees, graphs and managing low-level memory.
13. What is the difference between const int *ptr, int * const ptr, and const int * const ptr?
1. const int *ptr;
Pointer to a const int (i.e., the value is constant, pointer is not)
Explanation:
- You cannot change the value pointed to by ptr.
- You can change ptr to point to another variable.
#include <stdio.h>
int main() {
int x = 10, y = 20;
const int *ptr = &x;
// *ptr = 15; or: read-only location
ptr = &y; // K: changing pointer target
printf("Value: %d\n", *ptr); // Outputs 20
return 0;
}
2. int * const ptr;
ptr is a constant pointer to an int (i.e., pointer is constant, value is not)
Explanation:
- You cannot change the pointer itself (i.e., ptr = &y; is illegal).
- You can change the value it points to (*ptr = 30; is fine).
#include <stdio.h>
int main() {
int x = 10;
int * const ptr = &x;
*ptr = 50;
printf("Value: %d\n", *ptr); // Outputs 50
return 0;
}
3. const int * const ptr;
Constant pointer to a constant int (i.e., neither pointer nor value can be changed)
Explanation:
- Cannot change the pointer -> ptr = &y;
- Cannot change the value -> *ptr = 10;
#include <stdio.h>
int main() {
int x = 100;
const int * const ptr = &x;
printf("Value: %d\n", *ptr); // Outputs 100
return 0;
}
14 What are stack and heap areas?
- Heap Area: It is used for the objects allocated dynamically (Using malloc() and calloc()).
- Stack Area: It is used to store local variables and arguments of a method. This stays in memory only till the termination of that particular method.
Please refer stack vs heap memory for details. It will not attach all the class. Rather it will give permission to access the class of java.lang
15. What will be the result when we try to dereference NULL?
#include <stdio.h>
int main() {
int *ptr = NULL;
*ptr = 5; // Dangerous: Dereferencing NULL
printf("%d", *ptr);
return 0;
}
- ptr is initialized to NULL, meaning it points to no valid memory.
- *ptr = 5; tries to store value in a non-existent memory location, causing a segmentation fault (runtime crash).
Dereferencing NULL is always undefined behavior. Some systems may crash, others may hang, or show access violation.
16. Write a code to show how one can use double pointers?
Double pointers are used to change a pointer using its address. It is particularly used when we wish pass pointers by reference.
#include <stdio.h>
int main() {
int x = 10;
int *p = &x;
int **q = &p;
**q = 20; // Same as *p = 20 or x = 20
printf("%d\n", x); // 20
return 0;
}
- p is a pointer to x
- q is a pointer to p (i.e., double pointer)
- **q = 20; modifies x directly via double dereferencing
- hence output will be 20
17. Will the below provided code successfully run?
#include <stdio.h>
#include <stdlib.h>
int main() {
int *a = malloc(sizeof(int));
*a = 100;
free(a); // memory deallocated
printf("%d\n", *a); //Accessing freed memory
return 0;
}
- After free(a), a becomes a dangling pointer.
- Dereferencing it (*a) causes undefined behavior:
- May print garbage
- May crash
- May silently succeed (but unsafe!)
18. What will be the output of the below code?
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr = malloc(0);
if (ptr)
printf("Allocated\n");
else
printf("Not allocated\n");
free(ptr); // Still safe to call free on NULL or non-NULL
return 0;
}
- malloc(0) behavior is implementation-defined:
May return NULL
May return a unique non-NULL pointer (but you shouldn't dereference it)
- Either result is valid.
19. Write the code to show how one can use realloc with malloc?
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p = malloc(2 * sizeof(int)); // Allocate space for 2 integers
p[0] = 1;
p[1] = 2;
// Resize memory to fit 4 integers
p = realloc(p, 4 * sizeof(int));
p[2] = 3;
p[3] = 4;
for (int i = 0; i < 4; i++)
printf("%d ", p[i]);
free(p);
return 0;
}
- malloc(2*sizeof(int)) allocates space for 2 integers.
- realloc(p, 4*sizeof(int)) expands the block.
- The values at indices 0 and 1 are preserved.
- Newly allocated memory (for index 2 and 3) is uninitialized, so we assign values.
- Thus, the output will be 1 2 3 4
If realloc() fails, it returns NULL. Always assign to a temporary pointer before overwriting the original pointer to avoid memory loss.
20. What is the difference between array and pointer?
See Array vs Pointer
21. Difference between ++*p, *p++ and *++p?
- Precedence of prefix ++ and * is same. Associativity of both is right to left.
- Precedence of postfix ++ is higher than both * and prefix ++. Associativity of postfix ++ is left to right.
(Refer: Precedence Table)
- The expression ++*p has two operators of same precedence, so compiler looks for associativity. Associativity of operators is right to left. Therefore the expression is treated as ++(*p). Therefore the output of first program is "arr[0] = 10, arr[1] = 20, *p = 11".
- The expression *p++ is treated as *(p++) as the precedence of postfix ++ is higher than *. Therefore the output of second program is "arr[0] = 10, arr[1] = 20, *p = 20".
- The expression *++p has two operators of same precedence, so compiler looks for associativity. Associativity of operators is right to left. Therefore the expression is treated as *(++p). Therefore the output of third program is "arr[0] = 10, arr[1] = 20, *p = 20"
Please refer Difference between ++*p, *p++ and *++p for details.
22. Explain Deep Copy and Shallow Copy with examples?
In the following C program, struct variable st1 contains pointer to dynamically allocated memory. When we assign st1 to st2, str pointer of st2 also start pointing to same memory location. This kind of copying is called Shallow Copy.
// C program to demonstrate shallow copy
# include <stdio.h>
# include <string.h>
# include <stdlib.h>
struct test
{
char *str;
};
int main()
{
struct test st1, st2;
st1.str = (char *)malloc(sizeof(char) * 20);
strcpy(st1.str, "GeeksforGeeks");
st2 = st1;
st1.str[0] = 'X';
st1.str[1] = 'Y';
/* Since copy was shallow, both strings are same */
printf("st1's str = %s\n", st1.str);
printf("st2's str = %s\n", st2.str);
return 0;
}
Output
st1's str = XYeksforGeeks st2's str = XYeksforGeeks
To do Deep Copy, we allocate new memory for dynamically allocated members and explicitly copy them.
// C program to demonstrate deep copy
# include <stdio.h>
# include <string.h>
# include <stdlib.h>
struct test
{
char *str;
};
int main()
{
struct test st1, st2;
st1.str = (char *)malloc(sizeof(char) * 20);
strcpy(st1.str, "GeeksforGeeks");
st2 = st1;
// We add extra statements to do deep copy
st2.str = (char *)malloc(sizeof(char) * 20);
strcpy(st2.str, st1.str);
st1.str[0] = 'X';
st1.str[1] = 'Y';
/* Since copy was deep, both strings are different */
printf("st1's str = %s\n", st1.str);
printf("st2's str = %s\n", st2.str);
return 0;
}
Output
st1's str = XYeksforGeeks st2's str = GeeksforGeeks