What is Pointer in C?

In the world of C programming, pointers are a fundamental concept that often puzzles beginners. However, mastering pointers is crucial for understanding how C interacts with your computer’s memory. By grasping the essence of pointers, you unlock a powerful tool for efficient memory management, data manipulation, and building complex data structures. Let’s unravel the mysteries of pointer in C and explore their various applications.

Pointer in C? A Key to Memory Addresses

At their core, pointers in C are variables that store memory addresses. Think of them as labels attached to specific locations in your computer’s RAM. These addresses act as references to the actual data stored in those locations. By manipulating pointers, you can directly access and modify the contents of memory, offering greater control and efficiency in your programs.

Declaring and Using C Pointers

For the declaration of the pointer in c at first we write date type followed by an asterisk ( * ) then we write;

Pointer name: Data Type * Pointer_Name;

For example, suppose we have to declare a pointer to hold the address of integer variable we will declare it as follows:

int *ptr;  

// Declares a pointer named 'ptr' that can store the address of an integer.

The above instruction will declare a pointer P that can hold an address of an integral variable.

Accessing memory of variable using Pointer: For accessing memory using a pointer in C at first we need to assign memory address to the pointer, then we can access, update or insert contents to that memory area. To do this at first we reference it then dereference it.

Referencing Operation: Providing reference we write pointer in C name followed by assignment operator then we write or provide an address;

Pointer_Name = Address of memory(or location);

For example, we have an integer i and its value is 10, we have to access it using a pointer for that we need to declare a pointer and assign the address of i to it. We do it using the following:

int num = 10;
ptr = # 

//'ptr' now holds the memory address of the variable 'num'.

Now to access the contents of memory we need to dereference it.

Dereferencing a Pointer: In C pointer is dereferenced using asterisk or dereference operator ( * ) followed by pointer name;

printf("%d", *ptr); 

// Prints the value stored at the address pointed to by 'ptr' (10 in this case).

To create a pointer to a variable, we use the * and & operators. For example, the following code declares a variable called total_cost and a pointer to it called total_cost_ptr;

float total_cost; 
float *total_cost_ptr;
total_cost_ptr = &total_cost;

The * symbol in the declaration of total_cost_ptr is the way to declare that variable to be a pointer in C. You can pronounce the above statement float *total_cost_ptr as declaring float.

Pointer called total_cost_ptr, and you can pronounce the statement total_cost_ptr = &total_cost; as total_cost_ptr take as its value the address of the variable total_cost.

Pointer notation in c

Consider the declaration,

int i = 3;

This declaration tells the C compiler to:

  • Reserve
    space in memory to hold the integer value.
  • Associate
    the name i with this memory location.
  • Store
    the value 3 at this location.

We may represent i’s location in memory by the following memory map.

We see that the computer has selected memory location 65524 as the place to store the value 3. The location number 65524 is not a number to be relied upon, because some other time the computer may choose a different location for storing the value 3. The important point is, i’s address in memory is a number.

We can print this address number through the following program:

main( )
{
int i = 3 ;
printf ( "\nAddress of i = %u", &i ) ; 
printf ( "\nValue of i = %d", i ) ;
}

The output of the above program would be:

  • Address of i = 65524,
  • Value of i = 3

Look at the first printf( ) statement carefully. ‘&’ used in this statement is C’s ‘address of’ operator. The expression &i returns the address of the variable i, which in this case happens to be 65524. Since 65524 represents an address, there is no question of a sign being associated with it. Hence it is printed out using %u, which is a format specifier for printing an unsigned integer. We have been using the ‘&’ operator all the time in the scanf() statement.

The other pointer operator available in C is ‘*’, called ‘value at address’ operator. It gives the value stored at a particular address. The ‘value at address’ operator is also called ‘indirection’ operator.

Consider the following memory map. As you can see, i value is 3, and j value is i address.

j is a variable that contains the address of i, it is declared as int *j ;

This declaration tells the compiler that j will be used to store the address of an integer value. In other words, j points to an integer. How do we justify the usage of * in the declaration int *j ;

Let us go by the meaning of *. It stands for ‘value at address’. Thus, int *j would mean, the value at the address contained in j is an int.

Here is a program that demonstrates the relationships we have been discussing.

main( )
{
int i = 3 ;
int *j ; j = &i ;
printf ( "\nAddress of i = %u", &i ) ; 
printf ( "\nAddress of i = %u", j ) ; 
printf ( "\nAddress of j = %u", &j ) ; 
printf ( "\nValue of j = %u", j ) ;
printf ( "\nValue of i = %d", i ) ; 
printf ( "\nValue of i = %d", *( &i ) ) ; 
printf ( "\nValue of i = %d", *j ) ;
}

The output of the above program would be:

  • Address of i = 65524
  • Address of i = 65524
  • Address of j = 65522
  • Value of j = 65524
  • Value of i = 3
  • Value of i = 3
  • Value of i = 3

Arithmetic operations on Pointers: Arithmetic operations can be applied to pointers in restricted form because the outcome of the operation is governed by pointer arithmetic.

Pointer uses the following rules of arithmetic:

Addition Operation: If numeric 1 is added to a pointer, then it is incremented by 1 multiplied by the size of a data type. If numeric 2 is added it is increment by 2 multiplied by the size of data type and so on.

For example, suppose we have an integral pointer named P having an address 1002 if numeric 1 is added to it, it will be incremented by 1 multiplied by 2. Because the size of the data type is 2.

int * P ;
P= &i;  //Let the address of i is 1002

(P + 1) it will give result equal to (P + 1 * size of integer) i.e. (P + 1 * 2) that will be (1002 +2) equal to (1004) and (P + 2) it will give result equal to (P + 2 * size of integer) i.e. (P + 2 * 2) that will be  (1002 + 4) equal to (1006).

Increment operation: Generally we use increment operation for arrays. If any pointer is incremented by numeric 1 then it points the address of the next location. You can understand it by assigning the base address of an array to a pointer.

Subtraction Operation: If any pointer is subtracted by numeric 1 then it is decremented by 1 multiplied by the size of a data type. If the pointer is subtracted by numeric 2 then it is decremented by 2 multiplied by the size of a data type.

For example, suppose we have a float pointer named P having an address 1010 if numeric 1 is subtracted from it, it will be decremented by 1 multiplied by 4. Because the size of the data type is 4.

float * P ;
P= &i; //Let the address of i is 1010

(P – 1) it will give result equal to (P – 1 * size of a float) i.e. (P – 1 * 4) that will be (1010 – 4) equal to (1006) and (P – 2) it will give result equal to (P – 2 * size of a float) i.e. (P + 2 * 4) that will be (1010 -8) equal to (1002).

Decrement Operation: Generally we use decrement operation for arrays. If any pointer is decremented by numeric 1 then it points the address of the previous location. You can understand it by assigning the base address of an array to a pointer.

An array of Pointers: An array of pointers is the collection of addresses. For example, suppose we have a declare a pointer that can hold the addresses of 3 variables at a time. To achieve this we need to write the following:

int * P[3]; //This statement will create a pointer that can hold the address of 3 //integral variables.

Let assume we have three variables

int a=10, b=20,c=30;

Then we can assign the address of a,b,c to pointer P by the following statement.

P[0] = &a: 
P[1] = &b;  
P[2] = &c;

For accessing the values of a,b and c through pointer P we write code:

*(P[0]) 
*(P[1]) 
*(P[2])

Why Pointers are Essential in C

  • Dynamic Memory Allocation: Pointers enable dynamic memory allocation using functions like malloc() and calloc(). This gives you flexibility to allocate memory at runtime based on your program’s needs.
  • Efficient Data Manipulation: By passing pointers to functions, you can modify the original data directly, avoiding the overhead of copying large data structures.
  • Building Complex Data Structures: Linked lists, trees, and graphs are commonly built using pointers, allowing for dynamic and flexible data organisation.

Beyond the Basics: Advanced Pointer Concepts

  • Void Pointers: Generic pointers that can hold addresses of any data type.
  • Function Pointers: Pointers that store the addresses of functions, allowing for dynamic function invocation.
  • Pointer to Pointer: A pointer that holds the address of another pointer.

FAQs: Pointer in C

Q: Are pointers difficult to learn?

A: Pointers have a reputation for being tricky, but with practice and understanding of core concepts, they become powerful tools.

Q: What’s the difference between a pointer and a regular variable?

A: Regular variables store values, while pointers store memory addresses of those values.

Q: What are some common mistakes to avoid with pointers?

A: Uninitialized pointers, dangling pointers (pointing to freed memory), and memory leaks are common pitfalls to watch out for.

Q: How can I debug pointer-related errors?

A: Use debuggers, memory profilers, and code analyzers to identify and fix pointer errors.

Q: Are pointers used in other programming languages?

A: Yes, many languages (C++, Rust, etc.) use pointers or similar concepts like references.