C / C++ Pointer

A pointer stores a memory address

Initializing Pointers via the Address-Of Operator (&)

int number = 88;              // An int variable with a value
int * pNumber;                // Declare a pointer variable called pNumber pointing to an int (or int pointer)
pNumber = &number;            // Assign the address of the variable number to pointer pNumber
int * pAnother = &number;     // Declare another int pointer and init to address of the variable number

Indirection or Dereferencing Operator (*)

int number = 88;
int * pNumber = &number;  // Declare and assign the address of variable number to pointer pNumber (0x22ccec)
cout << pNumber<< endl;   // Print the content of the pointer variable, which contain an address (0x22ccec)
cout << *pNumber << endl; // Print the value "pointed to" by the pointer, which is an int (88)
*pNumber = 99;            // Assign a value to where the pointer is pointed to, NOT to the pointer variable
cout << *pNumber << endl; // Print the new value "pointed to" by the pointer (99)
cout << number << endl;   // The value of variable number changes as well (99)

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

As illustrated, a variable (such as number) directly references a value, whereas a pointer indirectly references a value through the memory address it stores. Referencing a value indirectly via a pointer 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.

Take note that the symbol * has different meaning in a declaration statement and in an expression. When it is used in a declaration (e.g., 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;), it refers to the value pointed to by the pointer variable.

int number = 88;    // Declare an int variable and assign an initial value
int * pNumber;      // Declare a pointer variable pointing to an int (or int pointer)
pNumber = &number;  // assign the address of the variable number to pointer pNumber

cout << pNumber << endl;  // Print content of pNumber (0x22ccf0)
cout << &number << endl;  // Print address of number (0x22ccf0)
cout << *pNumber << endl; // Print value pointed to by pNumber (88)
cout << number << endl;   // Print value of number (88)

*pNumber = 99;            // Re-assign value pointed to by pNumber
cout << pNumber << endl;  // Print content of pNumber (0x22ccf0)
cout << &number << endl;  // Print address of number (0x22ccf0)
cout << *pNumber << endl; // Print value pointed to by pNumber (99)
cout << number << endl;   // Print value of number (99)
                             // The value of number changes via pointer 
cout << &pNumber << endl; // Print the address of pointer variable pNumber (0x22ccec)

References (or Aliases) (&)

int number = 88;          // Declare an int variable called number
int & refNumber = number; // Declare a reference (alias) to the variable number
                            // Both refNumber and number refer to the same value

cout << number << endl;    // Print value of variable number (88)
cout << refNumber << endl; // Print value of reference (88)

refNumber = 99;            // Re-assign a new value to refNumber
cout << refNumber << endl;
cout << number << endl;    // Value of number also changes (99)

number = 55;               // Re-assign a new value to number
cout << number << endl;
cout << refNumber << endl; // Value of refNumber also changes (55)

References vs. Pointers

  • A reference is a name constant for an address. You need to initialize the reference during declaration. Once a reference is established to a variable, you cannot change the reference to reference another variable.
  • To get the value pointed to by a pointer, you need to use the dereferencing operator * (e.g., if pNumber is a int pointer, *pNumber returns the value pointed to by pNumber. It is called dereferencing or indirection). To assign an address of a variable into a pointer, you need to use the address-of operator & (e.g., 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 address of a variable to a reference variable, no address-of operator & is needed.

int number1 = 88, number2 = 22;

// Create a pointer pointing to number1
int * pNumber1 = &number1;  // Explicit referencing
*pNumber1 = 99;             // Explicit dereferencing
cout << *pNumber1 << endl;  // 99
cout << &number1 << endl;   // 0x22ff18
cout << pNumber1 << endl;   // 0x22ff18 (content of the pointer variable - same as above)
cout << &pNumber1 << endl;  // 0x22ff10 (address of the pointer variable)
pNumber1 = &number2;        // Pointer can be reassigned to store another address

// Create a reference (alias) to number1
int & refNumber1 = number1;  // Implicit referencing (NOT &number1)
refNumber1 = 11;             // Implicit dereferencing (NOT *refNumber1)
cout << refNumber1 << endl;  // 11
cout << &number1 << endl;    // 0x22ff18
cout << &refNumber1 << endl; // 0x22ff18
//refNumber1 = &number2;     // Error! Reference cannot be re-assigned
                            // error: invalid conversion from 'int*' to 'int'
refNumber1 = number2;        // refNumber1 is still an alias to number1.
                            // Assign value of number2 (22) to refNumber1 (and number1).
number2++;   
cout << refNumber1 << endl;  // 22
cout << number1 << endl;     // 22
cout << number2 << endl;     // 23

A reference variable provides a new name to an existing variable. It is dereferenced implicitly and does not need the dereferencing operator * to retrieve the value referenced. On the other hand, a pointer variable stores an address. You can change the address value stored in a pointer. To retrieve the value pointed to by a pointer, you need to use the indirection operator *, which is known as explicit dereferencing. Reference can be treated as a const pointer. It has to be initialized during declaration, and its content cannot be changed.

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

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

Pass-by-Value

int main() {
   int number = 8;
   cout <<  "In main(): " << &number << endl;  // 0x22ff1c
   cout << number << endl;         // 8
   cout << square(number) << endl; // 64
   cout << number << endl;         // 8 - no change
}

int square(int n) {  // non-const
   cout <<  "In square(): " << &n << endl;  // 0x22ff00
   n *= n;           // clone modified inside the function
   return n;
}

Pass-by-Reference with Pointer Arguments

int main() {
   int number = 8;
   cout <<  "In main(): " << &number << endl;  // 0x22ff1c
   cout << number << endl;   // 8
   square(&number);          // Explicit referencing to pass an address
   cout << number << endl;   // 64
}

void square(int * pNumber) {  // Function takes an int pointer (non-const)
   cout <<  "In square(): " << pNumber << endl;  // 0x22ff1c
   *pNumber *= *pNumber;      // Explicit de-referencing to get the value pointed-to
}

Pass-by-Reference with Reference Arguments

int main() {
   int number = 8;
   cout <<  "In main(): " << &number << endl;  // 0x22ff1c
   cout << number << endl;  // 8
   square(number);          // Implicit referencing (without '&')
   cout << number << endl;  // 64
}

void square(int & rNumber) {  // Function takes an int reference (non-const)
   cout <<  "In square(): " << &rNumber << endl;  // 0x22ff1c
   rNumber *= rNumber;        // Implicit de-referencing (without '*')
}

"const" Function Reference/Pointer Parameters

A const function formal parameter cannot be modified inside the function. Use const whenever possible as it protects you from inadvertently modifying the parameter and protects you against many programming errors. A const function parameter can receive both const and non-const argument. On the other hand, a non-const function reference/pointer parameter can only receive non-const argument.

Passing the Function's Return Value

Passing the Return-value as Reference

int & squareRef(int &);
int * squarePtr(int *);

int main() {
   int number1 = 8;
   cout <<  "In main() &number1: " << &number1 << endl;  // 0x22ff14
   int & result = squareRef(number1);
   cout <<  "In main() &result: " << &result << endl;  // 0x22ff14
   cout << result << endl;   // 64
   cout << number1 << endl;  // 64

   int number2 = 9;
   cout <<  "In main() &number2: " << &number2 << endl;  // 0x22ff10
   int * pResult = squarePtr(&number2);
   cout <<  "In main() pResult: " << pResult << endl;  // 0x22ff10
   cout << *pResult << endl;   // 81
   cout << number2 << endl;    // 81
}

int & squareRef(int & rNumber) {
   cout <<  "In squareRef(): " << &rNumber << endl;  // 0x22ff14
   rNumber *= rNumber;
   return rNumber;
}

int * squarePtr(int * pNumber) {
   cout <<  "In squarePtr(): " << pNumber << endl;  // 0x22ff10
   *pNumber *= *pNumber;
   return pNumber;
}

Bad example

int * squarePtr(int);
int & squareRef(int);

int main() {
   int number = 8;
   cout << number << endl;  // 8
   cout << *squarePtr(number) << endl;  // ??
   cout << squareRef(number) << endl;   // ??
}

int * squarePtr(int number) {
   int localResult = number * number;
   return &localResult;
      // warning: address of local variable 'localResult' returned
}

int & squareRef(int number) {
   int localResult = number * number;
   return localResult;
      // warning: reference of local variable 'localResult' returned
}

Passing Dynamically Allocated Memory as Return Value by Reference

int * squarePtr(int);
int & squareRef(int);

int main() {
   int number = 8;
   cout << number << endl;  // 8
   cout << *squarePtr(number) << endl;  // 64
   cout << squareRef(number) << endl;   // 64
}

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

  • In pass-by-value, a clone is made and passed into the function. 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 inside the function.
  • In pass-by-reference with reference arguments, you use the variable name as the argument.
  • In pass-by-reference with pointer arguments, you need to use &varName (an address) as the argument.

results matching ""

    No results matching ""