top of page

Arrays

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

 

To define an array, you have to specify, type of array, name, and size.

e.g.

int arr[10];//definition
arr[0]=19;
arr[1]=2;
float arr2[5];//definition

In the first statement, we are defining an array called arr of 10 integers.

 

In the next lines, we are setting the 0th element of the array to 10 and in the next line, we are setting the first element to 2.

 

The array elements which are not assigned a value will have garbage, similar to other variables.

In the last line, we are defining an array arr2 of two floating-point numbers.

Array initialization

An array can be initialized during its definition by providing values in braces. Remember that initialization should be done along with the definition.


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

 

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 the size of the array is not provided, it is taken as the total number of values provided which is  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,  the rest of the values are initialized with 0.

 

A partially initialized array is filled with 0s for the remaining elements.

Let us look at some wrong initializers.


int arr[3]=[1,2,3];//have to use braces
char a[3]={65,66,67};//no error
float m[5];

m={1.1,2.2,3.3,4,5};//initialization should be with definition


The first example is wrong because we are using square brackets instead of braces. The 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'

The third example is wrong because we have already defined array m in the 4th line and then trying to initialize in the 5th line. The array can not be initialized after it has been defined.

Exercises

  1. Write a program to read 10 numbers and find their sum using an array

  2. Write a program to print the elements of an array in reverse order.

  3. Write a program to find the largest element of an array.

  4. Write a program to find the second largest element of an array.


Next, let us move on to pointers. If you are familiar with C, you have hated pointers. Unless you are a pro in C, of course.

In C++ we do have pointers. But we also have something better. Which is called a 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? We will learn about references a little bit later.

First, let us revise/understand pointers.

Pointers

A pointer is a variable that stores the address of another variable.

 

Pointers are useful for handling arrays, strings, etc. They also help in communicating with functions, data structures, and control tables.


If a is a variable and &a is the address of a.

 

If we store &a in a variable called ptr, ptr is a pointer to a.

A pointer has to be defined like an ordinary variable but with a symbol * before its name.

 

The definition of a pointer uses its name, the data type of pointee, and the symbol *.


int *ptr;
float *ptr2;

 

The first-line defines a variable ptr, which is a pointer to an integer. The second line defines ptr2 which is a pointer to a float.

 

int *  together here is data type here which is integer pointer.

 

Pointers must be assigned to address of another variable using & operator.

 

Uninitialized pointers may crash the program.

Let us look at another example.

int *ptr;

int m=5;

ptr = &m;

cout<<*ptr;

Here we are defining ptr as integer pointer and assigning it to address of integer m. Then we are printing *ptr  -  value at address ptr. This will print variable m.

 

* used in lines 4   in the above example is not multiplication operator, but it is dereferencing or indirection operator. It says value at the address.

 

When we say cout<<*ptr , we say to display the value at the address stored in ptr.

 

Let us consider that m is stored in address 8130, then ptr stores 8130. 

Address        Contents         Variable

0x8130          0x00000005      m

0x8134           0x00008130      ptr

 

Pointer Indirection (*)

 

* operator is called indirection operator or dereference operator. It gives us the value stored in the address given by the 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

 
In the above example, cout prints *ptr. So instead of printing the 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 which is  1.7.


& (address of) and * (indirection) operators are complementary to each other.
 

Let us look at another example.

                     int a = 10;

         int *ptr = &a;

         *ptr = 33;/*now a is 33*/

Here we have changed the value of "a" indirectly using the pointer.

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

Arrays and pointers

 

An array name can be treated as pointer to its first element. Array elements can be accesed using pointers easily.


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]

In the above example, arr is addres of 0th element of array. So, when we set *arr to 100, we are setting arr[0] to 100.

The variable ptr points to 0th element of array arr. Now *ptr will be 0th element of array. *(ptr+1) will be first element of array and so on.

 

In fact, when an array is passed as a parameter to a function, the function treats it as a pointer to 0th element.

Pointer arithmetic


Pointers are basically integers. Does that mean we can perform arithmetic operations on pointers?

 

Add numbers to them, subtract, multiply? Yes and no.

 

The only operations permitted on pointers are:

  • Pointers can be incremented or 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++;


In the above example, we are incrementing the pointer. If a is stored in location 1000 (in practice, the addresses are written in hex notation, and local variable addresses will be quite large numbers. But let us take1000 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 the size of the pointee. If  size of int is 8 then ptr++ will increment it by 8. So ptr will be 1008

The reason for this is, once a pointer is incremented it will point to the next variable of the same type - in this case, the next integer. As an integer needs 8 bytes, the next integer will be at 1008. So ptr++ makes ptr as 1008.

 

If a float pointer is incremented, it gets incremented by 4. If a character pointer is incremented, it gets added by 1 and so on.

The same thing happens if we use the decrement operator with pointers and or we add an integer to a pointer.


int ptr1 = &a;

ptr--;//ptr will be ptr-8

 

Subtracting two pointers

 

When we subtract two ponters, we get the distance between them.

 

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.  Correct?

 

Wrong.

It will be 500/sizeof(int)

References


As mentioned earlier, pointers can be dangerous. They are messy too. But they are quite useful in function calls, linked lists, etc.

In C++, the parameters to a function are sent using call by value mechanism.  This means parameters are copies of actual arguments.  If parameters are modified inside the function, the caller function will not see these modifications.

The only way of sending a value back from a function will be using a return statement. But a function can return only one value. What if we need to return two values or three?

If a function has to change more than one value, you will have to use pointer parameters. ( A typical example is a function to swap two variables. ) 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 the actual argument. So if we modify a reference parameter to a function, its value is changed in the caller function. Just like a pointer.

Definition of a reference


A reference is defined with type, its name and & symbol. & indicates it is a reference.

 

int &m = b;

 

int & in the above example means a reference to an integer.

The second part of the statement initializes this reference variable with b. Now m is a reference to b or alias to b.

 

Any changes to m will change b and vice versa.
 

 Remember that a reference should always be initialized with a variable it is referring to.

int &m = b;

cout<<m;

m=99;//both m and b are 99

Rules for a reference variable

  • A reference can not be pointed to another variable after initialization, unlike a pointer.

  • 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.  

 

Let us look at a complete program now.

 

Here, the variable b is a reference variable.  b is a reference to a or it is an alias to a.

 

In the 3rd statement, 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.

References and functions


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.
 

What will be the output of this program?


$./a.out
n1 in function20
n2 in function24
a in main10
b in main12

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

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


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

What will be the output of the program after these changes?

$ ./a.out
n1 in function20
n2 in function24
a in main20
b in main24

 

Now both a and b are changed

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

There are also situations where we use reference parameters - but we don't want the function to modify the arguments. If we have functions that have large objects as parameters, we can make them constant reference parameters, to avoid the overhead of copying them, at the same time protecting them from accidental modification.

Function which returns reference

Even the return type of a function can be of reference type. In such a case, the return values are not copied.

When we have to use such a function

  • The return value must be an lvalue

  • Its  will not go out of scope when the function returns.

Let us understand this with an example.

#include <iostream>
 

int& max(int& x, int& y)
{
  return (x > y) ? x : y;
}

int main()
{
  int a{ 5 };
  int b{ 6 };
  max(a, b) = 7; // sets the greater of a or b to 7
  std::cout << a << b << '\n';
  return 0;
}

In the example, the function max(int &x, int &y) takes two reference parameters and returns the reference to the larger of the two. And main() function assigns 7 to the return of the function. That is, the function returns reference to b in this case and b is assigned to 7. 

You should be careful when using functions which return reference. This variable must exist when the function ends. Which means you can not return a non-static local variable.

Exercises:

  1. Write a function to swap two variables using references.

  2. Write a function to swap two variables using pointer parameters.

  3. Write a function to find the largest of 3 numbers using reference parameters.

rvalue reference

rvalue reference is a new feature introduced in C++11 to help in move semantics.

 

Let us first distinguish between lvalue and rvalue.

 

Lvalue is something which is stored in an address, has a memory location and is not temporary. rvalue is a temporary object, which does not have a value and is not stored at a memory location.

When we say int k = 10;

k is lvalue and 10 is rvalue.

Now references we talked about earlier were lvalue references. They referred to lvalues or constant rvalues.

A reference which refers to rvalue is called rvalue reference. It is defined with &&.

e.g..

    int &&ref1 = 10;//ref1 is an rvalue reference

Please remember that rvalue reference can't bind itself to an lvalue.

e.g.

     int x = 10;

  int &&ref2 = x; //error

bottom of page