1 of 25

POINTERS

A LECTURE FOR THE C++ COURSE

Each slide may have its own narration in an audio file. �For the explanation of any slide, click on the audio icon to start the narration.

The Professor‘s C++ Course by Linda W. Friedman is licensed under a �Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.

2 of 25

ADDRESSES OF VARIABLES

  • Recall that every variable has: a name, a type, an address in memory, and (possibly) a value.

  • If we want to refer to the address of a particular variable we can use the address operator, &. [We have already used this operator when passing by reference.]

  •  The address operator, &, operates on the variable name to produce an address:

variable name 🡪 & 🡪 variable address

POINTERS

2

3 of 25

WORKING WITH ADDRESSES

//ptr1.cpp

#include <iostream>

int main(){

int n=33;

cout << "n= " << n << endl;

cout << "&n= " << &n << endl;

return 0;

}

Output:

The address output is a hexadecimal number. All hex numbers start with 0x.

POINTERS

3

n= 33

&n= 0x0068fe00

4 of 25

WORKING WITH ADDRESSES

Aside:

Let’s convert the hex number to one in the base 10 number system that we are more familiar with.

0 0 6 8 f e 0 0

167 166 165 164 163 162 161 160

 

[=6 x (16)5 + 8 x (16)4 + 15 x (16)3 + 14 x (16)2 + 0 x (16)1 + 0 x (16)0

=6 x 1,048,576 + 8 x 65,536 + 15 x 4096 + 14 x 256 + 0 + 0]

So, 0068fe00(16) = 6880768(10)

POINTERS

4

n= 33

&n= 0x0068fe00

5 of 25

REFERENCE VARIABLES

  • A reference variable is an alias for another variable.
  • In the following example, n and r are always the same. They are just different names for the same variable. Even the same address prints for both. They have the same value and occupy the same location.

//ptr2.cpp

#include <iostream>

int main(){

int n=33;

int &r = n;

cout << n << "\t\t" << r << endl;

n--;

cout << n << "\t\t" << r << endl;

r*= 2;

cout << n << "\t\t" << r << endl;

cout << &n << '\t' << &r << endl;

  return 0;

}

POINTERS

5

6 of 25

REFERENCE VARIABLES

  • The output:

  • A reference parameter is also an alias, or synonym. A reference parameter for a function is just a reference variable whose scope is limited to the function.

POINTERS

6

7 of 25

BACK TO POINTERS

What does this have to do with pointers? If we take the address of a variable and store it in another variable, that's a pointer.

  • The pointer variable p and the expression &n have the same type (pointer to int) and the same value.
  • Since we don't care what the exact address of n is we might indicate a pointer as: 

p n

POINTERS

7

33

8 of 25

THE POINTER ‘TYPE’

  • When we declare a variable as a pointer – for example, int *p - it’s type is “pointer to int”.

  • Derived Types: Types derived from (based on) other types.
        • int a[] = {33, 66}; // type array of int
        • const int c = 33; // type const int
        • int f() {return 33;} // type function returns int
        • int & r = n; // type reference to int
        • int * p = &n; // type pointer to int

 

  • These are all types derived from a single type. What is a type that is derived from multiple types? A struct or class.

POINTERS

8

9 of 25

DEREFERENCING A POINTER

Since *p is an alias for n, we can use it to get the value of n. This is called dereferencing a pointer.

The & and * operators are inverses of each other.

n = = *p n = = *&n

p = = &n p = = &*p

 

We say that a (regular) variable directly references a value and a pointer indirectly references a value.

POINTERS

9

10 of 25

INITIALIZATION OF POINTERS

  • Pointers can (and should) be initialized when they are declared: to 0, NULL, or an address. A pointer with value 0 or NULL is not pointing to anything. NULL is a symbolic constant defined in iostream.h (among others).

  • Even though the constant 0 has type int, it can be assigned to all the fundamental types, and takes on the appropriate value for each particular type:
          • char c = 0; // initializes c to the char '\0' or NUL (ASCII 0)
          • float x = 0; // initializes x to the float 0.0
          • char* p = 0; // initializes p to NULL, no matter what type p is pointing to.

  • The NULL pointer cannot be dereferenced. So, *p = 22; is an invalid statement if p was initialized with int* p=0; We may wish to check before assigning a value to a dereferenced pointer: if (p) *p = 22;
        • Testing (p) is the same as testing the condition (p != NULL) because p = = 0 = = NULL.

POINTERS

10

11 of 25

PASSING BY REFERENCE

  • The default parameter passing mechanism is, of course, pass by value. We can pass by reference using either addresses (references) or pointers. For example, the swap function works the same if we use addresses or pointers to pass function parameters:

POINTERS

11

12 of 25

PASSING BY REFERENCE

Same output, using pointer variables as parameters:

POINTERS

12

13 of 25

PASSING BY REFERENCE

Pointers can themselves be passed by reference, if we wish to change the addresses stored in them. If a pointer is not specifically passed by reference, the system will make a local copy of the pointer in the function, and any changes to the pointer are not carried back to the calling function.

POINTERS

13

14 of 25

LVALUES

  • You have probably already come across the term lvalue in an error message. The term lvalue gets its name because it used to mean any token that could appar on the left side of an assignment statement, for example: x = 4; However, it is a bit more general than that.

  • An lvalue refers to an object or function (i.e., a region of storage), anything whose address is accessible.
        • Names of objects (variables) are lvalues: int n; n=127;
        • Literals are not lvalues: 44 = n; //error
        • Similarly, an expression cannot be an lvalue.
        • Symbolic constants are lvalues, even though they cannot be changed and so cannot appear on the left side of an assignment operation:

const int MAX = 999; //OK

MAX = 27; //error

POINTERS

14

15 of 25

LVALUES

  • In the above examples, we say that n is a mutable lvalue and MAX is an example of an immutable lvalue.
      • Other mutable lvalues:
        • subscripted variables a[5]=10;
        • Dereferenced pointers *p=25;

[Immutable lvalues: Arrays, functions, references. (later)]

 

  • To declare a reference variable the general syntax requirement is:

type & refname = lvalue; For example,

          • int &r = n; //OK, n is an lvalue
          • int &r = 44; //error
          • int &r = n++; //error
          • int &r = cube(n); //error

POINTERS

15

16 of 25

CONSTANT VS NON-CONSTANT POINTERS

  • There are four possibilities:
        • Non-constant pointer to non-constant data.
        • Non-constant pointer to constant data.
        • Constant pointer to non-constant data.
        • Constant pointer to constant data.

Examples:

        • int x = 5; // x is an integer
        • int * const p = &x; // p is a const pointer to x - p can't be changed but *p can be changed
        • const int * const p = &x; // p is a const pointer to x, *p is also a const and can't be changed

  • This becomes important when passing by reference using pointers. We want to give the function as little access to the data as possible.

POINTERS

16

17 of 25

CONSTANT VS NON-CONSTANT POINTERS

  • More examples:

[from Hubbard, p. 169]

int n = 44; //n is an int

int * p = &n; // pointer to n

++(*p); //OK. Increments *p (an integer)

++p; //OK. Increments p (a pointer)

int * const cp = &n; //a const pointer to n

++(*cp); //OK. Increments *cp

++cp; // error. Pointer cp is a constant

const int k = 88; //k is a const integer

const int * pc = &k; //pc points to k

++(*pc); //error: *pc is a constant (k)

++pc; //OK. Increments pointer pc

const int * const cpc = &k; //const pointer to a const integer

++(*cpc); //error: *cpc is a const

++cpc; //error: pointer cpc is a constant

POINTERS

17

18 of 25

POINTER ARITHMETIC:

  • Pointers contain addresses, not integers. But some arithmetic can be done on pointers. When you perform arithmetic on a pointer, it is pointing somewhere else. The change in address depends on the size of the type it is pointing to (i.e., when we increment a pointer, we are not simply adding 1).

  • We can use:

++ + +=

−− − −=

 

  • Because pointer arithmetic depends on the size of the memory locations (for any particular type), pointer arithmetic is machine dependent.

POINTERS

18

19 of 25

ARRAYS AND POINTERS

We can use pointer arithmetic to traverse an array:

POINTERS

19

20 of 25

ARRAYS AND POINTERS

Output:

POINTERS

20

21 of 25

ARRAYS AS PARAMETERS

We know that when we pass an array name as an argument to a function it is passed by reference even if there is no &. This is because the array name is a reference to the address of the first array element array[0]. In other words, it is a pointer. The compiler automatically converts int a[] in the argument list to int * const a. The default for an array name is as a constant pointer to non-constant data. This means that the pointer always points to the same memory location, and the data at that location can be modified through the pointer.

 

For example, the bubble sort algorithm using reference parameters:

POINTERS

21

22 of 25

ARRAYS AS PARAMETERS

Output:

POINTERS

22

23 of 25

POINTERS TO/FROM FUNCTIONS

  • Just like an array name is really the memory address of the first element of the array, a function name is the starting memory address of the function's code. So the function name is actually a const pointer.

  • So a function (or a pointer to a function) can be a parameter to another function. This allows us to define functions of functions.

  • The following example program (on the next slide) will sum f(0) + f(2) + f(3) + … + f(n) for any function.

POINTERS

23

24 of 25

POINTERS TO/FROM FUNCTIONS

This program will sum f(0) + f(2) + f(3) + … + f(n) for any function.

POINTERS

24

25 of 25

REVIEW

    • Pointer
    • Lvalue
    • Reference parameter
    • Dereferencing a pointer
    • Address operator

POINTERS

25

What did we learn in this lecture? Plenty.