Skip to content Skip to sidebar Skip to footer

Which Arithmetic Operations Can Be Performed on Pointers?

C++ Programming Language

Pointers, References and Dynamic Memory Resource allotment

Pointers, References and Dynamic Retentivity Allocation are the nigh powerful features in C/C++ language, which allows programmers to directly manipulate memory to efficiently manage the memory - the well-nigh critical and scarce resource in computer - for all-time performance. However, "pointer" is too the nearly complex and difficult feature in C/C++ language.

Pointers are extremely powerful because they allows you to admission addresses and manipulate their contents. Simply they are also extremely circuitous to handle. Using them correctly, they could greatly improve the efficiency and operation. On the other mitt, using them incorrectly could atomic number 82 to many bug, from un-readable and un-maintainable codes, to infamous bugs such as retention leaks and buffer overflow, which may expose your organization to hacking. Many new languages (such every bit Java and C#) remove pointer from their syntax to avert the pitfalls of pointers, by providing automatic retentiveness direction.

Although y'all can write C/C++ programs without using pointers, all the same, it is difficult not to mention arrow in teaching C/C++ language. Pointer is probably not meant for novices and dummies.

Pointer Variables

A computer memory location has an address and holds a content. The accost is a numerical number (often expressed in hexadecimal), which is hard for programmers to use directly. Typically, each accost location holds 8-bit (i.east., 1-byte) of data. It is entirely up to the programmer to interpret the meaning of the data, such every bit integer, real number, characters or strings.

To ease the burden of programming using numerical accost and programmer-interpreted data, early programming languages (such as C) innovate the concept of variables. A variable is a named location that can store a value of a particular type. Instead of numerical addresses, names (or identifiers) are attached to certain addresses. Also, types (such equally int, double, char) are associated with the contents for ease of interpretation of data.

Each address location typically hold 8-flake (i.due east., 1-byte) of data. A 4-byte int value occupies iv memory locations. A 32-chip organization typically uses 32-bit addresses. To store a 32-bit address, 4 retentivity locations are required.

The following diagram illustrate the relationship between computers' retentivity address and content; and variable's proper noun, type and value used past the programmers.

MemoryAddressContent.png

Pointer Variables (or Pointers)

A pointer variable (or pointer in short) is basically the aforementioned equally the other variables, which can store a piece of data. Unlike normal variable which stores a value (such equally an int, a double, a char), a pointer stores a memory address.

Declaring Pointers

Pointers must be declared before they can be used, just like a normal variable. The syntax of declaring a pointer is to place a * in front of the proper name. A pointer is associated with a type (such as int and double) as well.

          blazon          *ptr;              type*          ptr;           type          *          ptr;        

For example,

int * iPtr;                       double * dPtr;        

Take note that you need to identify a * in front of each pointer variable, in other words, * applies only to the proper noun that followed. The * in the declaration statement is not an operator, but indicates that the name followed is a pointer variable. For example,

int *p1, *p2, i;     int* p1, p2, i;      int * p1, * p2, i;        

Naming Convention of Pointers: Include a "p" or "ptr" as prefix or suffix, e.k., iPtr, numberPtr, pNumber, pStudent.

Initializing Pointers via the Accost-Of Operator (&)

When yous declare a pointer variable, its content is not initialized. In other words, information technology contains an address of "somewhere", which is of course not a valid location. This is dangerous! Yous need to initialize a pointer by assigning it a valid address. This is normally washed via the accost-of operator (&).

The address-of operator (&) operates on a variable, and returns the accost of the variable. For example, if number is an int variable, &number returns the address of the variable number.

You can use the accost-of operator to become the address of a variable, and assign the accost to a pointer variable. For case,

int number = 88;      int * pNumber;                 pNumber = &number;             int * pAnother = &number;        

PointerDeclaration.png

As illustrated, the int variable number, starting at address 0x22ccec, contains an int value 88. The expression &number returns the address of the variable number, which is 0x22ccec. This address is then assigned to the pointer variable pNumber, as its initial value.

The address-of operator (&) can only be used on the RHS.

Indirection or Dereferencing Operator (*)

The indirection operator (or dereferencing operator) (*) operates on a arrow, and returns the value stored in the address kept in the pointer variable. For example, if pNumber is an int pointer, *pNumber returns the int value "pointed to" by pNumber.

For example,

int number = 88; int * pNumber = &number;   cout << pNumber<< endl;    cout <<          *pNumber          << endl;  *pNumber          = 99;             cout <<          *pNumber          << endl;  cout << number << endl;        

Take note that pNumber stores a retentiveness address location, whereas *pNumber refers to the value stored in the address kept in the pointer variable, or the value pointed to by the arrow.

As illustrated, a variable (such every bit number) straight references a value, whereas a pointer indirectly references a value through the memory address it stores. Referencing a value indirectly via a arrow is called indirection or dereferencing.

The indirection operator (*) can be used in both the RHS (temp = *pNumber) and the LHS (*pNumber = 99) of an assignment statement.

Accept note that the symbol * has different meaning in a proclamation argument and in an expression. When information technology is used in a declaration (due east.k., int * pNumber), it denotes that the name followed is a pointer variable. Whereas when it is used in a expression (e.g., *pNumber = 99 ; temp << *pNumber;), information technology refers to the value pointed to by the pointer variable.

Pointer has a Blazon Too

A pointer is associated with a type (of the value it points to), which is specified during declaration. A arrow can only agree an address of the declared type; it cannot hold an address of a different type.

int i = 88; double d = 55.66; int * iPtr = &i;     double * dPtr = &d;    iPtr = &d;          // ERROR, cannot concur address of unlike type          dPtr = &i;          // ERROR          iPtr = i;          // Error, arrow holds address of an int, Not int value          int j = 99; iPtr = &j;        
Example
one 2 three 4 5 6 7 8 ix 10 xi 12 xiii 14 15 16 17 18 19 twenty 21 22 23
                    #include <iostream> using namespace std;   int principal() {    int number = 88;        int * pNumber;          pNumber = &number;        cout << pNumber << endl;      cout << &number << endl;      cout << *pNumber << endl;     cout << number << endl;         *pNumber = 99;                cout << pNumber << endl;      cout << &number << endl;      cout << *pNumber << endl;     cout << number << endl;                                       cout << &pNumber << endl;  }

Notes: The address values that you get are unlikely to be the same every bit mine. The Os loads the program in available free memory locations, instead of fixed memory locations.

Uninitialized Pointers

The post-obit code fragment has a serious logical error!

int * iPtr; *iPtr = 55; cout << *iPtr << endl;

The arrow iPtr was declared without initialization, i.e., information technology is pointing to "somewhere" which is of course an invalid retentivity location. The *iPtr = 55 corrupts the value of "somewhere"! You need to initialize a pointer by assigning it a valid address. Most of the compilers does not signal an error or a warning for uninitialized pointer?!

Zilch Pointers

Y'all tin initialize a pointer to 0 or NULL, i.east., it points to nothing. Information technology is called a null pointer. Dereferencing a zilch pointer (*p) causes an STATUS_ACCESS_VIOLATION exception.

int * iPtr = 0;          cout <<          *iPtr          << endl;          // ERROR! STATUS_ACCESS_VIOLATION exception          int * p = Zero;        

Initialize a pointer to null during proclamation is a good software applied science practice.

C++xi introduces a new keyword chosen nullptr to represent null pointer.

Reference Variables

C++ added the so-called reference variables (or references in curt). A reference is an alias, or an alternate name to an existing variable. For instance, suppose you lot brand peter a reference (alias) to paul, you tin refer to the person every bit either peter or paul.

The principal use of references is acting as function formal parameters to support pass-past-reference. In an reference variable is passed into a office, the function works on the original copy (instead of a clone copy in laissez passer-by-value). Changes within the role are reflected exterior the role.

A reference is similar to a arrow. In many cases, a reference can exist used as an alternative to pointer, in detail, for the function parameter.

References (or Aliases) (&)

Recall that C/C++ utilize & to denote the address-of operator in an expression. C++ assigns an additional meaning to & in declaration to declare a reference variable.

The meaning of symbol & is different in an expression and in a declaration. When it is used in an expression, & denotes the accost-of operator, which returns the address of a variable, eastward.g., if number is an int variable, &number returns the accost of the variable number (this has been described in the above section).

Howeve, when & is used in a annunciation (including part formal parameters), information technology is office of the type identifier and is used to declare a reference variable (or reference or allonym or alternating name). It is used to provide another proper noun, or another reference, or alias to an existing variable.

The syntax is as follow:

          blazon          &newName          =          existingName;           blazon&          newName          =          existingName;           type          &          newName          =          existingName;        

It shall be read equally " newName is a reference to exisitngName ", or " newNew is an alias of existingName ". Y'all tin can now refer to the variable equally newName or existingName .

For case,

1 2 3 iv 5 6 7 8 ix 10 xi 12 13 14 15 16 17 18 19 20
                    #include <iostream> using namespace std;   int master() {    int number = 88;                            int & refNumber = number;                                                      cout << number << endl;        cout <<                  refNumber                  << endl;                   refNumber = 99;                cout <<                  refNumber                  << endl;    cout << number << endl;          number = 55;                   cout << number << endl;    cout <<                  refNumber                  << endl;  }

PointerReferencing.png

How References Work?

A reference works every bit a pointer. A reference is alleged as an alias of a variable. It stores the address of the variable, as illustrated:

ReferenceIsAPointer.png

References vs. Pointers

Pointers and references are equivalent, except:

  1. A reference is a name constant for an address. You need to initialize the reference during declaration.
                  int & iRef;            
    Once a reference is established to a variable, you cannot change the reference to reference another variable.
  2. To go the value pointed to by a arrow, you need to apply the dereferencing operator * (e.g., if pNumber is a int arrow, *pNumber returns the value pointed to by pNumber. It is called dereferencing or indirection). To assign an accost of a variable into a pointer, you demand to employ the address-of operator & (e.k., pNumber = &number).
    On the other hand, referencing and dereferencing are done on the references implicitly. For example, if refNumber is a reference (alias) to another int variable, refNumber returns the value of the variable. No explicit dereferencing operator * should be used. Furthermore, to assign an accost of a variable to a reference variable, no accost-of operator & is needed.

For case,

ane 2 iii 4 v vi vii 8 9 10 11 12 xiii xiv 15 16 17 xviii 19 20 21 22 23 24 25 26 27 28 29 30 31
                    #include <iostream> using namespace std;   int main() {    int number1 = 88, number2 = 22;         int * pNumber1 = &number1;     *pNumber1 = 99;                cout << *pNumber1 << endl;     cout << &number1 << endl;      cout << pNumber1 << endl;      cout << &pNumber1 << endl;     pNumber1 = &number2;                int & refNumber1 = number1;     refNumber1 = 11;                cout << refNumber1 << endl;     cout << &number1 << endl;       cout << &refNumber1 << endl;                                       refNumber1 = number2;                                             number2++;       cout << refNumber1 << endl;      cout << number1 << endl;         cout << number2 << endl;      }

A reference variable provides a new name to an existing variable. Information technology is dereferenced implicitly and does non need the dereferencing operator * to retrieve the value referenced. On the other mitt, a pointer variable stores an address. You tin alter the address value stored in a pointer. To call up the value pointed to by a pointer, you need to utilise the indirection operator *, which is known as explicit dereferencing. Reference can exist treated as a const arrow. It has to be initialized during annunciation, and its content cannot be inverse.

Reference is closely related to pointer. In many cases, it tin can be used equally an culling to pointer. A reference allows you lot to manipulate an object using arrow, but without the pointer syntax of referencing and dereferencing.

The above example illustrates how reference works, just does non show its typical usage, which is used as the function formal parameter for pass-by-reference.

Pass-By-Reference into Functions with Reference Arguments vs. Pointer Arguments

Pass-by-Value

In C/C++, by default, arguments are passed into functions by value (except arrays which is treated as pointers). That is, a clone copy of the statement is made and passed into the part. Changes to the clone copy inside the part has no event to the original statement in the caller. In other words, the called role has no access to the variables in the caller. For example,

one 2 three 4 5 six 7 8 9 10 11 12 13 14 15 16 17 eighteen 19
                    #include <iostream> using namespace std;   int square(int);   int main() {    int number = 8;    cout <<  "In main(): " << &number << endl;     cout << number << endl;            cout << square(number) << endl;    cout << number << endl;         }   int square(int north) {      cout <<  "In square(): " << &n << endl;     n *= n;           return n; }

The output clearly shows that there are two different addresses.

Laissez passer-past-Reference with Pointer Arguments

In many situations, we may wish to modify the original copy directly (especially in passing huge object or array) to avoid the overhead of cloning. This can be done by passing a pointer of the object into the function, known equally pass-past-reference. For example,

1 2 3 four five 6 7 8 nine x 11 12 13 14 15 sixteen 17 18
                    #include <iostream> using namespace std;   void square(int *);   int primary() {    int number = 8;    cout <<  "In main(): " << &number << endl;     cout << number << endl;      foursquare(&number);             cout << number << endl;   }   void square(int * pNumber) {     cout <<  "In square(): " << pNumber << endl;                   *pNumber                  *=                  *pNumber;      }

The called function operates on the same address, and can thus modify the variable in the caller.

Pass-by-Reference with Reference Arguments

Instead of passing pointers into function, y'all could as well pass references into office, to avoid the clumsy syntax of referencing and dereferencing. For case,

1 2 3 four 5 six 7 8 9 10 xi 12 13 14 fifteen 16 17 18
                    #include <iostream> using namespace std;   void square(int &);   int primary() {    int number = 8;    cout <<  "In main(): " << &number << endl;     cout << number << endl;     square(number);             cout << number << endl;  }   void square(int & rNumber) {     cout <<  "In square(): " << &rNumber << endl;                   rNumber                  *=                  rNumber;        }

Over again, the output shows that the chosen role operates on the same accost, and tin thus change the caller'south variable.

Take note referencing (in the caller) and dereferencing (in the function) are done implicitly. The only coding difference with pass-by-value is in the function's parameter annunciation.

Call back that references are to be initialized during proclamation. In the case of function formal parameter, the references are initialized when the part is invoked, to the caller's arguments.

References are primarily used in passing reference in/out of functions to allow the chosen part accesses variables in the caller directly.

"const" Office Reference/Pointer Parameters

A const function formal parameter cannot be modified inside the part. Use const whenever possible equally it protects you from inadvertently modifying the parameter and protects y'all against many programming errors.

A const office parameter can receive both const and non-const statement. On the other paw, a non-const role reference/pointer parameter can only receive non-const argument. For example,

ane ii iii 4 5 6 7 8 nine 10 11 12 thirteen xiv 15 sixteen 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 twoscore 41 42
                    #include <iostream> using namespace std;   int squareConst(const int); int squareNonConst(int); int squareConstRef(const int &); int squareNonConstRef(int &);   int master() {    int number = viii;    const int constNumber = 9;    cout << squareConst(number) << endl;    cout << squareConst(constNumber) << endl;    cout << squareNonConst(number) << endl;    cout << squareNonConst(constNumber) << endl;      cout << squareConstRef(number) << endl;    cout << squareConstRef(constNumber) << endl;    cout << squareNonConstRef(number) << endl;                  cout << squareNonConstRef(constNumber) << endl;                          }   int squareConst(const int number) {                      number *= number;                      return number * number; }   int squareNonConst(int number) {     number *= number;    return number; }   int squareConstRef(const int & number) {     return number * number; }   int squareNonConstRef(int & number) {     return number * number; }

Passing the Function'southward Return Value

Passing the Return-value as Reference

Y'all tin also pass the return-value as reference or pointer. For case,

1 ii 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
                    #include <iostream> using namespace std;   int & squareRef(int &); int * squarePtr(int *);   int master() {    int number1 = 8;    cout <<  "In primary() &number1: " << &number1 << endl;     int & consequence = squareRef(number1);    cout <<  "In main() &result: " << &upshot << endl;     cout << result << endl;      cout << number1 << endl;       int number2 = ix;    cout <<  "In main() &number2: " << &number2 << endl;     int * pResult = squarePtr(&number2);    cout <<  "In main() pResult: " << pResult << endl;     cout << *pResult << endl;      cout << number2 << endl;    }   int & squareRef(int & rNumber) {    cout <<  "In squareRef(): " << &rNumber << endl;     rNumber *= rNumber;    return rNumber; }   int * squarePtr(int * pNumber) {    cout <<  "In squarePtr(): " << pNumber << endl;     *pNumber *= *pNumber;    return pNumber; }
Yous should not pass Function's local variable equally return value past reference
one 2 3 4 5 6 7 8 9 10 11 12 13 xiv 15 16 17 eighteen 19 20 21 22 23 24 25
                    #include <iostream> using namespace std;   int * squarePtr(int); int & squareRef(int);   int main() {    int number = viii;    cout << number << endl;     cout << *squarePtr(number) << endl;     cout << squareRef(number) << endl;   }   int * squarePtr(int number) {    int localResult = number * number;    return &localResult;       }   int & squareRef(int number) {    int localResult = number * number;    render localResult;       }

This program has a serious logical error, as local variable of function is passed back every bit return value by reference. Local variable has local scope within the part, and its value is destroyed later on the function exits. The GCC compiler is kind plenty to effect a alert (but not mistake).

It is safe to return a reference that is passed into the function as an statement. See earlier examples.

Passing Dynamically Allocated Retentiveness equally Return Value by Reference

Instead, y'all demand to dynamically allocate a variable for the return value, and render it by reference.

1 2 3 iv five 6 7 8 nine 10 11 12 13 14 15 16 17 18 nineteen 20 21 22 23
                    #include <iostream> using namespace std;   int * squarePtr(int); int & squareRef(int);   int main() {    int number = 8;    cout << number << endl;     cout << *squarePtr(number) << endl;     cout << squareRef(number) << endl;   }   int * squarePtr(int number) {    int * dynamicAllocatedResult = new int(number * number);    return dynamicAllocatedResult; }   int & squareRef(int number) {    int * dynamicAllocatedResult = new int(number * number);    return *dynamicAllocatedResult; }

Summary

Pointers and references are highly complex and difficult to principal. But they can greatly amend the efficiency of the programs.

For novices, avoid using pointers in your programme. Improper usage tin atomic number 82 to serious logical bugs. Nonetheless, you need to understand the syntaxes of pass-by-reference with pointers and references, considering they are used in many library functions.

  • In pass-by-value, a clone is made and passed into the office. The caller's copy cannot be modified.
  • In pass-by-reference, a pointer is passed into the function. The caller's copy could be modified within the function.
  • In laissez passer-by-reference with reference arguments, you utilise the variable name as the argument.
  • In pass-by-reference with pointer arguments, you lot need to apply &varName (an address) equally the argument.

Dynamic Memory Allocation

new and delete Operators

Instead of define an int variable (int number), and assign the address of the variable to the int arrow (int *pNumber = &number), the storage can be dynamically allocated at runtime, via a new operator. In C++, whenever y'all allocate a slice of memory dynamically via new, y'all need to use delete to remove the storage (i.eastward., to return the storage to the heap).

The new operation returns a pointer to the memory allocated. The delete operator takes a pointer (pointing to the memory allocated via new) equally its sole argument.

For instance,

            int number = 88; int * p1 = &number;               int * p2;           cout << p2 << endl;           p2 = new int;                             *p2 = 99; cout << p2 << endl;   cout << *p2 << endl;           delete p2;        

Notice that new and delete operators work on pointer.

To initialize the allocated memory, you tin employ an initializer for fundamental types, or invoke a constructor for an object. For example,

            int * p1 = new int(88); double * p2 = new double(ane.23);    int * p1 = new int {88}; double * p2 = new double {1.23};    Date * date1 = new Engagement(1999, ane, 1);   Fourth dimension * time1 = new Fourth dimension(12, 34, 56);

Yous tin can dynamically allocate storage for global pointers inside a function. Dynamically allocated storage inside the function remains even after the function exits. For example,

1 ii 3 four 5 6 7 8 9 x xi 12 13 xiv 15 16 17 18 xix 20 21 22
                    #include <iostream> using namespace std;   int * p1, * p2;       void allocate() {    p1 = new int;         *p1 = 88;             p2 = new int(99);  }   int master() {    classify();    cout << *p1 << endl;      cout << *p2 << endl;      delete p1;      delete p2;    return 0; }

The main differences between static allocation and dynamic allocations are:

  1. In static resource allotment, the compiler allocates and deallocates the storage automatically, and handle memory management. Whereas in dynamic allocation, you, every bit the programmer, handle the memory resource allotment and deallocation yourself (via new and delete operators). You have full control on the pointer addresses and their contents, equally well as memory management.
  2. Static allocated entities are manipulated through named variables. Dynamic allocated entities are handled through pointers.

new[] and delete[] Operators

Dynamic array is allocated at runtime rather than compile-fourth dimension, via the new[] operator. To remove the storage, you lot demand to use the delete[] operator (instead of only delete). For example,

1 2 3 4 5 six 7 8 9 10 eleven 12 13 14 15 sixteen 17 18 nineteen 20 21 22 23 24
                    #include <iostream> #include <cstdlib> using namespace std;   int main() {    const int SIZE = 5;    int * pArray;      pArray = new int[SIZE];            for (int i = 0; i < SIZE; ++i) {       *(pArray + i) = rand() % 100;    }        for (int i = 0; i < SIZE; ++i) {       cout << *(pArray + i) << " ";    }    cout << endl;      delete[] pArray;      return 0; }

C++03 does non allow your to initialize the dynamically-allocated array. C++11 does with the brace initialization, as follows:

            int * p = new int[v] {ane, 2, 3, 4, five};

Pointer, Array and Function

Array is Treated equally Pointer

In C/C++, an assortment's name is a arrow, pointing to the outset element (alphabetize 0) of the assortment. For case, suppose that numbers is an int assortment, numbers is a likewise an int pointer, pointing at the starting time element of the array. That is, numbers is the aforementioned as &numbers[0]. Consequently, *numbers is number[0]; *(numbers+i) is numbers[i].

For case,

1 2 3 four five 6 7 8 nine 10 11 12 xiii 14 xv xvi
                    #include <iostream> using namespace std;   int main() {    const int SIZE = 5;    int numbers[SIZE] = {11, 22, 44, 21, 41};                cout << &numbers[0] << endl;     cout << numbers << endl;         cout << *numbers << endl;             cout << *(numbers + i) << endl;       cout << *(numbers + 4) << endl;    }

Pointer Arithmetic

Equally seen from the previous section, if numbers is an int assortment, it is treated as an int arrow pointing to the offset chemical element of the assortment. (numbers + 1) points to the next int, instead of having the next sequential accost. Accept note that an int typically has iv bytes. That is (numbers + ane) increases the address past 4, or sizeof(int). For instance,

int numbers[] = {11, 22, 33}; int * iPtr = numbers; cout << iPtr << endl;         cout << iPtr + 1 << endl;     cout << *iPtr << endl;        cout << *(iPtr + 1) << endl;   cout << *iPtr + 1 << endl;        

sizeof Array

The performance sizeof(arrayName) returns the total bytes of the assortment. You lot tin derive the length (size) of the array by dividing it with the size of an element (e.g. element 0). For case,

                    int numbers[100];          cout <<          sizeof(numbers)          << endl;      cout <<          sizeof(numbers[0])          << endl;   cout << "Assortment size is " << sizeof(numbers) / sizeof(numbers[0]) << endl;        

Passing Array In/Out of a Part

An array is passed into a function as a pointer to the starting time element of the array. You can use array note (e.g., int[]) or pointer notation (east.g., int*) in the function announcement. The compiler always treats it as pointer (e.one thousand., int*). For example, the post-obit declarations are equivalent:

int max(int numbers[], int size); int max(int *numbers, int size); int max(int number[50], int size);

They will be treated as int* by the compiler, equally follow. The size of the array given in [] is ignored.

int max(int*, int);

Array is passed by reference into the function, because a arrow is passed instead of a clone re-create. If the array is modified within the function, the modifications are applied to the caller's copy. You could declare the array parameter equally const to preclude the array from existence modified inside the part.

The size of the array is not part of the assortment parameter, and needs to be passed in another int parameter. Compiler is not able to deduce the assortment size from the assortment pointer, and does not perform array bound bank check.

Example: Using the usual assortment notation.

1 two 3 4 5 vi 7 viii nine 10 11 12 13 fourteen fifteen 16 17 18 nineteen 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
                    #include <iostream> using namespace std;    int max(const int arr[], int size); void replaceByMax(int arr[], int size); void impress(const int arr[], int size);   int main() {    const int SIZE = four;    int numbers[SIZE] = {11, 22, 33, 22};    print(numbers, SIZE);    cout << max(numbers, SIZE) << endl;    replaceByMax(numbers, SIZE);    print(numbers, SIZE); }     int max(const int arr[], int size) {    int max = arr[0];    for (int i = 1; i < size; ++i) {       if (max < arr[i]) max = arr[i];    }    return max; }     void replaceByMax(int arr[], int size) {    int maxValue = max(arr, size);    for (int i = 0; i < size; ++i) {       arr[i] = maxValue;    } }    void print(const int arr[], int size) {    cout << "{";    for (int i = 0; i < size; ++i) {       cout << arr[i];       if (i < size - i) cout << ",";    }    cout << "}" << endl; }

Take notation that you can alter the contents of the caller's array inside the role, as array is passed by reference. To foreclose adventitious modification, you lot could employ const qualifier to the function's parameter. Recall that const inform the compiler that the value should not be inverse. For example, suppose that the role print() prints the contents of the given assortment and does not modify the array, you could apply const to both the array name and its size, as they are non expected to be changed inside the office.

void impress(const          int arr[],          int          size);

Compiler flags out an error "assignment of read-simply location" if it detected a const value would be changed.

Example: Using pointer notation.

1 2 3 4 5 half dozen 7 8 9 10 11 12 thirteen 14 15 16 17 eighteen 19 xx 21
                    #include <iostream> using namespace std;    int max(const int *arr, int size);   int principal() {    const int SIZE = 5;    int numbers[SIZE] = {10, 20, ninety, 76, 22};    cout << max(numbers, SIZE) << endl; }    int max(const int *arr, int size) {    int max =                  *arr;    for (int i = i; i < size; ++i) {       if (max <                  *(arr+i)) max =                  *(arr+i);    }    return max; }

Pass-by-Reference and sizeof

1 2 three four 5 6 vii eight nine x 11 12 13 14 15 16 17 18 19 twenty 21
                    #include <iostream> using namespace std;    void fun(const int *arr, int size);    int main() {    const int SIZE = 5;    int a[SIZE] = {8, 4, 5, 3, 2};    cout << "sizeof in main() is " <<                  sizeof(a)                  << endl;    cout << "accost in main() is " << a << endl;    fun(a, SIZE); }    void fun(const int *arr, int size) {    cout << "sizeof in office is " <<                  sizeof(arr)                  << endl;    cout << "accost in function is " << arr << endl; }
sizeof in main() is 20 address in main() is 0x22fefc sizeof in function is iv address in function is 0x22fefc

The address of arrays in principal() and the office are the same, every bit expected, as assortment is passed by reference.

In main(), the sizeof array is 20 (4 bytes per int, length of 5). Inside the function, the sizeof is iv, which is the sizeof int pointer (4-byte address). This is why you need to pass the size into the function.

Operating on a Range of an Array

i 2 3 four five half dozen vii 8 9 10 xi 12 thirteen 14 15 16 17 eighteen 19 xx 21 22 23 24 25
                    #include <iostream> using namespace std;    int sum(const int *begin, const int *end);    int main() {    int a[] = {8, 4, 5, 3, ii, 1, 4, eight};    cout << sum(a, a+eight) << endl;           cout << sum(a+2, a+five) << endl;         cout << sum(&a[2], &a[5]) << endl;  }      int sum(const int *begin, const int *end) {    int sum = 0;    for (const int *p = begin; p != end; ++p) {       sum += *p;    }    return sum; }

Program Notes:

  • To write a function that operates on a range of the given array, you can pass the begin arrow and the cease pointer into the function. By convention, the performance shall kickoff at the begin pointer, upwardly to the end pointer, but excluding the end pointer.
  • In "const int *p", *p (content pointed-to) is constant, just p is non constant.

C-String and Arrow

C-string (of the C linguistic communication) is a character array, terminated with a cipher character '\0'. For example,

ane 2 3 4 5 half dozen seven 8 nine ten eleven 12 xiii fourteen fifteen 16 17 eighteen 19 20 21 22 23 24 25 26 27
                    #include <iostream> #include <cstring> using namespace std;   int main() {    char msg1[] = "How-do-you-do";    char *msg2 = "Howdy";            cout << strlen(msg1) << endl;       cout << strlen(msg2) << endl;    cout << strlen("Hello") << endl;      int size = sizeof(msg1)/sizeof(char);    cout << size << endl;     for (int i = 0; msg1[i] != '\0'; ++i) {       cout << msg1[i];    }    cout << endl;      for (char *p = msg1; *p != '\0'; ++p) {                 cout << *p;    }    cout << endl; }

Take note that for C-String function such equally strlen() (in header cstring, ported over from C'south cord.h), there is no need to pass the assortment length into the part. This is considering C-Strings are terminated by '\0'. The part tin iterate thru the characters in the array until '\0'. For example,

1 2 three 4 5 6 7 eight 9 10 eleven 12 13 xiv 15 16 17 18 19 20 21 22 23 24 25 26
                    #include <iostream> #include <cstring> using namespace std;   int count(const char *str, const char c);     int main() {    char msg1[] = "Howdy, world";    char *msg2 = "Hello, world";      cout << count(msg1, 'l') << endl;    cout << count(msg2, 'l') << endl;    cout << count("Hullo, world", 'l') << endl; }     int count(const char *str, const char c) {    int count = 0;    while (*str) {          if (*str == c) ++count;       ++str;    }    return count; }

*More On Pointers

Function Pointer

In C/C++, functions, like all data items, have an address. The name of a role is the starting address where the part resides in the memory, and therefore, can exist treated as a pointer. Nosotros can pass a function arrow into role equally well. The syntax for declaring a role arrow is:

            render-type          (*          function-ptr-name) (parameter-list)    double (*fp)(int, int)   double *dp;              double *fun(int, int)     double f(int, int);       fp = f;        
Example
1 ii three 4 5 vi seven 8 nine 10 11 12 xiii 14 15 16 17 eighteen 19 20 21 22 23 24 25
                    #include <iostream> using namespace std;   int arithmetics(int, int,                  int (*)(int, int));       int add(int, int); int sub(int, int);   int add together(int n1, int n2) { return n1 + n2; } int sub(int n1, int n2) { return n1 - n2; }   int arithmetic(int n1, int n2,                  int (*operation) (int, int)) {    return                  (*operation)(n1, n2); }   int main() {    int number1 = v, number2 = vi;          cout << arithmetic(number1, number2, add) << endl;        cout << arithmetic(number1, number2, sub) << endl; }

Generic Pointer or void Pointer (void *)

A void pointer can hold address of any data type (except part pointer). We cannot operate on the object pointed to past void pointer, every bit the type is unknown. Nosotros tin can utilise a void pointer to compare with another address.

[TODO] Example

Constant Pointer vs. Constant Pointed-to Data

  1. Not-abiding pointer to abiding information: Data pointed to CANNOT be inverse; only pointer Tin be changed to signal to another data. For example,
    int i1 = 8, i2 = 9;              const              int * iptr = &i1;   // *iptr = 9;              // error: assignment of read-just location              iptr = &i2;            
  2. Constant arrow to not-constant information: Data pointed to Tin be changed; but pointer CANNOT be changed to point to some other data. For example,
    int i1 = eight, i2 = 9; int *              const              iptr = &i1;   *iptr = nine;    // iptr = &i2;              // error: assignment of read-only variable            
  3. Constant pointer to constant information: Data pointed to CANNOT be changed; and arrow CANNOT be changed to bespeak to another information. For example,
    int i1 = 8, i2 = 9;              const              int *              const              iptr = &i1;   // *iptr = nine;              // error: assignment of read-only variable              // iptr = &i2;              // error: assignment of read-merely variable            
  4. Non-constant pointer to not-constant data: Data pointed to Tin exist changed; and pointer Tin can be changed to point to another data. For example,
    int i1 = 8, i2 = 9; int *              iptr = &i1;   *iptr = 9;    iptr = &i2;            

Link to "C++ References & Resource"

bentleylignouser.blogspot.com

Source: https://www3.ntu.edu.sg/home/ehchua/programming/cpp/cp4_PointerReference.html

Post a Comment for "Which Arithmetic Operations Can Be Performed on Pointers?"