You are on page 1of 44

Pointers in C++

Dr. K. Veningston
Department of Computer Science and Engineering
National Institute of Technology Srinagar
veningstonk@nitsri.net
Introduction to Pointers
• In C/C++,
– Entire memory is viewed as the 1-D array
– Variables generally be stored in the memory at some location.
• We use variable names for convenience
– Example: char a;
a

100 101 102 103 104 105 106 107 108

• Byte addressable --> every byte is going to get a different address


– How many cells does a variable takes?
• Variable size depends on the platform on which program in running
Pointers
• For any type T, T* is the type ‘pointer to T.’
– A variable of type T* can hold the address of an object of type T.
• Example:
char c = ‘a’;
char* p = &c; // p holds the address of c; & is the address-of operator

p &c

c ‘a’
Variable name vs. Pointer
• In C/C++,
– Example: int b;
b

100 101 102 103 104 105 106 107 108

• 2 ways of accessing variables:


– (i) Access by using their name i.e. b
• Name of a variable --> we have access inside the cell.
• It has to be converted to address, as we need the address to access an element from the
memory.
– (ii) Access by using their address i.e. 104
• It will be faster to access
Advantages of Pointer
• Pointers handle the addresses.
– It supposed to run faster compared to the program which does not
have pointers
• Dynamically created data structures
– We cannot name the address
– Handle to access pointer is address
What is a pointer?
• Pointer is a variable which contain the address of some other
variable or some other entity.
• If length of logical address is 16 bits in the system, we use a
pointer whose size is 16 bits
– Logical address --> bits used to address the memory
– Pointers should be able to hold those many bits
– Size of the pointer also depends on the platform
How to declare a pointer?
• Example: int *p;
– p is a variable of type pointer that holds the address of an integer object.
int b;
int *p;
p = &b; // &b will get the address of b and assign it to p.
– Note: remember the precedence of operators
– ‘address of’ operator is used to get the address of variables and other structures which are
declared or located in the memory.
• What pointer cannot do? Variable name p is as good as
using address 104
– Pointer cannot get the address of an expression
• Expressions cannot be declared or stored in the memory p b
– We cannot get the address of a constant 104
100 101 102 103 104 105 106 107 108

Starting address of b
& operator
• & operator can be applied to anything that has a memory
address.
– You cannot apply & on register variables as they are stored on CPU
registers.
– in C, constants are not compile-time constants (i.e. always allocated
storage), so you can safely take address of a constant variable.
Address of a constant

• NOTE:
– a const variable may not need to be assigned any storage at all (i.e. The code generated using a
const variable will often be almost like you would use a literal directly. However, your source code
gets to use a meaningful name instead).
Dereferencing or indirection
• Referring to the object pointed to by the pointer.
char c1 = ‘a’;
char* p = &c1;
char c2 = *p; // c2 = ‘a’; * is the dereference operator

• The object pointed to by p is c1, and the value stored in c1 is


‘a’, so the value of *p assigned to c2 is ‘a’.

p &c

c1 ‘a’
How to declare a pointer?
• Note:
– Either it is an int, char or any other structure, pointer size is always same.
– Variable container size may vary, but its address is same size for all the
data structures.
• Analogy: houses are of different size, but all the houses will have same size address.
– We need to change the size of the pointer depending on the type of the
pointer
• They why do we write int *p;? --> Pointer arithmetic or Address arithmetic
– Depending on the type of element, we have same size of the pointer
How to declare a pointer?
• Dereferencing or indirection operator (*) int b;
int *p;
– & is used to get the address p = &b;
– * is for dereferencing or indirection
• Name of a pointer variable --> standing outside the memory cell
• When we apply * --> getting inside the memory cell

p b
104
100 101 102 103 104 105 106 107 108

• NOTE: b and *p are same, when p is pointing to b


void*
• In low-level code, we occasionally need to store or pass along an
address of a memory location without actually knowing what type
of object is stored there.
– A void* is used for that, read as ‘pointer to an object of unknown type’.
• A pointer to any type of object can be assigned to a variable of type
void*
int* ip;
void* vp = ip; // allowed

• A void* can be assigned to another void*


nullptr keyword
• The literal nullptr represents the null pointer, which is a pointer that
does not point to an object.
• It can be assigned to any pointer type, but not to other built-in
type.
int* ip = nullptr; //instead of assigning 0
double* dp = nullptr;
int p = nullptr; // Error: p is not a pointer.

• Using nullptr makes code more readable and avoids potential


confusion when a function is overloaded to accept either a pointer
or an integer (using nullptr instead of 0)
Example on Pointers - 1

Address Content
1000
1001
1002
1003
1004 i= 5
1005
1006
1007
1008 p = 1004
1009
1010
Example on Pointers - 1

Address Content
1000
1001
1002
1003
1004 i= 5
1005
1006
1007
1008 p = 1004
1009
1010
Example on Pointers - 2

Address Content
1000
1001 i = 10
int i = 10; 1002
1003
int *ip;
1004 ✓
double d = 3.14; 1005 ip = 1001
double *dp; 1006 X X
ip = &i; //Valid 1007
ip = &d; //Not valid 1008 d = 3.14
dp = &d; //Valid
1009

1010
dp = &i; //Not valid 1011 dp = 1008
1012
1013
1014
To find
• i=?&j=?
void f(int *p, int *q){
p = q;
*p = 2;
}
int i = 0, j = 1;
int main(){
f(&i, &j);
cout << i <<“ “<< j <<endl;
return 0;
}
To find
• i=0&j=2
void f(int *p, int *q){
p = q;
*p = 2;
}
int i = 0, j = 1;
int main(){
f(&i, &j);
cout << i <<“ “<< j <<endl;
return 0;
}
Type-casting
Pointers and Arrays
• Arrays and Pointers are strongly related.
– Whatever possible with arrays, also possible with pointers
– Array name itself is like a pointer
– Ex: int a[5];

100 101 102 103 104 105 106 107 108 109 110 111 112 113

… … … …

a[0] a[1] a[2] a[3] a[4]


– Array name a is actually a mnemonic [other way of representing the address]
• Array name is not a variable.
• No memory space is allocated to the array name. It cannot be used to store any value.
– Use of array name a is as good as standing at the address 102
• a --> address 102
• *a --> a[0] *(a+2) ≠ a[1]
Address arithmetic
• Addition will be scaled addition.
– If an array represents elements of int, the addition will be scaled.
– Ex: int a[5]; //Assume sizeof(int) is 2 bytes
100 101 102 103 104 105 106 107 108 109 110 111 112 113

… … … …

a[0] a[1] a[2] a[3] a[4]


• If a represents the address 102, a+1 represents 104, a+2 represents 106,…
• a+i (or) a[i] --> will be converted internally to a + (i * sizeof(int))
– If a is pointing to the element a[0], then a+i will be pointing to ith element after a[0] ith
element from the starting point of an array. a[2] -> user friendly
*(a+2) = a[2] *(a+2) -> machine friendly
Pointers and Arrays
• A pointer kind of representation is machine friendly.
Address of a[2]
(a+2) = &a[2]
– &a[2] vs. &(a[2]) --> Both are same. Array subscript operation has higher
precedence over unary operator
• Ex:
100 101 102 103 104 105 106 107 108 109 110 111 112 113
int *p; … … … …
int a[5];
p = &a[0];
a[0] a[1] a[2] a[3] a[4]

102
p
Pointers and Arrays
• A pointer kind of representation is machine friendly.
Address of a[2]
(a+2) = &a[2]
– &a[2] vs. &(a[2]) --> Both are same. Array subscript operation has higher
precedence over unary operator
• Ex:
100 101 102 103 104 105 106 107 108 109 110 111 112 113
int *p; … … … …
int a[5];
p = &a[0];
X a[0] a[1] a[2] a[3] a[4]

104
p++; p
How does compiler resolve that p++; should add 2 bytes?
int *p;
Parenthesis essential? No Details
Pointer p can be used as if it is an array

• *(p+3) = a[3] = p[3] = *(a+1)


• p + 3 = a + 3 = &a[3] = &p[3]

100 101 102 103 104 105 106 107 108 109 110 111 112 113
int *p; … … … …
int a[5];
p = &a[0]; X
a[0] a[1] a[2] a[3] a[4]

108
p++; p

p = p + 2;
Difference between Pointer and Array
• (i) A pointer is a variable, but array name is a mnemonic to the
starting address
• (ii) Operations on array and pointer variable.
– int a[5];
• a = a + 1;
• a++; Invalid
• a = p;
– int *p;
• p = p + 1;
• p++; Perfectly valid
• p = a;
Pointer Arithmetic (or) Address Arithmetic
• Valid pointer operations:
– (i) Assignment of pointers of the same type.
• If they are not of same type, we should go through typecasting.
– (ii) Adding or subtracting a pointer and an integer.
– (iii) Subtracting or comparing 2 pointers to members of the same
array.
– (iv) Assigning or comparing to 0.
(i) Assignment of pointers of the same type
• Ex 1: int *p, *q;
– p = q;
– q = p; Legal assignment
• Ex 2: int *p, a[];
– a could be treated as the pointer to integer --> p = a; //allowed
– a = p; Illegal --> a is a mnemonic, not the variable
• If we try to assign a pointer of different type, then we have to do
type casting.
– Ex: char *c;
• c = p;
Not allowed
• p = c;
typecasting
• Ex: int *p, *q;
char *c;
• Address of p, q, c are of same type and size, whether it is a pointer
to char or int.
– Still, assigning to a pointer variable has a problem.
– When we try to increment/decrement, compiler might get confused about
the scaling.
• p = p + 1; --> p = p + i * sizeof(int)
• c = c + 1; --> c = c + i * sizeof(char)
– Assigning one to another will require some casting.
• p = (int*) c;
Pointer to int Pointer to char
NOTE: In case, c is a void* pointer, typecasting is not required.
typecasting
(ii) Adding or subtracting a pointer and an integer
• We could either add an integer or subtract an integer.
100 101 102 103 104 105 106 107 108 109 110 111 112 113

Ex: … … … …
int *p;
int a[5];
p = &a[1]; a[0] a[1] a[2] a[3] a[4]
p 104

– If we do p+1, p will be pointing to 106.


– If we do p-1, p will be pointing to 102.
• NOTE:
– Ensure that you never go out of the bounds. It leads to getting ERROR.
– We cannot add to a pointer, anything which is not an int i.e. float/long.
– We cannot multiply a pointer and an int.
(iii) Subtracting or comparing 2 pointers to members
of the same array
100 101 102 103 104 105 106 107 108 109 110 111 112 113
Ex: a … … … …
int *p;
int a[5]; a[0] a[1] a[2] a[3] a[4]
p = &a[0];
q = &a[3]; p 102 q 108

• If both p and q are pointing to the same array, we can compare


them.
– if (p < q) --> will return true only when p points to the element which
is to the left of q.
(iii) Subtracting or comparing 2 pointers to members
of the same array
100 101 102 103 104 105 106 107 108 109 110 111 112 113
Ex: a … … … …
int *p;
int a[5]; a[0] a[1] a[2] a[3] a[4]
p = &a[0];
q = &a[3]; p 102 q 108

• If both p and q are pointing to the same array, we can even do


subtraction of 2 pointers.
– (q – p) --> will return ‘how many elements are there between p and q’
– (q – p) here is (108 – 102) = 6
• It will be divided by 2 [i.e. sizeof(int)] to scale down w.r.t int datatype.
(iii) Subtracting or comparing 2 pointers to members
of the same array
• NOTE:
– Subtracting or comparing 2 pointers provided that both are pointing
to the same array.
– What is not allowed?
• We cannot add/multiply/divide 2 pointers,
• We cannot do left shift, right shift, etc.
(iv) Assigning or comparing to 0.
• Assigning a value to a pointer is not meaningful. Ex:
• Assigning a 0 is allowed. int *p;
int a[5];
– if (p == 0) --> This is valid p = 0;
– In C/C++, NULL is defined as 0.
• #define NULL 0
• It is better that you use NULL. It is meaningful compared to assigning 0.
• NOTE:
– There will be no element which is at address 0.
• Therefore, we can assign or compare with 0.
– 0 is used for Error case i.e. to test whether a pointer is valid or not.
Beware while returning a pointer from a function
Pointers & Multi-dimensional arrays
• int matrix[2][5] = {{1,2,3,4,5}, {6,7,8,9,10}};
– matrix + 1?
• 100 + 1 = 105. 5 integer block is skipped i.e. we will skip 20 bytes of memory.
– matrix is a 2D array in which each element is basically an array of 5 integers.
• Assigning 2D into a pointer …
– int (*pmatrix)[5] = matrix; 100
100
1
101
2
102
3
103
4
104
5
matrix[0]
• pmatrix is a pointer to an array of size 5, each of which is an int. 105 106 107 108 109
– We cannot assign matrix to an int*. 105 6 7 8 9 10
matrix[1]
• When passing 1D array into a function 110 110 111 112 113 114

– void fun(int a[], int rows){…} (or) void fun(int* a, int rows){…} 115 115 116 117 118 119

• When passing 2D array into a function 120 120 121 122 123 124

– void fun(int arr[][5], int rows){…} (or) void fun(int (*arr)[5], int rows){…} 125 125 126 127 128 129
– We have to mention that how many columns are there in every row of the 2D 130
array. Because, that will be the size of 1 element of this 2D array.
– int arr[][5] means arr is an array of some number of elements, each element is an
array of size 5.
• void fun(int (*arr)[2][4], int rows){…}
– arr is a pointer to a 2D array of 2x4 elements each of type int.
Pointers & 2D array - Example

10
104

109
5
103

108
4

9
102

107
3

8
*(pmatrix+1)+1

101

106
2

7
100

105
1

6
*(*(pmatrix+1)+1)
matrix

*pmatrix 100
Pointers & Multi-dimensional arrays
• 3D array
– char arr[3][4][6] …
• 3 2-D array, each of which is of 7 rows 6 columns.
100 100 101 102 103 104 105
• arr points to a memory location which is a 2D array of size 4x6.
• arr + 1 will point to the 2nd 2D array of size 4x6 i.e. 124. 106 106 107 108 109 110 111
arr[0]
– We skip 24 integers. 112 112 113 114 115 116 117

• char (*p)[3][4][6] = &arr; 118 118 119 120 121 122 123

– p is a pointer to a 3D array of 3x4x6 elements each are of 124 124 125 126 127 128 129

type char. 130 130 131 132 133 134 135


arr[1]
• cout << (&arr + 1) - &arr; //pointer arithmetic 136 136 137 138 139 140 141

– This subtraction will result in 1. 142 142 143 144 145 146 147

– This says that we have skipped 1 block of 4x6 integers. 148 148 149 150 151 152 153

• cout << (char*)(&arr + 1) – (char*) &arr; 154 154 155 156 157 158 159
arr[2]
160
– This subtraction will result in 24 * sizeof(char) = 24. 160 161 162 163 164 165

166 166 167 168 169 170 171


– When we typecast array into a char pointer i.e. char*, arr is
no longer a type of 2D array. 172 172 173 174 175 176 177

• arr will behave like a char* 178 178 179 180 181 182 183

• 124 – 100 = 24 i.e. these many characters lies between these 2 184
addresses.
Pointer to Function
• Like a (data) object, the code generated for a function body is
placed in memory somewhere, so it has an address.
• We can have a pointer to a function just as we can have a pointer
to an object.
• The pointer obtained by taking the address of a function can then
be used to call the function.
• Defining pointer to function: void (*foo)(int);

– foo is a pointer to a function taking one argument, an integer, and that


returns void.
Pointer to Function
void* (*foo)(int*);

• How we can read it? Read inside-out


– The innermost element of the expression is *foo, and otherwise, it
looks like a normal function declaration.
– foo is a pointer to a function that returns a void* and takes an int*.
– void* can handle any type of pointer.
Initializing Pointer to Function
• To initialize a function pointer, you must give it the address of a
function in your program
void fun(int x){
cout<< x << endl;
}
void (*ptr)(int);
ptr = &fun; (or) ptr = fun;
You can skip ampersand & here.
Using Pointer to Function
• To call the function pointed to by a function pointer, you treat
the function pointer as though it were the name of the
function you wish to call.
void fun(int x){
cout<< x << endl;
}

void (*ptr)(int);
ptr = &fun; (or) ptr = fun;
ptr(5); (or) (*ptr)(5);
You can skip asterisks * here.
Practice Question
• Displaying the array content in reverse order.

You might also like