0% found this document useful (0 votes)
43 views39 pages

Understanding Pointers in C Programming

The document explains pointers in C programming, detailing their declaration, initialization, dereferencing, and various types such as NULL pointers and function pointers. It also discusses pointer arithmetic, advantages and disadvantages of using pointers, and how they relate to arrays. Additionally, it covers dynamic memory allocation using functions like malloc and calloc.

Uploaded by

rasila.walhekar
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
43 views39 pages

Understanding Pointers in C Programming

The document explains pointers in C programming, detailing their declaration, initialization, dereferencing, and various types such as NULL pointers and function pointers. It also discusses pointer arithmetic, advantages and disadvantages of using pointers, and how they relate to arrays. Additionally, it covers dynamic memory allocation using functions like malloc and calloc.

Uploaded by

rasila.walhekar
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

Pointers

A pointer is a variable that stores the memory address of another variable. Instead of holding a
direct value, it holds the address where the value is stored in memory. It is the backbone of low-
level memory manipulation in C.
● A pointer is declared by specifying its data type and name, with an asterisk (*) before
the name.

Syntax: data_type *pointer_name;

● The data type indicates the type of variable the pointer can point to. For example, "int
*ptr;" declares a pointer to an integer.
● Accessing the pointer directly will just give us the address that is stored in the pointer.
To get the value at the address stored in a pointer variable, we use * operator which is
call dereferencing operator in C
● Note that we use * for two different purposes in pointers. One is to declare a pointer
variable and the other is in an operator to get the value stored at address stored in
pointer.

#include <stdio.h>

int main()
{

// Normal Variable
int var = 10;

// Pointer Variable ptr that stores address of var


int *ptr = &var;

// Directly accessing ptr will give us an address


printf("%d", ptr);

return 0;
}
Output
1751215308
This hexadecimal integer (starting with 0x) is the memory address.

Let us understand different steps of the above program.


Initialize the Pointer
● A pointer is initialized by assigning it the address of a variable using the address
operator (&).
● Syntax: pointer_name = &variable;
● Initializing a pointer ensures it points to a valid memory location before use.
● You can also initialize a pointer to NULL if it doesn’t point to any variable yet: int
*ptr = NULL;

Dereference a Pointer
We have to first dereference the pointer to access the value present at the memory address. This
is done with the help of dereferencing operator(*) (same operator used in declaration).
#include <stdio.h>

int main() {
int var = 10;

// Store address of var variable


int* ptr = &var;
// Dereferencing ptr to access the value
printf("%d", *ptr);

return 0;
}

Output
10
Size of Pointers
The size of a pointer in C depends on the architecture (bit system) of the machine, not the data
type it points to.
● On a 32-bit system, all pointers typically occupy 4 bytes.
● On a 64-bit system, all pointers typically occupy 8 bytes.

The size remains constant regardless of the data type (int*, char*, float*, etc.). We can verify this
using the sizeof operator.
#include <stdio.h>

int main() {
int *ptr1;
char *ptr2;

// Finding size using sizeof()


printf("%zu\n", sizeof(ptr1));
printf("%zu", sizeof(ptr2));

return 0;
}

Output
8
8
The reason for the same size is that the pointers store the memory addresses, no matter what type
they are. As the space required to store the addresses of the different memory locations is the
same, the memory required by one pointer type will be equal to the memory required by other
pointer types.
Special Types of Pointers
There are 4 special types of pointers that used or referred to in different contexts:
NULL Pointer

● The NULL Pointers are those pointers that do not point to any memory location.
● They can bejcreated by assigning NULL value to the pointer. A pointer of any type
can be assigned the NULL value.
● This allows us to check whether the pointer is pointing to any valid memory location
by checking if it is equal to NULL.

#include <stdio.h>

int main() {
// Null pointer
int *ptr = NULL;

return 0;
}

C Pointer Arithmetic
The pointer arithmetic refers to the arithmetic operations that can be performed on a pointer. It is
slightly different from the ones that we generally use for mathematical calculations as only a
limited set of operations can be performed on pointers. These operations include:

● Increment/Decrement

● Addition/Subtraction of Integer

● Subtracting Two Pointers of Same Type

● Comparing/Assigning Two Pointers of Same Type

● Comparing/Assigning with NULL

Constant Pointers
In constant pointers, the memory address stored inside the pointer is constant and cannot be
modified once it is defined. It will always point to the same memory address.
#include <stdio.h>

int main() {
int a = 90;
int b = 50;

// Creating a constant pointer


int* const ptr = &a;

// Trying to reassign it to b
ptr = &b;

return 0;
}

Output
solution.c: In function ‘main’:
solution.[Link] error: assignment of read-only variable ‘ptr’
11 | ptr = &b;
|
Multilevel Pointers
In C, we can create multi-level pointers with any number of levels such as – ***ptr3, ****ptr4,
******ptr5 and so on. Most popular of them is double pointer (pointer to pointer). It stores the
memory address of another pointer. Instead of pointing to a data value, they point to another
pointer.
#include <stdio.h>

int main() {
int var = 10;
// Pointer to int
int *ptr1 = &var;
// Pointer to pointer (double pointer)
int **ptr2 = &ptr1;

// Accessing values using all three


printf("var: %d\n", var);
printf("*ptr1: %d\n", *ptr1);
printf("**ptr2: %d", **ptr2);

return 0;
}
Output
var: 10
*ptr1: 10
**ptr2: 10
Advantages of Pointers
Following are the major advantages of pointers in C:

● Pointers are used for dynamic memory allocation and deallocation.

● An Array or a structure can be accessed efficiently with pointers

● Pointers are useful for accessing memory locations.

● Pointers are used to form complex data structures such as linked lists, graphs, trees,
etc.

● Pointers reduce the length of the program and its execution time as well.

Issues with Pointers


Pointers are vulnerable to errors and have following disadvantages:

● Memory corruption can occur if an incorrect value is provided to pointers.

● Pointers are a little bit complex to understand.

● Pointers are majorly responsible for memory leaks in C.

● Accessing using pointers are comparatively slower than variables in C.

● Uninitialized pointers might cause a segmentation fault.

Pointer to an Array | Array Pointer

You can also use pointers to access arrays.

Consider the following array of integers:

Example
int myNumbers[4] = {25, 50, 75, 100};

You learned from the arrays chapter that you can loop through the array elements with a for
loop:

Example

int myNumbers[4] = {25, 50, 75, 100};

int i;

for (i = 0; i < 4; i++) {

printf("%d\n", myNumbers[i]);

Result:

25

50

75

100

Instead of printing the value of each array element, let's print the memory address of each array
element:

Example
int myNumbers[4] = {25, 50, 75, 100};

int i;

for (i = 0; i < 4; i++) {

printf("%p\n", &myNumbers[i]);

Result:

0x7ffe70f9d8f0

0x7ffe70f9d8f4

0x7ffe70f9d8f8

0x7ffe70f9d8fc

Note that the last number of each of the elements' memory address is different, with an addition
of 4.

It is because the size of an int type is typically 4 bytes, remember:

Example

// Create an int variable

int myInt;

// Get the memory size of an int


printf("%zu", sizeof(myInt));

Result:

So from the "memory address example" above, you can see that the compiler reserves 4 bytes of
memory for each array element, which means that the entire array takes up 16 bytes (4 * 4) of
memory storage:

Example

int myNumbers[4] = {25, 50, 75, 100};

printf("%zu", sizeof(myNumbers));

Result:

16

How Are Pointers Related to Arrays

The memory address of the first element is the same as the name of the array:

Example

int myNumbers[4] = {25, 50, 75, 100};

// Get the memory address of the myNumbers array


printf("%p\n", myNumbers);

// Get the memory address of the first array element

printf("%p\n", &myNumbers[0]);

Result:

0x7ffe70f9d8f0

0x7ffe70f9d8f0

This basically means that we can work with arrays through pointers!

How? Since myNumbers is a pointer to the first element in myNumbers, you can use the *
operator to access it:

Example

int myNumbers[4] = {25, 50, 75, 100};

// Get the value of the first element in myNumbers

printf("%d", *myNumbers);

Result:

25
To access the rest of the elements in myNumbers, you can increment the pointer/array (+1, +2,
etc):

Example

int myNumbers[4] = {25, 50, 75, 100};

// Get the value of the second element in myNumbers

printf("%d\n", *(myNumbers + 1));

// Get the value of the third element in myNumbers

printf("%d", *(myNumbers + 2));

// and so on..

Result:

50

75

Or loop through it:

Example

int myNumbers[4] = {25, 50, 75, 100};


int *ptr = myNumbers;

int i;

for (i = 0; i < 4; i++) {

printf("%d\n", *(ptr + i));

Result:

25

50

75

100

It is also possible to change the value of array elements with pointers:

Example

int myNumbers[4] = {25, 50, 75, 100};

// Change the value of the first element to 13

*myNumbers = 13;

// Change the value of the second element to 17


*(myNumbers +1) = 17;

// Get the value of the first element

printf("%d\n", *myNumbers);

// Get the value of the second element

printf("%d\n", *(myNumbers + 1));

Result:

13

17

Function Pointer

A function pointer is like a normal pointer, but instead of pointing to a variable, it points to a
function.

This means it stores the address of a function, allowing you to call that function using the
pointer.

Function pointers let you decide which function to run while the program is running, or when
you want to pass a function as an argument to another function.

Think of it like saving a phone number - the pointer knows where the function lives in memory,
so you can "call" it later.

Declaring a Function Pointer

The general syntax for declaring a function pointer is:

returnType (*pointerName)(parameterType1, parameterType2, ...);


For example:

int (*ptr)(int, int);

This means ptr is a pointer to a function that takes two int values and returns an int.

Assigning a Function to a Pointer

You can assign a function to a pointer in two ways:

ptr = add;

ptr = &add;

Both are the same, because the function's name already represents its address in memory.

Calling a Function Through a Pointer

Once the pointer is assigned, you can call the function in two ways:

ptr(5, 3);

(*ptr)(5, 3);

Both are valid and do the same thing.

Function Pointer Example

Now that you know how to declare and assign a function pointer, let's see a complete example:

Example

A pointer to a function that adds two numbers:

int add(int a, int b) {

return a + b;
}

int main() {

int (*ptr)(int, int) = add;

int result = ptr(5, 3);

printf("Result: %d", result);

return 0;

Here:

● ptr is a pointer to the function add().


● ptr(5, 3) calls the function using the pointer.

This works exactly the same as calling add(5, 3).

Passing a Function as an Argument

Function pointers can be passed to other functions - this is called a callback.

It allows a function to call another function that you provide as input.

Example

Passing a function pointer to another function:


void greetMorning() { printf("Good morning!\n"); }

void greetEvening() { printf("Good evening!\n"); }

void greet(void (*func)()) {

func();

int main() {

greet(greetMorning);

greet(greetEvening);

return 0;

Here, greet() takes another function as a parameter and calls it. This is commonly used in event-
driven programs or libraries that need user-provided callback functions.

Function Pointer Array

You can also store several function pointers in an array, so you can choose which function to run
while the program is running.
This example runs three different functions using an array of function pointers:

Example

void add() { printf("Add\n"); }

void subtract() { printf("Subtract\n"); }

void multiply() { printf("Multiply\n"); }

int main() {

void (*operations[3])() = { add, subtract, multiply };

for (int i = 0; i < 3; i++) {

operations[i]();

return 0;

This is often used for simple menus, command lists, or calculators - anywhere you want to call
different functions based on user input.

Example
A simple calculator using an array of function pointers:

void add(int a, int b) { printf("Result: %d\n", a + b); }

void subtract(int a, int b) { printf("Result: %d\n", a - b); }

void multiply(int a, int b) { printf("Result: %d\n", a * b); }

int main() {

int choice, x = 10, y = 5;

void (*operations[3])(int, int) = { add, subtract, multiply };

printf("x = %d, y = %d\n\n", x, y);

printf("Choose an operation:\n");

printf("0: Add\n1: Subtract\n2: Multiply\n");

scanf("%d", &choice);
if (choice >= 0 && choice < 3) {

operations[choice](x, y);

} else {

printf("Invalid choice!\n");

return 0;

In this program:

● All three math functions (add(), subtract(), and multiply()) are stored in an array.
● The user picks which one to run by entering a number.
● The correct function is then called using the pointer array.

This is a simple example of how function pointer arrays make programs more flexible and
reusable.

Function Pointer vs Normal Function

Normal Function Function Pointer

Called directly by its name Called using a pointer


The function is decided before the You can choose which function to call while the
program runs program is running

Good for simple code Good for flexible and reusable code

C 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.

C malloc()

The name "malloc" stands for memory allocation.

The malloc() function reserves a block of memory of the specified number of bytes. And, it
returns a pointer of void which can be casted into pointers of any form.
Syntax of malloc()

ptr = (castType*) malloc(size);

Example

ptr = (float*) malloc(100 * sizeof(float));

The above statement allocates 400 bytes of memory. It's because the size of float is 4 bytes. And,
the pointer ptr holds the address of the first byte in the allocated memory.

The expression results in a NULL pointer if the memory cannot be allocated.

C calloc()

The name "calloc" stands for contiguous allocation.

The malloc() function allocates memory and leaves the memory uninitialized, whereas the
calloc() function allocates memory and initializes all bits to zero.

Syntax of calloc()

ptr = (castType*)calloc(n, size);

Example:

ptr = (float*) calloc(25, sizeof(float));

The above statement allocates contiguous space in memory for 25 elements of type float.

C free()
Dynamically allocated memory created with either calloc() or malloc() doesn't get freed on their
own. You must explicitly use free() to release the space.

Syntax of free()

free(ptr);

This statement frees the space allocated in the memory pointed by ptr.

Example 1: malloc() and free()

// Program to calculate the sum of n numbers entered by the user

#include <stdio.h>

#include <stdlib.h>

int main() {

int n, i, *ptr, sum = 0;

printf("Enter number of elements: ");

scanf("%d", &n);

ptr = (int*) malloc(n * sizeof(int));

// if memory cannot be allocated


if(ptr == NULL) {

printf("Error! memory not allocated.");

exit(0);

printf("Enter elements: ");

for(i = 0; i < n; ++i) {

scanf("%d", ptr + i);

sum += *(ptr + i);

printf("Sum = %d", sum);

// deallocating the memory

free(ptr);

return 0;

}h

Output
Enter number of elements: 3

Enter elements: 100

20

36

Sum = 156

Here, we have dynamically allocated the memory for n number of int.

Example 2: calloc() and free()

// Program to calculate the sum of n numbers entered by the user

#include <stdio.h>

#include <stdlib.h>

int main() {

int n, i, *ptr, sum = 0;

printf("Enter number of elements: ");


scanf("%d", &n);

ptr = (int*) calloc(n, sizeof(int));

if(ptr == NULL) {

printf("Error! memory not allocated.");

exit(0);

printf("Enter elements: ");

for(i = 0; i < n; ++i) {

scanf("%d", ptr + i);

sum += *(ptr + i);

printf("Sum = %d", sum);

free(ptr);
return 0;

Output

Enter number of elements: 3

Enter elements: 100

20

36

Sum = 156

Write a program to display the matrix using dynamic memory allocation

#include <stdio.h>

#include <stdlib.h>

int main() {

int rows, cols;

int **matrix;

int i, j;
// Accept number of rows and columns

printf("Enter number of rows: ");

scanf("%d", &rows);

printf("Enter number of columns: ");

scanf("%d", &cols);

// Allocate memory for rows

matrix = (int **)malloc(rows * sizeof(int *));

// Allocate memory for columns

for (i = 0; i < rows; i++) {

matrix[i] = (int *)malloc(cols * sizeof(int));

} // Accept matrix elements

printf("Enter matrix elements:\n");

for (i = 0; i < rows; i++) {

for (j = 0; j < cols; j++) {

scanf("%d", &matrix[i][j]);

// Display matrix

printf("Matrix elements are:\n");

for (i = 0; i < rows; i++) {


for (j = 0; j < cols; j++) {

printf("%d\t", matrix[i][j]);

printf("\n");

// Free allocated memory

for (i = 0; i < rows; i++) {

free(matrix[i]);

free(matrix);

return 0;

C realloc()

If the dynamically allocated memory is insufficient or more than required, you can change the
size of previously allocated memory using the realloc() function.

Syntax of realloc()

ptr = realloc(ptr, x);

Here, ptr is reallocated with a new size x.


Example 3: realloc()

#include <stdio.h>

#include <stdlib.h>

int main() {

int *ptr, i , n1, n2;

printf("Enter size: ");

scanf("%d", &n1);

ptr = (int*) malloc(n1 * sizeof(int));

printf("Addresses of previously allocated memory:\n");

for(i = 0; i < n1; ++i)

printf("%pc\n",ptr + i);

printf("\nEnter the new size: ");

scanf("%d", &n2);

// rellocating the memory


ptr = realloc(ptr, n2 * sizeof(int));

printf("Addresses of newly allocated memory:\n");

for(i = 0; i < n2; ++i)

printf("%pc\n", ptr + i);

free(ptr);

return 0;

Output

Enter size: 2

Addresses of previously allocated memory:

26855472

26855476

Enter the new size: 4


Addresses of newly allocated memory:

26855472

26855476

26855480

26855484

In the C programming language, pointers are variables that store memory addresses. There are
several distinct types of pointers, each with specific characteristics and uses:

● Null Pointer: A null pointer does not point to any valid memory location. It is typically
assigned the NULL value (which is often 0 or (void*)0) to indicate that it is not currently
referencing any object or function. This is useful for initializing pointers or signaling the
end of a linked list or other data structure.

int *ptr = NULL;

● Void Pointer (Generic Pointer): A void pointer, declared as void *, can point to any
data type. It is a generic pointer that does not have an associated data type and therefore
cannot be directly dereferenced. To use a void pointer, it must first be type-casted to a
specific data type.

int x = 10;

void *ptr = &x;

// To access x, cast ptr:

printf("%d\n", *(int*)ptr);
● Dangling Pointer: A dangling pointer occurs when a pointer still holds the address of a
memory location that has been deallocated or freed. Accessing memory through a
dangling pointer leads to undefined behavior, as the memory might be reused or become
invalid.

int *ptr = (int *)malloc(sizeof(int));

free(ptr); // ptr now dangles

// Accessing *ptr here is dangerous

/*Ex3. Sample program of dangling pointer*/

#include<stdio.h>

#include<stdlib.h>

int main()

int a;

printf("\nEnter the number = ");

scanf("%d",&a);

printf("\na = %d",a);

int *ptr=(int *)malloc(sizeof(int));

ptr=&a;

free(ptr);
printf("\nptr = %d",ptr);

return 0;

/*Ex4. Sample program of dangling pointer*/

#include <stdio.h>

int *demo()

int y=10;

return &y;

int main()

int *p=demo();

printf("%d", *p);

return 0;

● Wild Pointer: A wild pointer is an uninitialized pointer. It contains an arbitrary,


unknown memory address, and attempting to dereference it can lead to crashes or data
corruption. It is crucial to initialize pointers before use.
C

int *ptr; // Wild pointer

// Using *ptr here is dangerous

Accept n integers in an array. Copy only the non-zero elements to another array (allocated using
dynamic memory allocation). Calculate the sum and average of non- zero elements.

#include <stdio.h>

#include <stdlib.h>

int main()

int n, i, count = 0;

int *arr, *nonZeroArr;

int sum = 0;

float avg;

printf("Enter number of elements: ");

scanf("%d", &n);

// Dynamic memory allocation for original array

arr = (int *)malloc(n * sizeof(int));


printf("Enter %d elements:\n", n);

for (i = 0; i < n; i++)

scanf("%d", &arr[i]);

if (arr[i] != 0)

count++;

// Dynamic memory allocation for non-zero array

nonZeroArr = (int *)malloc(count * sizeof(int));

// Copy non-zero elements

int j = 0;

for (i = 0; i < n; i++)

if (arr[i] != 0)

nonZeroArr[j] = arr[i];
sum += nonZeroArr[j];

j++;

// Calculate average

if (count > 0)

avg = (float)sum / count;

else

avg = 0;

// Display results

printf("\nNon-zero elements are:\n");

for (i = 0; i < count; i++)

printf("%d ", nonZeroArr[i]);

printf("\nSum of non-zero elements = %d", sum);

printf("\nAverage of non-zero elements = %.2f", avg);


// Free allocated memory

free(arr);

free(nonZeroArr);

return 0;

● Function Pointer: A function pointer stores the memory address of a function. This
allows functions to be passed as arguments to other functions, stored in data structures, or
called dynamically.

int add(int a, int b) { return a + b; }

int (*func_ptr)(int, int) = &add;

int result = func_ptr(5, 3); // Calls the add function

● Structure Pointer: A structure pointer points to a structure variable. It is used to access


the members of a structure using the arrow operator (->).

struct Person {

char name[50];
int age;

};

struct Person p1;

struct Person *ptr = &p1;

strcpy(ptr->name, "Alice");

ptr->age = 30;

● Array Pointer: While not a distinct type in the same sense as the others, an array name
without an index often decays into a pointer to its first element. Pointers can also be
explicitly declared to point to entire arrays.

int arr[5] = {1, 2, 3, 4, 5};

int *ptr = arr; // ptr points to arr[0]

int (*array_ptr)[5] = &arr; // array_ptr points to the entire array

● Near, Far, and Huge Pointers (Historical): These types of pointers were relevant in
older 16-bit systems (like DOS) due to segmented memory architectures. They
distinguished between pointers within the same memory segment (near) and those that
could access memory across different segments (far and huge). These are largely obsolete
in modern 32-bit and 64-bit systems.

You might also like