Pointer Basics in C: What Is A Pointer?

Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 24

Pointer Basics in C

The real power of C lies in pointers. The pointers are slightly difficult to grasp at first. After going
through the basics of pointers, you will get a better idea about what they are and how to use them.

What is a Pointer? #
A pointer is a variable used to store a memory address. Let's first learn how memory is organized
inside a computer.

Memory in a computer is made up of bytes (A byte consists of 8 bits) arranged in a sequential


manner. Each byte has a number associated with it just like index or subscript in an array, which is
called the address of the byte. The address of byte starts from 0 to one less than size of memory. For
example, say in a 64MB of RAM, there are 64 * 2^20 = 67108864 bytes. Therefore the address of
these bytes will start from 0 to 67108863.

Let's see what happens when you declare a variable.

int marks;
As we know an int occupies 4 bytes of data (assuming we are using a 32-bit compiler) , so compiler
reserves 4consecutive bytes from memory to store an integer value. The address of the first byte of
the 4 allocated bytes is known as the address of the variable marks. Let's say that address
of 4 consecutive bytes are 5004, 5005, 5006and 5007 then the address of the variable marks will
be 5004.
Address Operator (&) #
To find the address of a variable, C provides an operator called address operator (&). To find out the
address of the variable marks we need to place & operator in front of it, like this:
&marks
The following program demonstrates how to use address operator (&).
// Program to demonstrate address(&) operator

#include<stdio.h>

int main()
{
int i = 12;

printf("Address of i = %u \n", &i);


printf("Value of i = %d ", i);

// signal to operating system program ran fine


return 0;
}
Expected Output:
Address of i = 2293340
Value of i = 12
Note: Address of i may vary every time you run the program.

**How it works ? **

To find the address of the variable, precede the variable name by & operator. Another important
thing to notice about the program is the use of %u conversion specification. Recall
that %u conversion specification is used to print unsigned decimal numbers and since the memory
addresses can't be negative, you must always use %uinstead of %d.
Address of operator (&) can't be used with constants or expression, it can only be used with a
variable.
&var; // ok

&12; // error because we are using & operator with a constant

&(x+y) // error because we are using & operator with an expression


We have been using address operator(&) in the function scanf() without knowing why ? The address
of a variable is provided to scanf(), so that it knows where to write data.
Declaring Pointer Variables #
As already said a pointer is a variable that stores a memory address. Just like any other variables you
need to first declare a pointer variable before you can use it. Here is how you can declare a pointer
variable.

Syntax: data_type *pointer_name;


data_type is the type of the pointer (also known as the base type of the pointer). pointer_name is the
name of the variable, which can be any valid C identifier.

Let's take some examples:

int *ip;
float *fp;
int *ip means that ip is a pointer variable capable of pointing to variables of type int. In other words,
a pointer variable ip can store the address of variables of type int only. Similarly, the pointer
variable fp can only store the address of a variable of type float. The type of variable (also known as
base type) ip is a pointer to int and type of fp is a pointer to float. A pointer variable of type pointer
to int can be symbolically represented as (int *). Similarly, a pointer variable of type pointer to float
can be represented as (float *).
Just like other variables, a pointer is a variable so, the compiler will reserve some space in memory.
All pointer variable irrespective of their base type will occupy same space in memory.
Normally 4 bytes or 2 bytes (On a 16-bit Compiler) are used to store a pointer variable (this may vary
from system to system).
Assigning Address to Pointer Variable #
After declaring a pointer variable the next step is to assign some valid memory address to it. You
should never use a pointer variable without assigning some valid memory address to it, because just
after declaration it contains garbage value and it may be pointing to anywhere in the memory. The
use of an unassigned pointer may give an unpredictable result. It may even cause the program to
crash.

int *ip, i = 10;


float *fp, f = 12.2;

ip = &i;
fp = &f;
Here ip is declared as a pointer to int, so it can only point to the memory address of an int variable.
Similarly, fp can only point to the address of a float variable. In the last two statements, we have
assigned the address of i and f to ip and fp respectively. Now ip points to variable i and fp points to
variable f. It is important to note that even if you assign an address of a float variable to
an int pointer, the compiler will not show you any error but you may not get the desired result. So as
a general rule always you should always assign the address of a variable to its corresponding pointer
variable of the same type.

We can initialize pointer variable at the time of declaration, but in this case, the variable must be
declared and initialized before the pointer variable.

int i = 10, *iptr = &i;

You can assign the value of one pointer variable to another pointer variable If their base type is same.
For example:

int marks = 100, *p1, *p2;

p1 = &marks;

p2 = p1;
After the assignment p1 and p2 points to the same variable marks.
[img]

As already said when a pointer variable is declared it contains garbage value and it may be point
anywhere in the memory. You can assign a symbolic constant called NULL (defined in stdio.h) to
any pointer variable. The assignment of NULL guarantees that pointer doesn't point to any valid
memory location.
int i = 100, *iptr;

iptr = NULL;
Dereferencing Pointer Variable #
Dereferencing a pointer variable simply means accessing data at the address stored in the pointer
variable. Up until now, we have been using the name of the variable to access data inside it, but we
can also access variable data indirectly using pointers. To make it happen, we will use a new operator
called indirection operator (*). By placing indirection operator (*) before a pointer variable we can
access data of the variable whose address is stored in the pointer variable.
int i = 100, *ip = &i;
Here ip stores address of variable i, if we place * before ip then we can access data stored in the
variable i. It means following two statements does the same thing.
printf("%d\n", *ip); // prints 100
printf("%d\n", i); // prints 100
Indirection operator (*) can be read as value at the address. For example, *ip can be read as value at
address ip.
Note: It is advised that you must never apply indirection operator to an uninitialized pointer variable,
doing so may cause unexpected behavior or the program may even crash.
int *ip;
printf("%d", *p); // WRONG

Now we know by dereferencing a pointer variable, we can access the value at the address stored in
the pointer variable. Let's dig a little deeper to view how the compiler actually retrieves data.

char ch = 'a';
int i = 10;
double d = 100.21;
char *cp = &ch;
int *ip = &i;
double *ip = &d;
Let's say pointer cp contains the address 1000. When we write *cp the compiler knows that it has to
retrieve information from the starting address 1000. Now the question arises how much data to
retrieve from starting address 1000 ? 1 bytes, 2 bytes; What do you think ? To know how much
information to retrieve from starting address 1000, the compiler looks at the base type of pointer and
will retrieve the information depending upon the base type of pointer. For example, if the base type is
pointer to char then1byte of information from the starting address will be retrieved and if the base
type pointer tointthen4bytes of information from the starting address will be retrieved. It is important
to note that if you are on a system where the size ofintis2bytes then2` bytes of information from the
starting address will be retrieved.
So in our case, only 1 byte of data from starting address will be retrieved. i.e the data stored at
address 2000will be retrieved only.
Similarly, if ip points to the address 2000. On writing *ip compiler will retrieve 4 bytes of data
starting from address 2000.

In the following image, shaded portion shows the number of bytes retrieved.

Before moving ahead, Interpret the meaning of the following expression:

*(&i) , where i is a variable of type int.


We know from the precedence table that parentheses () has the highest precedence, so &i is evaluated
first. Since &i is the address of variable i, so dereferencing it with * operator will give us the value of
the variable i. So we can conclude that writing *(&i) is same as writing i.
The following example demonstrates everything we have learned about pointers so far.

#include<stdio.h>

int main()
{
int i = 12, *ip = &i;
double d = 2.31, *dp = &d;

printf("Value of ip = address of i = %d\n", ip);


printf("Value of fp = address of d = %d\n\n", d);

printf("Address of ip = %d\n", &ip);


printf("Address of dp = %d\n\n", &dp);

printf("Value at address stored in ip = value of i = %d\n", *ip);


printf("Value at address stored in dp = value of d = %f\n\n", *dp);

// memory occupied by pointer variables


// is same regardless of its base type

printf("Size of pointer ip = %d\n", sizeof(ip));


printf("Size of pointer dp = %d\n\n", sizeof(dp));

// signal to operating system program ran fine


return 0;
}
Expected Output:
Value of ip = address of i = 2686788
Value of fp = address of d = 1202590843

Address of ip = 2686784
Address of dp = 2686772

Value at address stored in ip = value of i = 12


Value at address stored in dp = value of d = 2.310000

Size of pointer ip = 4
Size of pointer dp = 4
Note: Memory address may vary every time you run the program.

There is nothing new in the above program that deserves any explanation. Before we proceed to next
chapter, always remember that the size of the pointer variables is same regardless of its base type but
the size of the memory address that will be accessed while dereferencing depends on upon the base
type of the pointer variable.
Pointer Arithmetic in C
You should know by now that a pointer is nothing more that a variable used to store a memory
address. And if this is new to you go back and read Pointer Basics before continuing with this
chapter. In this chapter, we will discuss arithmetic operations that can be performed on pointers.

We can't perform every type of arithmetic operations with pointers. Pointer arithmetic is slightly
different from arithmetic we normally use in our day to day life. The only valid arithmetic operations
applicable on pointers are:

1. Addition of integer to a pointer


2. Subtraction of integer to a pointer
3. Subtracting one pointer from another of the same type
The pointer arithmetic is performed relative to the base type of the pointer. For example: if we have
an integer pointer ip which contains address 1000, then on incrementing it by 1, we will
get 1004 (i.e 1000 + 1 * 4) instead of 1001 because the size of the int data type is 4 bytes. If we had
been using a system where the size of int is 2 bytes then we would get 1002 ( i.e 1000 + 1 * 2 ).
Similarly, on decrementing it we will get 996 (i.e 1000 - 1 * 4) instead of 999. So the expression ip +
4 will point to address 1016 (i.e 1000 + 4 * 4 ).

Let's take some more examples.

int i = 12, *ip = &i;


double d = 2.3, *dp = &f;
char ch = 'a', *cp = &ch;
Suppose the address of i, d and ch are 1000, 2000 , 3000 respectively, therefore ip, fp and cp are
at 1000, 2000, 3000 initially.
Pointer Arithmetic on Integers #
Pointer Expression How it is evaluated ?
ip = ip + 1 ip => ip + 1 => 1000 + 1*4 => 1004
ip++ or ++ip ip++ => ip + 1 => 1004 + 1*4 => 1008
ip = ip + 5 ip => ip + 5 => 1008 + 5*4 => 1028
ip = ip - 2 ip => ip - 2 => 1028 - 2*4 => 1020
ip-- or --ip ip => ip + 2 => 1020 + 2*4 => 1028
Pointer Arithmetic on Float #
Pointer Expression How it is evaluated ?
dp + 1 dp = dp + 1 => 2000 + 1*8 => 2008
dp++ or ++dp dp++ => dp+1 => 2008+1*8 => 2016
dp = dp + 5 dp => dp + 5 => 2016+5*8 => 2056
dp = dp - 2 dp => dp - 2 => 2056-2*8 => 2040
Pointer Expression How it is evaluated ?
dp-- or --dp dp => dp - 1=>2040-1*8=>2032`
Pointer Arithmetic on Characters #
Pointer Expression How it is evaluated ?
cp + 1 cp = cp + 1 => 3000 + 1*1 => 3001
cp++ or ++cp cp => cp + 1 => 3001 + 1*1 => 3002
cp = cp + 5 cp => cp + 5 => 3002 + 5*1 => 3007
cp = cp - 2 cp => cp + 5 => 3007 - 2*1 => 3005
cp-- or --cp cp => cp + 2 => 3005 - 1*1 => 3004
Note: When we increment or decrement pointer variables using pointer arithmetic then, the address
of variables i, d, ch are not affected in any way.
Arithmetic operation on type char seems like ordinary arithmetic because the size of char type
is 1 byte. Another important point to note is that when we increment and decrement pointer variable
by adding or subtracting numbers then it is not necessary that the pointer variable still points to a
valid memory location. So, we must always pay special attention when we move the pointer in this
way. Generally, we use pointer arithmetic with arrays because elements of an array are arranged in
contiguous memory locations , which is discussed in detail in the next chapter.

The following program shows pointer arithmetic.

#include<stdio.h>

int main()
{
int i = 12, *ip = &i;
double d = 2.3, *dp = &d;
char ch = 'a', *cp = &ch;

printf("Value of ip = %d\n", ip);


printf("Value of dp = %d\n", dp);
printf("Value of cp = %d\n\n", cp);

printf("Value of ip + 1 = %d\n", ip + 1);


printf("Value of dp + 1 = %d\n", dp + 1);
printf("Value of cp + 1 = %d\n\n", cp + 1);

printf("Value of ip + 2 = %d\n", ip + 2);


printf("Value of dp + 2 = %d\n", dp + 2);
printf("Value of cp + 2 = %d\n", cp + 2);

// signal to operating system program ran fine


return 0;
}
Expected Output:
Value of ip = 2293316
Value of dp = 2293304
Value of cp = 2293303

Value of ip + 1 = 2293320
Value of dp + 1 = 2293312
Value of cp + 1 = 2293304

Value of ip + 2 = 2293324
Value of dp + 2 = 2293320
Value of cp + 2 = 2293305
Pointer Arithmetic between Two Pointers #
If we have two pointers p1 and p2 of base type pointer to int with
addresses 1000 and 1016 respectively, then p2 - p1 will give 4, since the size of int type is 4 bytes. If
you subtract p2 from p1 i.e p1 - p2 then answer will be negative i.e -4.

The following program demonstrates pointer arithmetic between two pointers of the same type.

#include<stdio.h>

int main()
{
int i1 = 12, *ip1 = &i1;
int i2 = 12, *ip2 = &i2;

printf("Value of ip1 or address of i1 = %u\n", ip1);


printf("Value of ip2 or address of i2 = %u\n\n", ip2);

printf("ip2 - ip1 = %d\n", ip1 - ip2);


printf("ip1 - ip2 = %d\n", ip2 - ip1);

// signal to operating system program ran fine


return 0;
}
Expected Output:
Value of ip1 or address of i1 = 2686788
Value of ip2 or address of i2 = 2686780

ip2 - ip1 = 2
ip1 - ip2 = -2
Combining Indirection operator (*) and Increment/Decrement
operator #
While processing elements of an array (as you will see in the next chapter) C programmers often mix
indirection operator (*) and increment/decrement operator( ++ and -- ).
Always remember that the precedence of indirection operator (*) and increment/decrement operator
are same and they associate from right to left.
Suppose x is integer variable and p is a pointer to int. Now consider the following statements and try
to interpret them.
Example 1:
x = *p++;
Since * and ++ operators have the same precedence and associate from right to left ++ will be
applied to p not to *p. Because the increment operator is postfix, so first the value of p is used in the
expression then it will be incremented. Therefore first integer pointed by p will be dereferenced and
assigned to x, then the value of p will be incremented by 1.
Example 2:
x = ++*p;
Here * operator will be first applied to p then ++ will be applied to *p. Therefore first integer pointer
is dereferenced, the value obtained from dereferencing is incremented and eventually assigned to x.
Example 3:
x = *++p;
++ operator is prefixed, so first, p will be incremented, then the value at the new address is
dereferenced and assigned to x.
Note: If you still have any confusion, you can always use () around expression which you want to
evaluate first.
Pointer Comparison #
You can use relational operators ( <, <=, >, >= , == , !=) with pointers. The == and !=operators are
used to compare two pointers whether they contain the same address or not. Two pointers are equal
when they both are null, or contains the address of the same variable. Use of these (i.e == and !=)
operators are valid only when pointers are of the same base type, or between a null pointer and any
other pointer, or void pointer(will be discussed later) and any other pointer. Use of other relational
operators (<, <=, >, >=) to compare two pointers is meaningful only when they both point to the
elements of the same array.
Pointer to Pointer #
As we know the pointer is a variable that contains the memory address. The pointer variable itself
occupies some space in memory and hence it also has a memory address. We can store the address of
pointer variable in some other variable, which is known as pointer to pointer. The syntax of declaring
pointer to pointer is as follows:

Syntax: data_type **p;

Let's take an example:

int i = 10;
int *ip = &i;
int **iip = &ip;
Here ip is of type (int *) or pointer to int, iip is of type (int **) or pointer to pointer to int.
We know that *ip will give value at address ip i.e value of i. Can you guess what value will **iip will
return ?
**iip
We know that indirection operator evaluated from right to left so **iip can also be written as
*(*iip)
*iip means value at address iip or address stored at ip. On dereferencing address stored at ip we will
get the value stored in the variable i.
*(*iip)
=> *ip
=> i
Therefore **iip gives the value stored in the variable i.

The following program demonstrates how to use pointer to pointer in int.

#include<stdio.h>

int main()
{
int i = 10;
int *ip = &i;
int **iip = &ip;

printf("Value of i = %d\n\n", i);

printf("Address of i = %u\n", &i);


printf("Value of ip = %d\n\n", ip);

printf("Address of ip = %u\n", &ip);


printf("Value of iip = %d\n\n", iip);

printf("Value of *iip = value of ip = %d\n", *iip);


printf("Value of **iip = value of i = %d\n\n", **iip);

// signal to operating system program ran fine


return 0;
}
Expected Output:
Value of i = 10

Address of i = 2293332
Value of ip = 2293332

Address of ip = 2293320
Value of iip = 2293320

Value of *iip = value of ip = 2293332


Value of **iip = value of i = 10

Pointers and 1-D arrays


In C, the elements of an array are stored in contiguous memory locations. For example: if we have
the following array.

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

Then, this is how elements are stored in the array.

Here the first element is at address 5000, since each integer takes 4 bytes the next element is
at 5004 and so on.

In C, pointers and arrays are very closely related. We can access the elements of the array using a
pointer. Behind the scenes compiler also access elements of the array using pointer notation rather
than subscript notation because accessing elements using pointer is very efficient as compared to
subscript notation. The most important thing to remember about the array is this:
The name of the array is a constant pointer that points to the address of the first element of the array
or the base address of the array.

We can use subscript notation (i.e using square brackets) to find the address of the elements of the
array. For example:

int my_arr[5] = {11, 22, 33, 44, 55};


here &my_arr[0] points to the address of the first element of the array. Since the name of the array is
a constant pointer that points to the first element of the array, my_arr and &my_arr[0] represent the
same address. &my_arr[1] points to the address of the second element. Similarly &my_arr[2] points
to the address of the third element and so on.
Note: my_arr is of type (int *) or pointer to int.

The following program demonstrates that the elements of an array are stored in contiguous memory
locations.

#include<stdio.h>

int main()
{
int my_arr[5] = {1, 2, 3, 4, 5}, i;

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


{
printf("Value of a[%d] = %d\t", i, my_arr[i]);
printf("Address of a[%d] = %u\n", i, &my_arr[i]);
}

// signal to operating system program ran fine


return 0;
}
Expected Output:
Value of a[0] = 1 Address of a[0] = 2293312
Value of a[1] = 2 Address of a[1] = 2293316
Value of a[2] = 3 Address of a[2] = 2293320
Value of a[3] = 4 Address of a[3] = 2293324
Value of a[4] = 5 Address of a[4] = 2293328
Note: Memory address may differ every time you run the program.
Using pointers to access elements and address of elements in an
array #
We know that name of the array is a constant pointer to the first element. Consider the following
snippet:

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


Here arr is a pointer to the first element. But, What is the base type of pointer arr ? If your answer is a
pointer to int or (int *). Well, Done ;).
In this case, arr points to the address of an integer number i.e address of integer 1. So the base type
of arr is a pointer to int or (int*) .

Let's take some more examples:

char arr[] = {'A','B','C','D','E'};


What is the type of pointer arr ?.
Here arr points to the address of the first element which is a character. So the type of arr is a pointer
to charor (char *).

Similarly,

double arr[] = {1.03, 29.3, 3.42, 49.3, 51.2};


here arr is a pointer of type pointer to double or (double *).
Note: These concepts are the building blocks for the upcoming chapters, so don't skip them. If it is
still confusing to you, go through it one more time.
Now you can easily access values and address of elements using pointer arithmetic.
Suppose my_arr is an array of 5 integers.
int my_arr[5] = {11, 22, 33, 44, 55};
Here my_arr is a constant pointer of base type pointer to int or (int *) and according to pointer
arithmetic when an integer is added to a pointer we get the address of the next element of same base
type. So In the above example my_arr points to the address of the first element, my_arr+1 points to
the address of the second element, my_arr + 2 points to the address of the third element and so on.
Therefore we can conclude that:
my_arr is same as &my_arr[0] my_arr + 1 is same as &my_arr[1] my_arr + 2 is same
as &my_arr[2] my_arr + 3 is same as &my_arr[3] my_arr + 4 is same as &my_arr[4]
In general (my_arr + i) is same as writing &my_arr[i].
Now we know how to get the address of each element of the array, by using indirection operator (*)
we can get the value at the address. If we dereference my_arr then we get the first element of the
array i.e *my_arr. Similarly, *(my_arr + 1) will return the second element of the array and so on.
`*(my_arr)` is same as `my_arr[0]`
`*(my_arr + 1)` is same as `my_arr[1]`
`*(my_arr + 2)` is same as `my_arr[2]`
`*(my_arr + 3)` is same as `my_arr[3]`
`*(my_arr + 4)` is same as `my_arr[4]`
In general *(my_arr+i) is same as writing my_arr[i].

The following program prints value and address of array elements using pointer notation.

#include<stdio.h>

int main()
{
int my_arr[5] = {1, 2, 3, 4, 5}, i;

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


{
printf("Value of a[%d] = %d\t", i, *(my_arr + i) );
printf("Address of a[%d] = %u\n", i, my_arr + i );
}

// signal to operating system program ran fine


return 0;
}
Expected Output:
Value of a[0] = 1 Address of a[0] = 2293312
Value of a[1] = 2 Address of a[1] = 2293316
Value of a[2] = 3 Address of a[2] = 2293320
Value of a[3] = 4 Address of a[3] = 2293324
Value of a[4] = 5 Address of a[4] = 2293328
Note: Memory address may differ every time you run the program.
Assigning 1-D array to a Pointer variable #
Yes, you can assign a 1-D array to a pointer variable. Consider the following example:

int *p;
int my_arr[] = {11, 22, 33, 44, 55};
p = my_arr;
Now you can use pointer p to access address and value of each element in the array. It is important to
note that assignment of a 1-D array to a pointer to int is possible because my_arr and p are of the
same base type i.e pointer to int. In general (p+i) denotes the address of the ith element
and *(p+i) denotes the value of the ith element.
There are some differences between the name of the array (i.e my_arr) and pointer variable (i.e p).
The name of the array is a constant pointer hence you can't alter it to point to some other memory
location. You can’t assign some other address to it nor you can apply increment/decrement operator
like you do in a pointer variable.
my_arr++; // error
my_arr--; // error
my_arr = &i // error
However, p is an ordinary pointer variable, so you can apply pointer arithmetic and even assign a
new address to it.
p++; // ok
p--; // ok
p = &i // ok

The following program demonstrates how you can access values as address of elements of a 1-D
array by assigning it to a pointer variable.

#include<stdio.h>

int main()
{
int my_arr[5] = {1, 2, 3, 4, 5}, i;
int *p;
p = my_arr;
// p = &my_arr[0]; // you can also do this
for(i = 0; i < 5; i++)
{
printf("Value of a[%d] = %d\t", i, *(p + i) );
printf("Address of a[%d] = %u\n", i, p + i );
}

// signal to operating system program ran fine


return 0;
}
Expected Output:
Value of a[0] = 1 Address of a[0] = 2293296
Value of a[1] = 2 Address of a[1] = 2293300
Value of a[2] = 3 Address of a[2] = 2293304
Value of a[3] = 4 Address of a[3] = 2293308
Value of a[4] = 5 Address of a[4] = 2293312
Note: Memory address may differ every time you run the program.

Pointers and 2-D arrays


In the last chapter, we have created a pointer which points to the 0th element of the array whose base
type was (int *) or pointer to int. We can also create a pointer that can point to the whole array
instead of only one element of the array. This is known as a pointer to an array. Here is how you can
declare a pointer to an array.
int (*p)[10];
Here p is a pointer that can point to an array of 10 integers. In this case, the type or base type of p is a
pointer to an array of 10 integers.
Note that parentheses around p are necessary, so you can't do this:
int *p[10];
here p is an array of 10 integer pointers. An array of pointers will be discussed in upcoming chapters.

A pointer that points to the 0th element of an array and a pointer that points to the whole array are
totally different. The following program demonstrates this concept.

#include<stdio.h>

int main()
{
int *p; // pointer to int
int (*parr)[5]; // pointer to an array of 5 integers
int my_arr[5]; // an array of 5 integers

p = my_arr;
parr = my_arr;

printf("Address of p = %u\n", p );
printf("Address of parr = %u\n", parr );

p++;
parr++;

printf("\nAfter incrementing p and parr by 1 \n\n");


printf("Address of p = %u\n", p );
printf("Address of parr = %u\n", parr );

printf("Address of parr = %u\n", *parr );

// signal to operating system program ran fine


return 0;
}
Expected Output:
Address of p = 2293296
Address of parr = 2293296

After incrementing p and parr by 1

Address of p = 2293300
Address of parr = 2293316

**How it works ? **

Here p is a pointer which points to the 0th element of the array my_arr, while parr is a pointer which
points to the whole array my_arr. The base type of p is of type (int *) or pointer to int and base type
of parr is pointer to an array of 5 integers. Since the pointer arithmetic is performed relative to the
base type of the pointer, that's why parr is incremented by 20 bytes i.e ( 5 x 4 = 20 bytes ). On the
other hand, p is incremented by 4bytes only.

The important point you need to remember about pointer to an array is this:

Whenever a pointer to an array is dereferenced we get the address (or base address) of the array to
which it points.
So, on dereferencing parr, you will get *parr. The important thing to notice is
although parr and *parr points to the same address, but parr's base type is a pointer to an array
of 5 integers, while *parr base type is a pointer to int. This is an important concept and will be used
to access the elements of a 2-D array.
Pointers and 2-D Array #
While discussing 2-D array in the earlier chapters, we told you to visualize a 2-D array as a matrix.
For example:

int arr[3][4] = {
{11,22,33,44},
{55,66,77,88},
{11,66,77,44}
};

The above 2-D array can be visualized as following:

While discussing array we are using terms like rows and column. Well, this concept is only
theoretical, because computer memory is linear and there are no rows and cols. So how actually 2-D
arrays are stored in memory ? In C, arrays are stored row-major order. This simply means that first
row 0 is stored, then next to it row 1 is stored, next to it row 2 is stored and so on.

The following figure shows how a 2-D array is stored in the memory.

Here is the most important concept you need to remember about a multi-dimensional array.

A 2-D array is actually a 1-D array in which each element is itself a 1-D array. So arr is an array of 3
elements where each element is a 1-D array of 4 integers.
In the previous chapter, we have already discussed that the name of a 1-D array is a constant pointer
to the 0th element. In the case, of a 2-D array, 0th element is a 1-D array. Hence in the above
example, the type or base type of arr is a pointer to an array of 4 integers. Since pointer arithmetic is
performed relative to the base size of the pointer. In the case of arr, if arr points to
address 2000 then arr + 1 points to address 2016 (i.e 2000 + 4*4) .
We know that the name of the array is a constant pointer that points to the 0th element of the array.
In the case of a 2-D array, 0th element is a 1-D array. So the name of the array in case of a 2-D array
represents a pointer to the 0th 1-D array. Therefore in this case arr is a pointer to an array
of 4 elements. If the address of the 0th 1-D is 2000, then according to pointer arithmetic (arr + 1) will
represent the address 2016, similarly (arr + 2) will represent the address 2032.

From the above discussion, we can conclude that:

arr points to 0th 1-D array. (arr + 1) points to 1st 1-D array. (arr + 2) points to 2nd 1-D array.

In general, we can write:

(arr + i) points to ith 1-D array.


As we discussed earlier in this chapter that dereferencing a pointer to an array gives the base address
of the array. So dereferencing arr we will get *arr, base type of *arr is (int*). Similarly, on
dereferencing arr+1 we will get *(arr+1). In general, we can say that:
*(arr+i) points to the base address of the ith 1-D array.
Again it is important to note that type (arr + i) and *(arr+i) points to same address but their base
types are completely different. The base type of (arr + i) is a pointer to an array of 4 integers, while
the base type of *(arr + i) is a pointer to int or (int*).

So how you can use arr to access individual elements of a 2-D array?

Since *(arr + i) points to the base address of every ith 1-D array and it is of base type pointer to int,
by using pointer arithmetic we should we able to access elements of ith 1-D array.

Let's see how we can do this:

*(arr + i) points to the address of the 0th element of the 1-D array. So,
*(arr + i) + 1 points to the address of the 1st element of the 1-D array
*(arr + i) + 2 points to the address of the 2nd element of the 1-D array
Hence we can conclude that:

*(arr + i) + j points to the base address of jth element of ith 1-D array.

On dereferencing *(arr + i) + j we will get the value of jth element of ith 1-D array.

*(*(arr + i) + j)

By using this expression we can find the value of jth element of ith 1-D array.

Furthermore, the pointer notation ((arr + i) + j) is equivalent to the subscript notation

The following program demonstrates how to access values and address of elements of a 2-D array
using pointer notation.

#include<stdio.h>

int main()
{
int arr[3][4] = {
{11,22,33,44},
{55,66,77,88},
{11,66,77,44}
};

int i, j;

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


{
printf("Address of %d th array %u \n",i , *(arr + i));
for(j = 0; j < 4; j++)
{
printf("arr[%d][%d]=%d\n", i, j, *( *(arr + i) + j) );
}
printf("\n\n");
}

// signal to operating system program ran fine


return 0;
}
Expected Output:
Address of 0 th array 2686736
arr[0][0]=11
arr[0][1]=22
arr[0][2]=33
arr[0][3]=44

Address of 1 th array 2686752


arr[1][0]=55
arr[1][1]=66
arr[1][2]=77
arr[1][3]=88

Address of 2 th array 2686768


arr[2][0]=11
arr[2][1]=66
arr[2][2]=77
arr[2][3]=44
Assigning 2-D Array to a Pointer Variable #
You can assign the name of the array to a pointer variable, but unlike 1-D array you will need pointer
to an array instead of pointer to int or (int *) .

Here is an example:

int arr[2][3] = {
{33, 44, 55},
{11, 99, 66}
};
Always remember a 2-D array is actually a 1-D array where each element is a 1-D array. So arr as an
array of 2elements where each element is a 1-D arr of 3 integers. Hence to store the base address
of arr, you will need a pointer to an array of 3 integers.
Similarly, If a 2-D array has 3 rows and 4 cols i.e int arr[3][4], then you will need a pointer to an
array of 4integers.
int (*p)[3];
Here p is a pointer to an array of 3 integers. So according to pointer arithmetic p+i points to the ith 1-
D array, in other words, p+0 points to the 0th 1-D array, p+1 points to the 1st 1-D array and so on.
The base type of (p+i) is a pointer to an array of 3 integers. If we dereference (p+i) then we will get
the base address of ith 1-D array but now the base type of *(p + i) is a pointer to int or (int *). Again
to access the address of jth element ith 1-D array, we just have to add j to *(p + i). So *(p + i) +
j points to the address of jth element of ith 1-D array. Therefore the expression *(*(p + i) + j) gives
the value of jth element of ith 1-D array.

The following program demonstrates how to access elements of a 2-D array using a pointer to an
array.

#include<stdio.h>

int main()
{
int arr[3][4] = {
{11,22,33,44},
{55,66,77,88},
{11,66,77,44}
};

int i, j;
int (*p)[4];

p = arr;

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


{
printf("Address of %d th array %u \n",i , p + i);
for(j = 0; j < 4; j++)
{
printf("arr[%d][%d]=%d\n", i, j, *( *(p + i) + j) );
}
printf("\n\n");
}

// signal to operating system program ran fine


return 0;
}
Expected Output:
Address of 0 th array 2686736
arr[0][0]=11
arr[0][1]=22
arr[0][2]=33
arr[0][3]=44

Address of 1 th array 2686752


arr[1][0]=55
arr[1][1]=66
arr[1][2]=77
arr[1][3]=88

Address of 2 th array 2686768


arr[2][0]=11
arr[2][1]=66
arr[2][2]=77
arr[2][3]=44

The following is an instantiation of the various "just print it" suggestions. I found it
instructive.

#include "stdio.h"

int main() {
static int x = 5;
static int *p = &x;
printf("(int) p => %d\n",(int) p);
printf("(int) p++ => %d\n",(int) p++);
x = 5; p = &x;
printf("(int) ++p => %d\n",(int) ++p);
x = 5; p = &x;
printf("++*p => %d\n",++*p);
x = 5; p = &x;
printf("++(*p) => %d\n",++(*p));
x = 5; p = &x;
printf("++*(p) => %d\n",++*(p));
x = 5; p = &x;
printf("*p++ => %d\n",*p++);
x = 5; p = &x;
printf("(*p)++ => %d\n",(*p)++);
x = 5; p = &x;
printf("*(p)++ => %d\n",*(p)++);
x = 5; p = &x;
printf("*++p => %d\n",*++p);
x = 5; p = &x;
printf("*(++p) => %d\n",*(++p));
return 0;
}
It returns
(int) p => 256688152
(int) p++ => 256688152
(int) ++p => 256688156
++*p => 6
++(*p) => 6
++*(p) => 6
*p++ => 5
(*p)++ => 5
*(p)++ => 5
*++p => 0
*(++p) => 0
I cast the pointer addresses to ints so they could be easily compared.
I compiled it with GCC.

You might also like