Unit 3 - Pointers
Introduction
Pointers are variables that store the memory address of other variables.
- They are one of the most powerful features of the C programming language. With pointers, you can directly manipulate data in memory, access hardware, and create complex data structures.
- However, with great power comes great responsibility. Pointers can be tricky to use, and if not used correctly, can cause crashes, memory leaks, and other difficult-to-debug errors.
Understanding pointers
- Before delving into pointers, it's important to understand how memory works in C.
- Every variable in C is stored in a specific memory location. When you declare a variable, the compiler assigns a memory address to it. Pointers allow you to access and manipulate the data stored at a given memory address directly.
- They are declared using the
*symbol, and can be dereferenced using the*operator. - Understanding how to use pointers is essential for advanced C programming, but requires careful attention to avoid common pitfalls and errors.
Advantage of using pointer
Advantages of using pointers in C:
- Pointers allow direct manipulation of data in memory.
- Pointers can be used to access hardware and memory-mapped I/O.
- Pointers can be used to create complex data structures, such as linked lists and trees.
- Pointers can reduce memory usage and improve program performance by passing pointers instead of large data structures to functions.
- Pointers can be used to create dynamic data structures that can be resized at runtime.
Accessing the address of a variable
To access the address of a variable in C, you can use the & operator. The following steps show how to access the address of a variable:
- Declare a variable of any data type
- Use the
&operator followed by the variable name to get the address of the variable - Assign the address to a pointer variable using the
*symbol.
For example, to get the address of an integer variable named num:
int num = 42;
int *ptr = #
The variable ptr is now a pointer to the memory location where the value 42 is stored.
Declaring and initializing pointers
To declare a pointer in C, use the * symbol, followed by the name of the pointer variable. The following syntax can be used to declare a pointer:
data_type *pointer_name;
For example, to declare a pointer to an integer variable:
int *ptr;
To initialize a pointer, you can assign it the memory address of a variable using the & operator. For example, to initialize a pointer named ptr to point to an integer variable named num:
int num = 42;
int *ptr = #
The variable ptr is now a pointer to the memory location where the value 42 is stored.
Here are two basic pointer examples for students:
Example 1: Pointer to an integer
#include <stdio.h>
void main() {
int num = 42;
int *ptr = #
printf("The value of num is %d\n", num);
printf("The value of ptr is %p\n", ptr);
printf("The value pointed to by ptr is %d\n", *ptr);
}
Output:
The value of num is 42
The value of ptr is 0x7fff5fbff74c
The value pointed to by ptr is 42
Example 2: Pointer to a character
#include <stdio.h>
int main() {
char ch = 'A';
char *ptr = &ch;
printf("The value of ch is %c\\n", ch);
printf("The value of ptr is %p\\n", ptr);
printf("The value pointed to by ptr is %c\\n", *ptr);
return 0;
}
Output:
The value of ch is A
The value of ptr is 0x7fff5fbff74f
The value pointed to by ptr is A
Dereference
- Dereferencing a pointer means accessing the value stored at the memory address pointed to by the pointer.
- This is done using the
*operator. - For example, if
ptris a pointer to an integer variable, you can dereference it to access the value of the variable like this:int value = *ptr;
Note that if you attempt to dereference a null pointer or a pointer that has not been initialized, your program will likely crash.
Accessing a variable through pointer
To access a variable through a pointer in C, you can use the * operator to dereference the pointer. This allows you to read or write to the variable being pointed to. Here are some examples:
// Example 1: Accessing an integer variable through a pointer
int num = 42;
int *ptr = #
// To read the value of num through the pointer, use the * operator
int value = *ptr;
printf("The value of num is %d\n", value);
// To write a new value to num through the pointer, use the * operator
*ptr = 100;
printf("The new value of num is %d\n", num);
// Example 2: Accessing a character variable through a pointer
char ch = 'A';
char *ptr = &ch;
// To read the value of ch through the pointer, use the * operator
char value = *ptr;
printf("The value of ch is %c\n", value);
// To write a new value to ch through the pointer, use the * operator
*ptr = 'B';
printf("The new value of ch is %c\n", ch);
In these examples, we declare a pointer variable ptr that points to an existing variable, num or ch. We then use the * operator to read or write to the value being pointed to. Note that changes made to the value through the pointer are reflected in the original variable.
Pointer expressions
Pointer expressions are any expressions involving pointers, including arithmetic operations, comparison operations, and assignment operations. Here are some examples:
- Arithmetic operations:
#include<stdio.h>
void main()
{
int x = 10;
int y = 20;
int sum;
int *ptr1 = &x;
int *ptr2 = &y;
sum = *ptr1 + *ptr2;
printf("Sum is %d", sum);
/}
- Comparison operations:
int a = 42;
int b = 40;
int *ptr_a = &a;
int *ptr_b = &b;
printf("ptr_a = %d and ptr_b = %d \n", *ptr_a, *ptr_b);
if (*ptr_a < *ptr_b)
{
printf("ptr_a is less than ptr_b\n");
}
else
{
printf("ptr_a is greater than or equal to ptr_b\n");
}
Output:
ptr_a = 42 and ptr_b = 40
ptr_a is greater than or equal to ptr_b
- Assignment operations:
#include <stdio.h>
void main()
{
int a = 50;
int b = 60;
int *ptr_a = &a;
int *ptr_b = &b;
*ptr_a = *ptr_b;
printf("The value of a is now %d\n", a);
}
The value of a is now 60456 7i90p
These are just a few examples of pointer expressions. Pointers are a powerful tool in C programming, but require careful attention to use correctly.
Pointers and arrays
In C, arrays are implemented as a sequence of contiguous memory locations. Pointers and arrays are closely related, and in fact, the name of an array is a pointer to the first element of the array.
Here are two examples of using pointers and arrays in C:
int main()
{
int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr;
for (int i = 0; i < 5; i++)
{
printf("%d ", *(ptr + i));
}
printf("\n");
return 0;
}
In the first example, we use a pointer variable ptr to iterate over the array. We assign the address of the first element of the array to the pointer, and use pointer arithmetic to access the remaining elements.
In the second example, we define a function print_array that takes a pointer to an array and the size of the array as arguments. We use pointer arithmetic to iterate over the array and print out each element.
Note that in both examples, we use the * operator to dereference the pointer and access the value being pointed to.
Pointer increments and scale factor
- Pointer increments and scale factor can be used to access elements of an array.
- When a pointer is incremented, it points to the next element in the array, and the scale factor is used to determine the number of bytes between elements.
- For example, to print all the elements of an integer array using pointer increments and scale factor:
#include <stdio.h>
int main() {
int arr[] = {1, 2, 3, 4, 5};
int* ptr = arr; // pointer to the first element of the array
printf("The first element of the array is %d\n", *ptr); // should print 1
// increment the pointer to point to the second element
ptr += 1; // same as ptr = ptr + sizeof(int)
printf("The second element of the array is %d\n", *ptr); // should print 2
// increment the pointer again to point to the third element
ptr += 1; // same as ptr = ptr + sizeof(int)
printf("The third element of the array is %d\n", *ptr); // should print 3
// decrement the pointer to point back to the second element
ptr -= 1; // same as ptr = ptr - sizeof(int)
printf("The second element of the array is %d\n", *ptr); // should print 2
}
This will print the values 1 2 3 4 5 to the console.
Pointers and character strings
- Character strings are implemented as arrays of characters, with a null terminator character (
\0) at the end. - Because arrays are implemented as pointers to the first element, character strings and pointers are closely related. To declare a character string, you can use the
chardata type, followed by square brackets to indicate the size of the array.
For example:
char str[6] = "Hello";
- This declares a character string
strwith a length of 6 (including the null terminator character), and initializes it with the value"Hello". - To print the value of the string, you can use the
%sformat specifier in aprintfstatement:
printf("The value of str is: %s\n", str);
- This will print the value
"Hello"to the console. - Note that because arrays are implemented as pointers to the first element, you can also use pointer arithmetic to access individual characters in the string:
char *ptr = str;
printf("The first character of str is: %c\n", *ptr);
This will print the value "H" to the console.
Example:
#include <stdio.h>
int main()
{
/* string literal */
char str[] = "Hello World!";
printf("Value of str : %s \n", str);
char *ptr = str;
printf("Value of ptr : %s", ptr);
}
Value of str : Hello World! Value of ptr : Hello World!
Pointers and Functions
- Pointers can be used as arguments in functions, allowing functions to directly modify data in memory.
- This can be useful for functions that need to modify large data structures or arrays.
- When passing a pointer to a function, the function can use pointer arithmetic to access and modify the data being pointed to.
Call by Value and Call by Reference
When passing arguments to a function, there are two ways to do it:
Call by Value and Call by Reference.
- In Call by Value, a copy of the argument is passed to the function, and any changes made to the copy do not affect the original variable.
Here is Call by Value Example.
#include <stdio.h>
void swap (int a, int b)
{
int temp;
temp = a;
a=b;
b=temp;
printf("After swapping values in function a = %d, b = %d \n",a,b); // Formal parameters, a = 20, b = 10
}
int main()
{
int a = 10;
int b = 20;
printf("Before swapping the values in main a = %d, b = %d\n",a,b); // printing the value of a and b in main
swap(a,b);
printf("After swapping values in main a = %d, b = %d\n",a,b); // The value of actual parameters do not change by changing the formal parameters in call by value, a = 10, b = 20
}
Before swapping the values in main a = 10, b = 20
After swapping values in function a = 20, b = 10
After swapping values in main a = 10, b = 20
Call by Reference:
- In Call by Reference, a pointer to the original variable is passed to the function, allowing the function to modify the original variable directly.
- Pointers are often used in Call by Reference to pass large data structures to functions without making copies of the entire structure.
Here is a simple example of using a pointer in C:
#include <stdio.h>
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
void main() {
int x = 42;
int y = 69;
printf("Before swap: x = %d, y = %d\\n", x, y);
swap(&x, &y);
printf("After swap: x = %d, y = %d\\n", x, y);
}
- In this example, we define a function
swapthat takes two pointer argumentsaandb. The function uses pointer to access the values being pointed to, and swaps them. - In the
mainfunction, we declare two integer variablesxandy, and print their values to the console. We then call theswapfunction, passing in the memory addresses ofxandy. After the function call, we print the updated values ofxandy.
Note that changes made to the values through the pointers are reflected in the original variables. In this way, pointers allow functions to modify data in memory directly, without needing to pass large data structures as arguments.
Pointers and structures
- A structure pointer is a pointer that points to a memory block storing a structure.
- It is used to create complex data structures such as Linked lists, trees, and graphs. The structure pointer points to the structure variable's address in memory.
To declare a pointer to a structure.
Syntax:
struct struct_name *ptr;
For example, to declare a pointer to a structure named person:
struct person *ptr;
- To access members of a structure through a pointer, use the arrow
->operator.
For example, to access the name member of a structure pointed to by ptr:
#include <stdio.h>
struct person
{
char name[50];
int age;
};
void main()
{
struct person p1 = {"John", 42};
struct person *ptr = &p1;
printf("Name: %s \n", p1.name);
printf("Name: %s \n", ptr->name);
}
Name: John
Name: John
- This will print the value of the
namemember to the console.
Using pointers with structures can be a powerful way to manipulate complex data structures in C, but requires careful attention to avoid common errors and pitfalls.
Advantage of Using Pointer in structure
Advantages of using pointers in structures:
- Pointers allow direct manipulation of structure members in memory.
- Pointers can reduce memory usage and improve program performance by passing pointers instead of large structures to functions.
- Pointers can be used to create complex data structures, such as linked lists, trees, and graphs.
- Pointers can be used to access and modify members of a structure directly.
- By using a pointer to a structure, you can avoid making copies or passing large data structures as arguments.
Dynamic Memory Allocation and Linked List
- The concept of dynamic memory allocation in C language enables the programmer to allocate memory at runtime.
- This is made possible by four functions in the
stdlib.hheader file.
- malloc()
- calloc()
- realloc()
- free()
Before learning above functions, let's understand the difference between static memory allocation and dynamic memory allocation.
| Static memory allocation | Dynamic memory allocation |
|---|---|
| memory is allocated at compile time. | memory is allocated at run time. |
| memory can't be increased while executing program. | memory can be increased while executing program. |
| used in array. | used in linked list. |
Now let's have a quick look at the methods used for dynamic memory allocation.
| malloc() | allocates single block of requested memory. |
|---|---|
| calloc() | allocates multiple block of requested memory. |
| realloc() | reallocates the memory occupied by malloc() or calloc() functions. |
| free() | frees the dynamically allocated memory. |
Dynamic Memory Allocation
As you know, an array is a collection of a fixed number of values. Once the size of an array is declared, you cannot change it.
Sometimes the size of the array you declared may be insufficient. To solve this issue, you can allocate memory manually during run-time. This is known as dynamic memory allocation in C programming.
To allocate memory dynamically, library functions are malloc(), calloc(), realloc() and free() are used. These functions are defined in the <stdlib.h> header file.
malloc() function in C
The name "malloc" stands for memory allocation.
- Malloc() is a function in C language that is used to Dynamically allocate memory during runtime.
- To reserve a contiguous memory block, the user inputs the memory size (in bytes) at runtime, and the function returns a pointer of type void, which is cast into a pointer of any form.
- The memory block created by malloc is not initialized at execution time and contains default garbage values.
- To use the malloc function in a program, the stdlib.h header file must be included.
- If memory is allocated, it returns the address of the first block.
- It returns NULL if memory is not sufficient.
The syntax of malloc() function is given below:
**ptr=(cast-type*)malloc(byte-size)**
Example:
**ptr = (int*) malloc(sizeof(int));**
Here's a simple example of using the malloc function to allocate memory for an integer variable:
#include <stdio.h>
#include <stdlib.h>
void main() {
int *ptr;
// allocate memory for one integer variable
ptr = (int*) malloc(sizeof(int));
// check if allocation was successful
if (ptr == NULL) {
printf("Memory allocation failed\n");
return 1;
}
// assign a value to the variable
*ptr = 42;
// print out the value
printf("The value of the variable is %d", *ptr);
// free the allocated memory
free(ptr);
}
This program declares a pointer variable ptr, and uses the malloc function to allocate memory for an integer variable. It then checks if the allocation was successful, assigns a value to the variable, prints out the value, and frees the allocated memory using the free function. Note that failure to free allocated memory can lead to memory leaks and other errors.
Malloc with structure.
#include <stdio.h>
#include <stdlib.h>
struct student
{
char name[50];
int age;
};
void main()
{
struct student *ptr;
ptr = (struct student*) malloc(sizeof(struct student));
if(ptr == NULL)
printf("Out of Memory");
else
{
printf("Enter name: ");
scanf("%s", ptr->name);
printf("Enter age: ");
scanf("%d", &ptr->age);
printf("Name: %s \n", ptr->name);
printf("Age: %d \n", ptr->age);
free(ptr);
}
}
For Exercise
Program 1: Allocate and Initialize a String
This program allocates memory for a string using malloc, initializes the string with a value, and then prints the string.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char *str;
str = (char *)malloc(10 * sizeof(char));
strcpy(str, "Hello");
printf("%s\n", str);
free(str);
return 0;
}
Hello
calloc() function
- A calloc() function is a predefined function that stands for Contiguous memory Allocation.
- A calloc() function is used to create multiple blocks at the run time of a program having the same size in the memory.
- A calloc function is defined inside the stdlib.h header file.
- It has two parameters, no. of blocks and the size of each block.
- When the dynamic memory is allocated using the calloc() function, it returns the base address of the first block, and each block is initialized with 0. And if memory is not created, it returns a NULL pointer.
For example, suppose we want to create three blocks of memory using the calloc() function, we need to pass two parameters, a number of blocks (3) and the size of each block (int, char, float, etc.) in the byte. In this way, it creates three blocks whose size is the same inside the computer memory.
The syntax of calloc() function is given below:
**ptr=(cast-type*)calloc(number, byte-size)**
Let's see the example of calloc() function.
Example : Using calloc() function.
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *ptr;
/* use calloc() function to define the no. of blocks and size of each blocks. */
ptr = calloc (4, sizeof(int)); // here 4 is the no. of block and int is the size of block
if (ptr != NULL)
{
printf (" Memory is created successfully \n");
}
else
printf (" Memory is not created ");
return 0;
}
malloc vs calloc
| malloc | calloc |
|---|---|
| Allocates a single block of memory of a specified size. | Allocates multiple contiguous blocks of memory of a specified size. |
| Does not initialize the memory block. | Initializes the memory block with zero. |
| Takes one argument, the size of the memory block to allocate. | Takes two arguments, the number of blocks to allocate and the size of each block. |
| Returns a pointer to the first byte of the allocated memory block. | Returns a pointer to the first byte of the allocated memory block. |
| If the allocation fails, returns a null pointer. | If the allocation fails, returns a null pointer. |
Use malloc when you need to allocate a single block of memory, and use calloc when you need to allocate multiple contiguous blocks of memory, and when you want the memory to be initialized with zero.
realloc() function in C
- If memory is not sufficient for malloc() or calloc(), you can reallocate the memory by realloc() function. In short, it changes the memory size.
Let's see the syntax of realloc() function.
**ptr=realloc(ptr, newsize)**
Example:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *ptr, i, n1 = 4, n2 = 6;
ptr = (int *)malloc(n1 * sizeof(int));
printf("Values of previously allocated memory:\n");
ptr[0] = 1;
ptr[1] = 2;
ptr[2] = 3;
ptr[3] = 4;
// rellocating the memory
ptr = realloc(ptr, n2 * sizeof(int));
// assigning values to newly allocated memory
ptr[4] = 5;
ptr[5] = 6;
printf("Values of newly allocated memory:\n");
for (i = 0; i < n2; i++)
printf("%d ", ptr[i]);
free(ptr);
return 0;
}
This program demonstrates the use of the realloc function to dynamically reallocate memory for an array of integers. It first allocates memory for five integers, assigns values to the variables, and prints out the values. It then uses the realloc function to reallocate memory for ten integers, assigns values to the new variables, and prints out the values again. Finally, it frees the allocated memory using the free function. Note that failure to free allocated memory can lead to memory leaks and other errors.
For Exercise
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *ptr, i, n1, n2;
printf("Enter size of array: ");
scanf("%d", &n1);
ptr = (int *)malloc(n1 * sizeof(int));
for (i = 0; i < n1; i++){
printf("Enter element %d: ", i+1);
scanf("%d", &ptr[i]);
}
printf("Elements of array are: \n");
for (i = 0; i < n1; i++)
printf("%d ", ptr[i]);
printf("\n\n");
printf("Enter the new size of the array: ");
scanf("%d", &n2);
// rellocating the memory
ptr = realloc(ptr, n2 * sizeof(int));
printf("Array size is now: %d \n\n", n2);
printf("Enter the new elements of the array: \n");
for (i = n1; i < n2; i++)
{
printf("Enter element %d: ", i + 1);
scanf("%d", &ptr[i]);
}
printf("Values of array: \n");
for (i = 0; i < n2; i++)
printf("%d ", ptr[i]);
free(ptr);
return 0;
}
Enter size of array: 3
Enter element 1: 11
Enter element 2: 22
Enter element 3: 33
Elements of array are:
11 22 33
Enter the new size of the array: 5
Array size is now: 5
Enter the new elements of the array:
Enter element 4: 44
Enter element 5: 55
Values of array:
11 22 33 44 55
free() function in C
The memory occupied by malloc() or calloc() functions must be released by calling free() function. Otherwise, it will consume memory until program exit.
Let's see the syntax of free() function.
free(ptr)
#include <stdio.h>
#include <stdlib.h>
struct person
{
char name[50];
int age;
};
void main()
{
struct person p1 = {"John", 42};
struct person *ptr = &p1;
printf("Name: %s \n", p1.name);
printf("Name: %s \n", ptr->name);
**free(ptr);**
}
Linked List
- Linked List can be defined as collection of objects called nodes that are randomly stored in the memory.
- A node contains two fields i.e. data stored at that particular address and the pointer which contains the address of the next node in the memory.
- The last node of the lidst contains pointer to the null.

Uses of Linked List
- Linked list optimizes space utilization by allowing non-contiguously present nodes to be linked together.
- The size of the linked list doesn't need to be declared in advance and is limited only by the available memory space.
- The linked list does not allow empty nodes.
Why Use Linked List Over Array?
Arrays have limitations that can be overcome by using a linked list:
- The size of the array must be known in advance, whereas the linked list allocates memory dynamically.
- Increasing the size of the array is a time-consuming process, while the linked list grows as per the program's demand and is limited only by the available memory space.
- All the elements in the array need to be contiguously stored in the memory, while the linked list allows non-contiguously present nodes to be linked together with the help of pointers.