Skip to main content

Pointers and reference

Arrays

Array is a group of elements of same type sharing a common name. Array elements are written with name of array and an index.

e.g.
int arr[10];
arr[0]=19;
arr[1]=2;
float arr2[5];

Array initialization

An array can be initialized during its definition by providing values in braces.

int arr[] = {10,22,3};
char charr[4]={'a','2','<','['};
float flarr[10]={1.4};


This type of initialization assigns values provided to array elements. In the first statement, arr[0] is 10, arr[1] is 22 and arr[2] is 3. Since size of array is not provided it is taken as total number of values provided - 3.

In the second example charr[0] is character 'a', charr[1]='2' and so on.
In the third example, flarr has a size of 10, but only one value is provided, so the rest of values are initialized with 0

Let us look at some wrong initializers
int arr[3]=[1,2,3];//not square brackets
char a[3]={65,66,67};//this is correct
float m[5];
m={1.1,2.2,3.3,4,5};//initialization should be with definition

In the first example, we are using square brackets instead of braces. Second example is correct because, character variables can be assigned to integer values. These values are taken as their unicode values. a[0] is taken as character 'A', a[1] is 'B' and a[2] as 'C'

Third example is wrong because we have already defined array m in 4th line and then trying to initialize in 5th line.


If you are already familiar with C, you have hated pointers. Unless you are pro, of course.

In C++ we do have pointers. But we also have something better. Which is called reference. Reference is a better pointer - that is it looks like an ordinary variable but behaves like a pointer. And you don't have to use &, * ->etc. Great! Isn't it?

Let us revise/understand pointers first.

Pointers

 Pointer is a variable which stores the address of another variable. Pointers are useful for handling arrays, strings. They also help in communicating with functions, data structures and control tables. 

If a is a variable and &a is  address of a. If we store &a in ptr, ptr will be a pointer to a.

A pointer has to be defined like an ordinary variable. The definition uses the data type of pointee and the symbol *.

int *ptr;
float *ptr2;

ptr is a pointer to an integer and ptr2  is a pointer to a float. int *  together here is data type here. Pointers are and must be assigned to address of another variable using & operator. Uninitialized pointers will crash the program


int *ptr;
int m=5;
ptr = &m;
cout<<*ptr;
*ptr = 12;
* used here is not multiplication operator, but it means dereferencing. It says value at the address. So when we say cout<<*ptr we say display the value at the address stored in ptr. If let us say m is stored in address 8130, then ptr stores 8130.

Address Contents
0x8130 0x00000005      
0x8134 0x00008130

Pointer Indirection (*)

* operator is called indirection operator or dereference operator. It gives us the value stored in the address stored in pointer.

      float a = 1.7f;//if address of a is 1000
    float *ptr = &a;//ptr will be 1000
    cout<<*ptr;//print a value stored in address 1000
 
cout uses * operator. So instead of printing value of ptr, cout displays value at the address given by ptr - which is a. If a is stored in location 1000, ptr = &a makes ptr to have the value of a's address which is 1000. So *ptr will be a .

& and * operators are complimentary to each other.

  If indirection (*) is used on an uninitialized pointer or pointer with NULL value, program will crash. 

Arrays and pointers

An array can be treated as pointer to its first element. Array accessing can be done using indirection operator. In fact, when an array is passed as a parameter to a function, the function treats it as a pointer.

int arr[3]={11,22,33};
*arr = 100;//arr[0] is set to 100
int m = *arr + 10;//m is 110
int *ptr = arr;//ptr is set to &arr[0]

Pointer arithmetic


Pointers are basically integers. So can we do some arithmetic on pointers. Add numbers to it?
  • Pointers can be incremented and decremented.
  • Integers can be added to pointers and integers can be subtracted from pointers.
  • Two pointers can be subtracted to find the distance between them

No other operations are allowed on pointers.

int *ptr = &a;
ptr++;

Now if a is stored in location 1000 (in practice, the addresses are written in hex notation, and local variable addresses will be large numbers. But let us keep it 1000 for the sake of simplicity)

If a is stored in 1000, ptr will have 1000 as its value. So after ptr++, ptr becomes 1001. Right?

Wrong. Pointer arithmetic is always scaled up by size of pointee. If your machine has a word length of 8 bytes, then size of int is 8 and ptr++ will increment it by 8 - ptr=1008

The reason for this is, once a pointer is incremented it will point to next variable of same type. As integer needs 8 bytes, next variable can be stored at 1008.

Same thing happens in decrement operator and adding an integer to a pointer.

int ptr1 = &a;
int ptr2 = &b;
cout<<ptr2-ptr1;

If a is stored at 1000 and b at 1500 ptr1 is 1000, ptr2 is 1500. So ptr1-ptr2 will be  500? No, it will be 500/size of an integer.

Reference


As mentioned earlier, pointers can be dangerous. They are messy. But they are quite useful. More so in function calls.

In C++ (and in C), the parameters of a function are sent using call by value. Which means parameters are a copy of argument. This means, if parameters are modified in function, caller will not see these modifications. That is the reason many C functions make use of pointer parameters.

But C++ has something better. It has a reference type.

A reference variable is an alias for another variable. A reference parameter is an alias for argument. So if we modify a reference parameter to a function, its value is changed in caller.

Definition of a reference


int &m = b;

The reference definition always uses the symbol ampersand (&)  before variable name. int & means reference to an integer. float & means reference to a float.

Second part of the statement initializes this reference variable with b. Now m is an alias to b. Any change to m will change b and vice versa.

 Remember that a reference should always be initialized.

Using a reference variable


  •  A reference variable can be used as any other variable.
  •  A reference can not be pointed to another variable     
  • A reference variable should always be initialized
  • Reference can not point to NULL unlike pointer
  • A function returning a reference can be assigned to a value.  

int &m = b;
cout<<m;
m=99;//both m and b are 99

Let us look at a complete program now.

#include <iostream>
using namespace std;
int main() {
  int a = 10;
  int &b = a;
  cout<<b<<" "<<a<<endl;
  a = 300;
  b = 12;
}
cout will print 10 and 10 for b and a. a=300 will change both a and b to 300. Similarly when we change b to 12, a also changes to 12.

We will see how to use a reference parameter to a function and a reference return value in the next post.

The discussion would be incomplete unless you see how are references useful as function parameters.

Let us first write an example with a function with 2 ordinary parameters, which tries to double both these parameters.


#include<iostream>
using namespace std;
int db_fn(int n1,int n2)
{
   n1*=2;
   n2*=2;
   cout<<"n1 in function"<<n1<<endl;
   cout<<"n2 in function"<<n2<<endl;
}
int main()
{
   int a = 10,b=12;
   db_fn(a,b);
   cout<<"a in main"<<a<<endl;
   cout<<"b in main"<<b<<endl;
}

What will be the output of this program?

usha@dell:~/Desktop$ ./a.out
n1 in function20
n2 in function24
a in main10
b in main12


We expected  a and b to be 20 and 24. That did not happen. Why?

Because C++ sends values as copies. a and b are copied into n1 and n2. So any changes to n1 and n2 does not change the arguments.


That is the reason, we used pointers in C language. Whenever we needed the multiple arguments to be modified by a function, we sent them as pointers.

But let us now use references as parameters for the same function. 

int db_fn(int & n1,int & n2)
{
   n1*=2;
   n2*=2;
   cout<<"n1 in function"<<n1<<endl;
   cout<<"n2 in function"<<n2<<endl;
}
What will be the output of the program after these changes?
usha@dell:~/Desktop$ ./a.out
n1 in function20
n2 in function24
a in main20
b in main24


Now both a and b are changed. Just like pointer parameters. But the function looks elegant and there is no danger of pointer errors.

So whenever we need a function to modify multiple values in the caller, we can use reference parameters.

There are also situations where we use constant reference parameters - we don't want function to modify the arguments. If we have functions which have large objects as parameters, we can make them constant reference parameters, to avoid the overhead of copying them.

Comments

Popular posts from this blog

Find the error in C++ program

This C++ program is not compiling. What do you think is the error with the program? #include<iostream> using namespace std; int main() {    int arr[10];    arr={1,2,3,4,5,6,7,8};    cout<<"arr[0]="<<arr[0];    return 0; } Is the error due to Not using printf Initialising the array with only 8 elements instead of 10 Initialising array in the next statement instead of in definition. None of the above  Now if you like this question, there are plenty more questions like this and programs and notes in my app Simplified C++. Download the Simplif ied C++   by Hegdeapps now By the way the correct answer is (3)

Abstract class

 If we can not create any objects of a class, then it is called an abstract class. A class is made abstract by adding at least one pure virtual function to it. Pure virtual function A function is said to be a pure virtual function , if it has no definition but has only declaration. Pure virtual function is defined with the keyword virtual and followed by return type, function name and "=0". class Shape { public: virtual void printarea() =0 ; }; int main () { Shape obj1; //error } Here printarea() function of Shape class is a pure virtual function as it has no body. To make a function as pure virtual function, you should use =0 at the end of virtual function declaration Abstract class When a class has at least one pure virtual function, it is incomplete and no objects can be created from that class. Such a class is called an abstract class . In the earlier example class Shape is an abstract class, and objects can not be created from t...

Friends of C++

Friend function Remember that a member which is not public (Private and protected )  can not be accessed from outside the class.  Three are some situations where you may need to access these. Keyword friend  is used for this. A friend functions and classes can access all members of a class.  This concept is controversial. People say friend concept violates data encapsulation  A friend function is a non-member function but still can access all the members of a class including private members. Such a function is declared within class body with the prefix "friend" class Number { int num; public: Number( int m){ /*code*/ } friend void printNum(Number ob); /*friend function*/ }; void printNum (Number obj) { cout << obj.num << "endl" ; } printNum() is not member of class Number . But it can still access all members including private members, because it is a friend. Friend class An object of a friend c...