C Programming IT Programming Tutorials

Pointers in C

Pointers in C are defined as a variable, that represents the address location of another variable. The pointer is the most important topic which helps in memory management and has many useful applications.

It helps to pass values between the functions and their argument and also allows passing a function as an argument to other functions. It provides alternate ways to access each element of an array easily.

What is a pointer variable?

A variable that holds the address of another variable. It is usually represented by the ‘*’ which is the dereference symbol.

Syntax:

Data_type *var_name;

eg: int *b;

      char *q;

For example, 

int a=10, *b;where b=&a and 

‘&’ is the address-of operator.   *b gives the value inside the address which is 10.                                                                                                                        

Storage of normal and pointer variable

The variable is stored in the address given by the hexadecimal value EC8 and since b is a pointer variable, b holds the value EC8 and *b gives the value inside that address, here it is 10. The pointer variable b is stored in another location F6C.  

Null Pointer in C

Once we declare the pointer it is necessary to assign a valid address otherwise it will refer to some random address in the memory that contains some garbage value. To avoid such cases it is necessary to assign a constant value known as NULL which is predefined in the directives.
The pointer which is assigned the null value is known as the null pointer.

eg: int *p=NULL;

Since the null pointer is a constant with the value 0, if you try to print p it will give the value 0. It means address 0 is not an accessible memory location and it is reserved by the operating system.

Pointer Arithmetic in C

As we know pointer variable holds the address of another variable, and we can perform some of the arithmetic operations like ++, –, addition and subtraction with the addresses.
Now to understand the concept better, let us see an example, 

x=100,*y; 

y=&x=>204356, 

suppose if we increment the address,

y++=>(204356+4) 32bit processor.                                                           

y++=>(204356+2) 64bit processor.

If we increment y value by 3=> y+3=>(204356+(4or2)x3).

Example 1:    

Explanation:

In the above example, the pointer variable y holds the address of x which when incremented, increases the value by the number of bytes according to the given datatype. Similarly when decremented reduces the value.

Example 2:

Explanation: In this example, the pointer variable ‘b’ holds the base address (starting address) of an array. Since all elements of an array are placed in a continuous memory location, the value of b is incremented and the address and elements of an array are printed until the value of b is less than or equal to the address of the last element of an array.

Array Of Pointers in C

In arrays, we store values of a similar data type, similarly, a group of pointer variables that hold address can also be stored in the arrays, which are referred to as arrays of pointers.

The syntax is given as,

data-type *arrayname[]={ptr1,ptr2,ptr3};

or 

data-type *arrayname[3]={ptr1,ptr2,ptr3};

The array of pointers can also be used to represent the multidimensional arrays since the declared array will have one dimension less than the original multidimensional array.

Multidimensional arrays

For example, the two-dimensional array can be written as

data-type *arrayname[x];

without using the conventional way like

data-type *arrayname[x][y];

Similarly, n-dimensional array can be written as 

data-type *arrayname[x][y]....[n-1];

Not the usual way,

data-type arrayname[x][y]....[n];

Example 3:

Explanation:

The array ‘ptrarr’ holds the addresses of a, x and p as the ptrarr[0], ptrarr[1], ptrarr[2] respectively. The values inside each pointer variable of an array are obtained by using *ptrarr[0], *ptrarr[1], *ptrarr[2].

Passing Pointer variables as Arguments (Call By Reference)

Example 4:

Explanation: In this example, since we pass pointer variable as an argument we must use dereference in the argument part of function declaration and definition. During the function call, the pointer variable ‘b’ which has the address of ‘a’ is passed to the function. The function definition use dereference to get the value inside the passed address when we change the value of *b to 11. The original value inside the address gets changed and we get the value of ‘a’ as 11.

Pointer To Pointer(Double Pointers in C)

The use of a pointer to hold the address of another pointer is known as the pointer to pointer or double-pointer. While declaring a double-pointer the first pointer holds the address of the second pointer which in turn points to the location where the value is stored.

The pointer to pointer type can be declared as,

int **p; 

Double pointer

Example 5:

Explanation:

Here we have the pointer variable ‘*b’ and double-pointer ‘**p’. The ‘*b’ holds the address of a and the pointer variable ‘p’ holds the address of the ‘*b’ which in turn holds the address of ‘a’. To get the value we need to use a double dereference that is **p, which gives the value 100.

Void pointers in C

  • A pointer that has no associated data type and can be cast to any data type is known as a void pointer.
  • It is known as a generic pointer.
  • The malloc() or calloc() functions return void * or generic pointers.
  • The void pointer can be assigned to any data type’s address and assign any pointer to the void pointer without typecasting.

Syntax:

void *(pointer-variable)

Example 5:

Explanation: The void pointer is assigned with the address of an integer, float, and char variables. To access the values inside each address, it is typecast to the respective data types. The size of the pointer changes based on the compiler.

Wild pointers in C

The uninitialized pointer is known as the wild pointer. It points to some unknown memory location which leads to some problems in the program and it is not a good practice.
It is necessary to assign the value for a pointer variable once declared, if not you may get some undesired results or errors.

Example 6:

Explanation: The pointer variable *p is declared but it is not assigned to any memory address, so when we print, it gives some random values. 

Dangling pointers in C

A pointer that points to the memory location that has been deleted or freed is known as the dangling pointer. Since the location is not valid anymore when dereferenced gives some undesired results or segmentation fault. 
Some important cases where the pointer acts as a dangling pointer.

1. Function call 2. De-allocation of memory

Example 7:

Explanation: The variable x is local to the function so its scope lies only within the function, which when assigned to the pointer implies assigning the address of freed memory, making that pointer act as a dangling pointer.

Example 8:

Explanation: The address of the variable a is assigned to the pointer *p which is deallocated using the c library function free(), so the *p acts as a dangling pointer
NOTE:
The commonly known third case where the pointer act as the dangling pointer variable is out of scope.

Example 9:

Explanation: The address of variable t which has a value of 10 is assigned to ptr inside {…}, which when printed outside we expect to act as a dangling pointer since variable t scope is local but in many cases, we get the original value 10.
I will not mention this as a case where a pointer acts as a dangling pointer, I will attach more details in the future if I get any supporting documents. 

Complex pointers in C

When a pointer declaration is difficult to read it is referred to as complex pointers. It includes (), [], *, identifier and datatype. To read the declaration we must understand the precedence and associativity of the operators.

OperatorsPriorityAssociativity
(),  []1Left to Right
*,  Identifier2Right to Left
Datatype3

Now let us see some examples of complex pointer 

Consider the following example, 

eg:1.  A pointer to an array

pointer to an array

The numbering is done as per the priority and associativity given in the above table. It is read as ptr is the pointer to a one-dimensional array of size 3 and has data of type integer.

eg:2  Pointer to a function with single parameter 

pointer to a function with single paramter

 ptr is the pointer to the function with the parameter of type int and returns float values.         

eg 3:  Pointer to a function with multiple parameters 

Pointer to a function with multiple parameters 

ptr is the pointer to a function with two parameters in which the first parameter is the pointer to an array with the size of 5 and has data of type int and the second parameter is the pointer to a function and parameter of type void.

Near, Far, and Huge pointers

 In the olden days, MS-DOS and 16-bit Windows programming, it is necessary to deal with memory models that explain how a program organizes the code and data.

The 8086 processors used segmented memory, which divides the system memory into groups of individual segments that are referenced by pointers in the segment registers.

Each segment handles different types of data such as the
code segment which is a portion of the memory that stores the code

The data segment is the portion of the memory where the data element of the program is stored. It remains static and cannot be expanded after declaration.

Stack contains the procedure within the program and values passed to the functions.

Each segment includes
1. Segment number (4bit)and
2. Offset address(16bit)
The segment number and offset address range 0x0000 to 0xFFFF in turbo c 3.0.

Example 10:

Explanation:

The first printf statement prints offset address, u-unsigned %p prints value of var in hexadecimal when & not used before a variable.

The %p print offset address when & is used.

%Fp is used to format a FAR pointer which is of the form –> segment address: offset address.

The pointers in the segment registers are

Near Pointer:

It stores only 16-bit addresses, so only 64kb of data can be accessed at a time.

Far Pointer:

It is a 32-bit pointer that can access outside the current segment.

The compiler allocates a segment register to the segment address and another register to store offset within the current segment to use this pointer.

Huge Pointer:

It is also a 32-bit pointer similar to a far pointer and can also access bits outside the current segment but in a far pointer, the segment is fixed but can be modified in the huge pointer.

Recommended Articles

1 Comment

Leave a Reply

Your email address will not be published. Required fields are marked *