C++ Programming/Operators/Pointers
Pointers, Operator *
[edit | edit source]The * operator is used when declaring pointer types but it is also used to get the variable pointed to by a pointer.
Pointers are important data types due to special characteristics. They may be used to indicate a variable without actually creating a variable of that type. Because they can be a difficult concept to understand, some special effort should be spent on understanding the power they give to programmers.
Pointers have a very descriptive name. Pointers variables only store memory addresses, usually the addresses of other variables. Essentially, they point to another variable's memory location, a reserved location on the computer memory. You can use a pointer to pass the location of a variable to a function, this enables the function's pointer to use the variable space, so that it can retrieve or modify its data. You can even have pointers to pointers, and pointers to pointers to pointers and so on and so forth.
Declaring
[edit | edit source]Pointers are declared by adding a * before the variable name in the declaration, as in the following example:
int* x; // pointer to int.
int * y; // pointer to int. (legal, but rarely used)
int *z; // pointer to int.
int*i; // pointer to int. (legal, but rarely used)
Watch out, though, because the * associates to the following declaration only:
int* i, j; // CAUTION! i is pointer to int, j is int.
int *i, *j; // i and j are both pointer to int.
You can also have multiple pointers chained together, as in the following example:
int **i; // Pointer to pointer to int.
int ***i; // Pointer to pointer to pointer to int (rarely used).
Assigning values
[edit | edit source]Everyone gets confused about pointers as assigning values to pointers may be a bit tricky, but if you know the basics, you can proceed more easily. By carefully going through the examples rather than a simple description, try to understand the points as they are presented to you.
- Assigning values to pointers (non-char type)
double vValue = 25.0;// declares and initializes a vValue as type double
double* pValue = &vValue;
cout << *pValue << endl;
The second statement uses "&
" the reference operator and "*"
to tell the compiler this is a pointer variable and assign vValue
variable's address to it. In the last statement, it outputs the value from the vValue
variable by de-referencing the pointer using the "*"
operator.
- Assigning values to pointers (char type)
char pArray[20] = {"Name1"};
char* pValue(pArray);// or 0 in old compilers, nullptr is a part of C++0X
pValue = "Value1";
cout << pValue << endl ;// this will return the Value1;
So as mentioned early, a pointer is a variable which stores the address of another variable, as you need to initialize an array because you can not directly assign values to it. You will need to use pointers directly or a pointer to array in a mixed context, to use pointers alone, examine the next example.
char* pValue("String1");
pValue = "String2";
cout << pValue << endl ;
Remember you can't leave the pointer alone or initialize it as nullptr cause it will case an error. The compiler thinks it is as a memory address holder variable since you didn't point to anything and will try to assign values to it, that will cause an error since it does not point to anywhere.
Dereferencing
[edit | edit source]This is the * operator. It is used to get the variable pointed to by a pointer. It is also used when declaring pointer types.
When you have a pointer, you need some way to access the memory that it points to. When it is put in front of a pointer, it gives the variable pointed to. This is an lvalue, so you can assign values to it, or even initialize a reference from it.
#include <iostream>
int main()
{
int i;
int * p = &i;
i = 3;
std::cout<<*p<<std::endl; // prints "3"
return 0;
}
Since the result of an & operator is a pointer, *&i is valid, though it has absolutely no effect.
Now, when you combine the * operator with classes, you may notice a problem. It has lower precedence than .! See the example:
struct A { int num; };
A a;
int i;
A * p;
p = &a;
a.num = 2;
i = *p.num; // Error! "p" isn't a class, so you can't use "."
i = (*p).num;
The error happens because the compiler looks at p.num first ("." has higher precedence than "*") and because p does not have a member named num the compiler gives you an error. Using grouping symbols to change the precedence gets around this problem.
It would be very time-consuming to have to write (*p).num a lot, especially when you have a lot of classes. Imagine writing (*(*(*(*MyPointer).Member).SubMember).Value).WhatIWant! As a result, a special operator, ->, exists. Instead of (*p).num, you can write p->num, which is completely identical for all purposes. Now you can write MyPointer->Member->SubMember->Value->WhatIWant. It's a lot easier on the brain!
Null pointer
[edit | edit source]The null pointer is a special status of pointers. It means that the pointer points to absolutely nothing. It is an error to attempt to dereference (using the * or -> operators) a null pointer. A null pointer can be referred to using the constant zero, as in the following example:
int i;
int *p;
p = 0; //Null pointer.
p = &i; //Not the null pointer.
Note that you can't assign a pointer to an integer, even if it's zero. It has to be the constant. The following code is an error:
int i = 0;
int *p = i; //Error: 0 only evaluates to null if it's a pointer
There is an old macro, defined in the standard library, derived from the C language that inconsistently has evolved into #define NULL ((void *)0), this makes NULL, always equal to a null pointer value (essentially, 0).
Since a null pointer is 0, it will always compare to 0. Like an integer, if you use it in a true/false expression, it will return false if it is the null pointer, and true if it's anything else:
#include <iostream>
void IsNull (int * p)
{
if (p)
std::cout<<"Pointer is not NULL"<<std::endl;
else
std::cout<<"Pointer is NULL"<<std::endl;
}
int main()
{
int * p;
int i;
p = NULL;
IsNull(p);
p = &i;
IsNull(&i);
IsNull(p);
IsNull(NULL);
return 0;
}
This program will output that the pointer is NULL, then that it isn't NULL twice, then again that it is.
Pointers and multidimensional arrays
[edit | edit source]- Pointers and Multidimensional non-Char Arrays
A working knowledge of how to initialize two‑dimensional arrays, assign values to arrays, and return values from arrays is necessary. In depth information about arrays can be found in section 1.4.10.1.1 Arrays. However, when relevant to the understanding of pointers, arrays will be mentioned here, as well.
- The main objects are
- Assign Values to Multidimensional Pointers
- How to use Pointers with Multidimensional Arrays
- Return Values
- Initialize Pointers and Arrays
- How to Arrange Values in them
- Assign Values to Multidimensional Pointers.
In non-Char Type you need to involve arrays with Pointers since Pointers treat char* type to in special way and other type to another way like only refer the address or get the address and get the value by indirect method.
If you declare it like this way:
double (*pDVal)[2] = {{1,2},{1,2}};
It will probably generate an error! Because pointers used in non-Char type only directly, in char types refer the address of another variable by assigning a variable first then you can get its (that assigned variable) value indirectly!
double ArrayVal[5][5] = {
{1,2,3,4,5},
{1,2,3,4,5},
{1,2,3,4,5},
{1,2,3,4,5},
{1,2,3,4,5},
};
double(*pArray)[5] = ArrayVal;
*(*(pArray+0)+0) = 10;
*(*(pArray+0)+1) = 20;
*(*(pArray+0)+2) = 30;
*(*(pArray+0)+3) = 40;
*(*(pArray+0)+4) = 50;
*(*(pArray+1)+0) = 60;
*(*(pArray+1)+1) = 70;
*(*(pArray+1)+2) = 80;
*(*(pArray+1)+3) = 90;
*(*(pArray+1)+4) = 100;
*(*(pArray+2)+0) = 110;
*(*(pArray+2)+1) = 120;
*(*(pArray+2)+2) = 130;
*(*(pArray+2)+3) = 140;
*(*(pArray+2)+4) = 150;
*(*(pArray+3)+0) = 160;
*(*(pArray+3)+1) = 170;
*(*(pArray+3)+2) = 180;
*(*(pArray+3)+3) = 190;
*(*(pArray+3)+4) = 200;
*(*(pArray+4)+0) = 210;
*(*(pArray+4)+1) = 220;
*(*(pArray+4)+2) = 230;
*(*(pArray+4)+3) = 240;
*(*(pArray+4)+4) = 250;
There is another way instead
*(*(pArray+0)+0)
it is
*(pArray[0]+0)
You can use one of them to assign value to Array through the pointer to return values you can use either the appropriate Array or Pointer.
- Pointers and multidimensional char arrays
This is bit hard and even hard to remember so I suggest keep practicing until you get the spirit of Pointers only! You cannot use Pointers + Multidimensional Arrays with Char Type. Only for non-char type.
- Multidimensional pointer with char type
char* pVar[5] = { "Name1" , "Name2" , "Name3", "Name4", "Name5" }
pVar[0] = "XName01";
cout << pVar[0] << endl ; //this will return the XName01 instead Name1 which was replaced with Name1.
here the 5 in the first statement is the number of rows (no columns need to be specified in pointer it is only in Arrays) the next statement assigns another string to position 0 which is the position of first place of first statement. finally return the answer
- Dynamic memory allocation
In your system memory each memory block got an address so whenever you compile the code at the beginning all variable reserve some space in the memory but in Dynamic Memory Allocation it only reserve when it needed it means at execution time of that statement this allocates memory in your free space area(unused space) so it means if there is no space or no contiguous blocks then the compiler will generate and error message
- Dynamic memory allocation and pointer non-char type
This is same as assigning a non-char 1‑dimensional Array to Pointer
double* pVal = new double[5];
//or double* pVal = new double; // this line leaves out the necessary memory allocation
*(pVal+0) = 10;
*(pVal+1) = 20;
*(pVal+2) = 30;
*(pVal+3) = 40;
*(pVal+4) = 50;
cout << *(pVal+0) << endl;
The first statement's Lside (left side) declares a variable and Rside requests a memory allocation to hold 5 elements of type double. The following statements are an example of pointer arithmetic, the addition here advances the pointer's memory location by one whole element. Since pVal points to the base address of the array (i.e. the element at index 0), *(pVal+0) is identical and will return the address of the first element (value 10). Following this logic, *(pVal+1) will point to the next element's memory location where the value 20 will be written to.
The use of () parentheses is required due to priority of operations (see: operator precedence): + < * < (). Otherwise the *pVal operation would be executed first. * is called indirection operator, it de-references the pointer and returns the value stored at the corresponding memory location.
- Dynamic memory allocation and pointer char type
char* pVal = new char;
pVal = "Name1";
cout << pVal << endl;
delete pVal; //this will delete the allocated space
pVal = nullptr //null the pointer
You can see this is the same as static memory declaration, in static declaration it goes:
char* pVal("Name1");
- Dynamic memory allocation and pointer non-char array type
double (*pVal2)[2]= new double[2][2]; //this will add 2x2 memory blocks to type double pointer
*(*(pVal2+0)+0) = 10;
*(*(pVal2+0)+1) = 10;
*(*(pVal2+0)+2) = 10;
*(*(pVal2+0)+3) = 10;
*(*(pVal2+0)+4) = 10;
*(*(pVal2+1)+0) = 10;
*(*(pVal2+1)+1) = 10;
*(*(pVal2+1)+2) = 10;
*(*(pVal2+1)+3) = 10;
*(*(pVal2+1)+4) = 10;
delete [] pVal; //the dimension does not matter; you only need to mention []
pVal = nullptr
Pointers to classes
[edit | edit source]Indirection operator ->
[edit | edit source]This pointer indirection operator is used to access a member of a class pointer.
Member dereferencing operator .*
[edit | edit source]This pointer-to-member dereferencing operator is used to access the variable associated with a specific class instance, given an appropriate pointer.
Member indirection operator ->*
[edit | edit source]This pointer-to-member indirection operator is used to access the variable associated with a class instance pointed to by one pointer, given another pointer-to-member that's appropriate.
Pointers to functions
[edit | edit source]When used to point to functions, pointers can be exceptionally powerful. A call can be made to a function anywhere in the program, knowing only what kinds of parameters it takes. Pointers to functions are used several times in the standard library, and provide a powerful system for other libraries which need to adapt to any sort of user code. This case is examined more in depth in the Functions Section of this book.