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
* 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.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;
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; }
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; }
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; }
usha@dell:~/Desktop$ ./a.out
n1 in function20
n2 in function24
a in main20
b in main24
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
Post a Comment