Pointers

The address of a variable

On our machine storm.cis.fordham.edu, a memory address (and a pointer that holds a memory address) occupies 64 bits = 8 bytes. That means it could take up to 64 ÷ 4 = 16 hexadecinal digits to write the value of the address.

  1. address.C, address.txt: output the address of a variable in hexadecimal. “Ampersand” and “address” begin with the same letter. & is a unary operator like the negative sign -i.
  2. Store the address of a variable into a pointer.
    (Diagram with pointer pointing left.)
    Use the data type size_t for a variable holding the number of elements in an array, or the number of bytes in a block of memory.
    1. pointerint.C, pointerint.txt: store the address of an int into a “pointer to an int”.
    2. pointerdouble.C, pointerdouble.txt: store the address of a double into a “pointer to a double”.

Dereference a pointer with the operators   *   [ ]   ->

Dereferencing a pointer means getting the value of the variable that the pointer points to.

  1. The unary * operator dereferences the pointer, i.e., gets the value to which the pointer points.
    (The other * operator is the binary one that performs multiplication.)
    1. dereferenceint.C dereferenceint.txt.
    2. dereferencedouble.C, dereferencedouble.txt.
  2. If a pointer points to an element in an array, the pointer also gives us access to the values that are neighbors of the element.
    1. neighborint.C, neighborint.txt.
    2. neighbordouble.C, neighbordouble.txt.
  3. Handy abreviations. Let’s say we have
    	int a[] {0, 10, 20, 30, 40, 50, 60, 70, 80, 90};
    	int n {size(a)};   //the number of elements in the array
    
    1. In this language, the name of an array all by itself means the address where the array begins. So you can write a in place of &a[0].
    2. In this language, if you add an integer to the address where the array begins, you get the address of the element specified by the integer. Remember, the name of the array all by itself is the address where the array begins. Thus:
      You can write a+1 instead of &a[1].
      You can write a+2 instead of &a[2].

      You can write a+i instead of &a[i].

      You can write a+n instead of &a[n].
    3. Exercise. Make these changes to neighborint.C and neighbordouble.C.
  4. We saw an array of structures here:
    struct.C, struct.txt.
    Use the unary * operator to dereference a “pointer to a structure” to get the value of each field of the original structure.
    See the asterisk in level 3 and the dot in level 2 of the table of operator precedences and associativities.
    struct.C, struct.txt:

A picture of an array of ints in memory

	//Assume that the array starts at memory address 1000.
	//The array contains 10 elements, each occupying 4 bytes on our machine.
	//Therefore the array occupies addresses 1000 to 1039 inclusive.

	int a[] {
		 0,   //Occupies addresses 1000 to 1003
		10,   //Occupies addresses 1004 to 1007
		20,   //Occupies addresses 1008 to 1011
		30,   //Occupies addresses 1012 to 1015
		40,   //Occupies addresses 1016 to 1019
		50,   //Occupies addresses 1029 to 1023
		60,   //Occupies addresses 1024 to 1027
		70,   //Occupies addresses 1028 to 1031
		80,   //Occupies addresses 1032 to 1035
		90    //Occupies addresses 1036 to 1039
	};

Note that address 1040 is just beyond the end of the array.

Use a pointer in a loop

  1. loop.C, loop.txt. You can increment (or decrement) a pointer only if it is pointing to an element in an array. The increment (or decrement) will make the pointer point to the next (or to the previous) element in the array.
  2. elementaddress.C. elementaddress.txt. Output the address of each element of an array, in hexadecimal and decimal. When we increment a pointer to an int (with ++p), we are actually adding 4 to the value of the pointer. That’s because each int occupies 4 bytes of memory. (The addresses are up in the 140 trillions.)
  3. rocket.C: overwrite four consecutive array elements. Has to be seen to be believed.
  4. movingaverage.C, movingaverage.txt. compute the sum of five consecutive array elements.
    The expression
    p[-2] + p[-1] + p[0] + p[1] + p[2]
    is simpler than the expression
    a[i-2] + a[i-1] + a[i] + a[i+1] + a[i+2]
  5. Use a pointer to access a pair of consecutive elements of an array.
    1. bubble1.C, bubble1.txt. Uses subscripts i, j.
    2. bubble2.C, bubble2.txt. Uses pointers p, q.
    The statement
    if (q[0] > q[1]) {
    is simpler than the statement
    if (a[j] > a[j + 1]) {

Use a pointer as an argument to a function.

  1. pass.C, pass.txt.
    three ways to pass an int to a function.
    1. The function f receives a copy of the variable i, and can change the value of the copy. (The name of the copy is cop.) But changing the value of the copy has no effect on the value of i.
    2. val is just another name for the variable j. This allows the function to change the value of j. No copy of j is created.
    3. The function receives the address of the variable k, and can use this address (dereferenced with a *) to install a new value into k.
    The statement that calls the function (j(i, j, &k);) makes it obvious that the function can change the value of k. But it is dangerously unobvious that the function can also change the value of j.
  2. array.C, array.txt.
    Pass the address of the start and end (actually, just beyond the end) of an array to a function.
  3. passstruct.C, passstruct.txt.
    Pass the address of a structure to a function. We had to tell the computer what a month is before we could tell it what f is. Believe it or not, passing the address of a structure to a function is the jumping-off point into the world of Object-Oriented Programming!
  4. llawrence18
    1. structure.C
    2. structure1.C with “bitwise and” (&) and “bitwise or” (|).
        00000000000000000000000000000010   Monday
        00000000000000000000000000000100   Tuesday
        00000000000000000000000000001000   Wednesday
        00000000000000000000000000010000   Thursday
      | 00000000000000000000000001000000   Saturday
        00000000000000000000000001011110
      
    3. structure2.C holds the three structures in an array of structures.
  5. jcrews1.C: sort an array of structures.
  6. jr224.C: search an array instead of executing a series of if statements.

Two ways to make a pointer const

You can insert the keyword const into a declaration in two places:

  1. at the start of the declaration
  2. immediately after an asterisk in the declaratation
	int i {10};
	int j {20};
	int *p {&i};   //p points to i.

The following diagram shows two regions of memory, the pointer on the right and the pointed-to variable on the left.

  1. const.C. Not allowed to change the value of a const variable. It always holds the same value.
    If you uncomment the ++j in line 18, the c++ compiler will say
    const.C:18:11: error: increment of read-only variable ā€˜j’
    
  2. constptr.C. Not allowed to change the value of a const pointer. It always points to the same place.
  3. readonly.C. A read-ony pointer is a needle that can’t scratch the record.
  4. both.C. A pointer that is const in both ways.