POINTER in C++

Page Contents:

1.What is the definition of Pointer?
2.Pointer Variable Declarations and Initialization
3.Pointer Operators
4.Calling Function by Reference
5.Using const with Pointers
6.Nonconstant Pointer to Nonconstant Data
7.Nonconstant Pointer to Constant Data
8.Constant Pointer to Nonconstant Data
9.Constant Pointer to Constant Data
10.Pointer Expressions and Pointer Arithmetic

What is the definition of Pointer?

POINTER in C++
POINTER in C++

The pointer is among C++’s most difficult capabilities to master. Pointer enables programs to simulate pass-by-reference and to create and manipulate dynamic data structures(i.e. data structures that can grow and shrink), such as linked lists, queues, stacks, and trees.

Pointer Variable Declarations and Initialization

Pointer variables contain memory addresses as their values. Normally, a variable directly contains a specific value. A pointer, on the other hand, contains the address of a variable that contains a specific value. In this sense, a variable name directly references a value and a pointer indirectly references a value. Referencing a value through a pointer is often called indirection.

Pointers, like any other variables, must be declared before they can be used. For example, the declaration

int *countPtr,count;

declares the variable countPtr to be of type int* (i.e. a pointer to an int value) and is read, “countPtr is a pointer to int” or “countPtr points to an object of type int.” Also, the variable count in the preceding declaration is declared to be an int, not a pointer to an int. The * in the declaration applies only to countPtr.Each variable is declared as a pointer that must be preceded by an asterisk(i.e. *). For example, the declaration

double *xPtr,*yPtr;

indicates that both xPtr and yPtr are pointers to double values.When * appears in a declaration, it is not an operator; rather, it indicates that the variable being declared is a pointer. Pointers can be declared to point to object of any data type.

Error comes when you do this:

Assuming that the * used to declare a pointer distributes to all variable names in a declaration’s comma-separated list of variables can lead to errors. Each pointer must be declared with the * prefixed to the name.

Dereferencing a pointer that has not been properly initialized or that has not been assigned to point to a specific location in memory could cause a fatal execution-time error, or it could accidentally modify important data and allow the program to run to completion, possibly with incorrect results.

An attempt to dereference a variable that is not a pointer is a syntax error

Dereferencing a 0 pointer is normally a fatal execution-time error.

The pointer should be initialized either when they are declared or in an assignment statement. A pointer may be initialized to 0, NULL or an address. A pointer with the value 0 or NULL points to nothing. Symbolic constant NULL is defined in the header file <iostream> (and in several other standard library headers files) to represent the value 0. Initializing a

Directly and indirectly, referencing a variable
Directly and indirectly, referencing a variable

Pointer to NULL is equivalent to initializing a pointer to 0 but in C++, 0 is used by convention. When 0 is assigned, it is converted to a pointer of the appropriate type. The value 0 is the only integer value that can be assigned directly to a pointer variable without casting the integer to a pointer type first.

Pointer Operators

The address operator (&) is a unary operator that returns the memory address of its operands. For example, assuming the declarations

int y=5;
int *yPtr;

assigns the address of the variable y to a pointer variable yPtr.Then the variable yPtr is said to “point to” y. Now, yPtr indirectly references variable y‘s value. Not that the & in the preceding assignment statement is not the same as the & in a reference variable declaration, which is always preceded by a data-type name.

pointer pointing to a variable in memory
A pointer pointing to a variable in memory

The above figure shows a schematic representation of memory after the preceding assignment. In the figure, we show the “pointing relationship” by drawing an arrow from the box that represents the pointer yPtr in memory to the box that represents the variable y in memory.

Pointer in memory
Pointer in memory

The above figure shows another representation of the pointer in memory, assuming that integer variable y is stored at a location 600000 and that pointer variable yPtr is stored at a location 500000. The operand of the address operator must be an lvalue(i.e, something to which a value can be assigned, such as a variable name); the address operator cannot be applied to constants or to expressions that do not result in references

The * operator commonly referred to as the indirection operator or dereferencing operator, returns a synonym(i.e an alias or a nickname) for the object to which its pointer operand points.

1. Write a Program using the & and * operators

#include<iostream>
using std::cout;
using std::endl;
int main()
{
int a;
int *aPtr;
a=7;
aPtr=&a;
cout<<"The address of a is"<<&a
    <<"\nThe value of aPtr is"<<aPtr;
cout<<"\n\nThe value of a is"<<a
    <<"\nThe value of *aPtr is"<<*aPtr;
cout<<"\n\nShowing that * and & are inverse of"     
    <<"each other. \n& *aPtr="<<&*aPtr
    <<"\n*&aPtr="<<*&aPtr<<endl;

return 0;
}

OUTPUT:

The address of a is0x7ffe152a5334
The value of aPtr is0x7ffe152a5334
The value of a is7
The value of *aPtr is7
Showing that * and & are inverse ofeach other.
& *aPtr=0x7ffe152a5334
*&aPtr=0x7ffe152a5334      

Calling Function by Reference

There are three ways in C++ to pass arguments to the function pass-by-value, pass-by-reference with reference arguments and pass-by-reference with a pointer arguments

  • An argument can be passed to a function using reference arguments
  • Reference arguments also enable programs to pass large data objects to a function and avoid the overhead of passing the objects by value
  • Pointers, like references, also can be used to modify one or more variables in the caller or to pass a pointer to large data objects to avoid the overhead of passing the objects by value
  • In C++, programmers can use pointers and the indirection operator to simulate pass-by-reference
  • When calling a function with arguments that should be modified, the addresses of the arguments are passed
  • This is normally accomplished by applying the address operator(&) to the name of the variable whose value will be modified

1. Write a Program to Cube a variable using pass-by-value

#include<iostream>
 using std::cout;
 using std::endl;
 int cubeByValue(int);
 int main()
 {
  int number=5;
  cout<<"The original value of number is"<<number;
  number=cubeByValue(number);
  cout<<"\nThe new value of number is"<<number<<endl;
  return 0;
 }
 int cubeByValue(int n)
 {
 return n*n*n;
 }

OUTPUT:

The original value of number is 5
The new value of number is 125

2. Write A Program To Cube A Variable Using Pass-By-Reference

#include<iostream>
using std::cout;
using std::endl;
void cubeByReference(int*);
int main()
{
int number=5;
cout<<"The original value of number is"<<number;
cubeByReference(&number);
cout<<"\nThe new value of number is"<<number<<endl;
return 0;
}
void cubeByReference(int *nPtr)
{
*nPtr=*nPtr * *nPtr * *nPtr;
}

OUTPUT:

The original value of number is 5
The new value of number is 125

Using const with Pointers

The const qualifier enables the programmer to inform the compiler that the value of a particular variable should not be modified.

The const qualifier can be used to enforce the principle of least privilege. Using the principle of least privilege to properly design software can greatly reduce debugging time and improper side effects and make a program easier to modify and maintain

Consider a function that takes a single-subscripted array and its size as arguments and subsequently prints the array. Such a function should loop through the array and output each array element individually. the size of the array is used in the function body to determine the highest subscript of the array so the loop can terminate when the printing completes. The size of the array does not change in the function body, so it should be declared const. Of course, because the array is only being printed, it, too, should be declared const.

If a value does not(or should not) change in the body of a function to which it is passed, the parameter should be declared const to ensure that it is not accidentally modified

Only one value can be returned to the caller when pass-by-value is used. To modify multiple values in a calling function, several arguments can be passed by reference

Before using a function, check its function prototype to determine the parameters that it can modify

Nonconstant Pointer to Nonconstant Data

The highest access is granted by a nonconstant pointer to nonconstant data-the data can be modified through the dereferenced pointer and the pointer can be modified to point to other data. Declarations for the nonconstant pointers to nonconstant data do not include const. Such a pointer can be used to receive a string in a function that changes the pointer value to the process and possibly modify each character in the string.

Converting lowercase letters to uppercase letters using a non-constant pointer to non-constant data.

#include<iostream>
using std::cout;
using std::endl;
#include<cctype>
void convertToUppercase(char *);
int main()
{
char phrase[]="characters and $32.9B";
cout<<"The phrase before conversion is:"<<phrase;
convertToUppercase(phrase);
cout<<"\nThe phrase after conversion is:"
    <<phrase<<endl;
return 0;
}
void convertToUppercase(char *aPtr)
{
while(*sPtr!='\0')
{
if(islower(*sPtr))
*sPtr=toupper(*sPtr);
++sPtr;
}
}

OUTPUT:

The phrase before conversion is:characters and $32.98
The phrase after conversion is:CHARACTERS AND  $32.98

Nonconstant Pointer to Constant Data

A nonconstant pointer to constant data is a pointer that can be modified to point to any data item of the appropriate type, but the data to which it points cannot be modified through that pointer. Such a pointer might be used to receive an array argument to a function that will process each element of the array but should not be allowed to modify the data

Write a Program to Print a string one character at a time using a non-constant pointer to the constant data

#include<iostream>
using std::cout;
using std::endl;
void printChatracters(const char*);
int main()
{
char phrase[]="print characters of a string";
cout<<"The string is:\n";
printCharacters(phrase);
cout<<endl;
return 0;
}
void printCharacters(const char *sPtr)
{
for(; *sPtr!='\0';sPtr++)
cout<<*sPtr;
}

OUTPUT:

The string is:
print characters of a string

Constant Pointer to Nonconstant Data

A constant pointer to nonconstant data is a pointer that always points to the same memory location; the data at the location can be modified through the pointer. This is the default for an array name. An array name is a constant pointer to the beginning of the array. All data in the array can be accessed and changed by using the array name and array sub-scripting. A constant pointer to nonconstant data can be used to receive an array as an argument to a function that accesses array elements using an array subscript notation. Pointers that are declared const must be initialized when they are declared. (If the pointer is a function parameter, it is initialized with aq pointer which is passed to the function.)

#include<iostream>
int main()
{
int x,y;
int *const ptr=&x;
*ptr=7;
ptr=&y;
return 0;
}

OUTPUT:

error will come that ptr=&y;
So here we are attempting to modify a constant pointer to nonconstant data
while ptr is const; cannot assign new address

Error comes when you do this:

Not initializing a pointer that is declared const is syntax error

Constant Pointer to Constant Data

The least amount of access privilege is granted by a constant pointer to constant data. Such a pointer always points to the same memory location and the data at that memory location cannot be modified using the pointer. This is how an array should be passed to a function that only reads the array, using array subscript notation, and does not modify the array.

Write a Program attempting to modify a constant pointer to constant data.

#include<iostream>
using std::cout;
using std::endl;
int main()
{
int x=5,y;
const int *const ptr=&x;
cout<<*ptr<<endl;
*ptr=7;
ptr=&y;
return 0;
}

OUTPUT:

error: 
*ptr=7; //error:*ptr is const; cannot assign a new value 
ptr=&y; //error:ptr is const; cannot assign new address
So here, we are attempting to modify a constant pointer to constant data

Now Let’s take an example for Bubble Sort Using Pass-by-Reference

#include<iostream>
using std::cout;
using std::endl;
#include<iomanip>
using std::setw;
void bubbleSort(int *,const int);
void swap(int *const,int *const);
int main()
{
const int arraySize=10;
int a[arraySize]={2,6,}
cout<<"Data items in original order\n";
for(int i=0;i<arraySize;i++)
cout<<setw(4)<<a[i];
bubbleSort(a,arraySize);
cout<<"\nData items in ascending order\n";
for(int j=0;j<arraySize;j++)
cout<<setw(4)<<a[j];
cout<<endl;
return 0;
}
void bubbleSort(int *array,const int size)
{
for(int pass=0;pass<size-1;pass++)
for(int k=0;k<size-1;k++)
if(array[k]>array[k+1])
swap(&array[k],&array[k+1]);
}
void swap(int *const element1Ptr,int *const element2Ptr)
{
int hold=+element1Ptr;
*element1Ptr=*element2Ptr;
*element2Ptr=hold;
}

OUTPUT:

Data items in original order
2 6 4 8 10 12 89 68 45 37
Data items in ascending order
2 4 6 8 10 12 37 45 68 89

Pointer Expressions and Pointer Arithmetic

Pointers are valid operands in arithmetic expressions, assignment expressions, and comparison expressions. However, not all the operators normally used in these expressions are valid with pointer variables. Several arithmetic operations may be performed on pointers. A pointer may be incremented(++) or decremented(), an integer may be added to a pointer(+ or +=), an integer may be subtracted from a pointer (or -=) or one pointer may be subtracted from another.

Pointer Expression and pointer arithmetic
Array v and a pointer variable vPtr that points to v

Now assuming that array int v[5] has been declared and that its first element is at location 1000 in memory. Assume that pointer vPtr has been initialized to point to v[0]

vPtr=v;
vPtr=&v[0];

Pointer vPtr after the pointer arithmetic
Pointer vPtr after the pointer arithmetic

In conventional arithmetic, the addition of 1000+2 yields a value of 1002. This is normally not the case with pointer arithmetic. When an integer is added to, or subtracted from, a pointer, the pointer is not simply incremented or decremented by that integer, but by that integer times the size of the object to which the pointer refers. The number of bytes depends on the object’s data type. For example:

vPtr +=2;

would produce 1008(1000+2*4), assuming that an int is stored in four bytes of memory. If an integer is stored in two bytes of memory, then the preceding calculation would result in memory location 1004(1000+2*2). If the array were of a different data type, the preceding statement would increment the pointer by twice the number of bytes it takes to store an object of that data type. When performing pointer arithmetic on a character array, the result will be consistent with regular arithmetic, because each character is one byte long

Error comes when you do this

Using pointer arithmetic on a pointer that does not refer to an array of values is a logic error

Assigning a pointer of one type to a pointer of another(other than void*) without casting the first pointer to the type of the second pointer is a syntax error

All operations on a void* pointer are syntax errors, except comparing void* pointers with other pointers, casting void* pointers to valid pointer types and assigning addresses to void* pointer

Subtracting or comparing two pointers that do not refer to elements of the same array is logic error

Using pointer arithmetic to increment or decrement a pointer such that the pointer refers to an element past the end of the array or before the beginning of the array is normally a logic error

Translate »