0% found this document useful (0 votes)
68 views7 pages

05 Pointers

This document provides a summary of a lecture on pointers, arrays, and pointer arithmetic in C++. It includes announcements about homework, exams, and office hours. The lecture covers defining and initializing pointer variables, dereferencing pointers, pointer arithmetic, comparing pointers, null pointers, and using pointers to iterate through arrays. An example is provided and step-through to illustrate pointer arithmetic on arrays.

Uploaded by

jeep2014
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
68 views7 pages

05 Pointers

This document provides a summary of a lecture on pointers, arrays, and pointer arithmetic in C++. It includes announcements about homework, exams, and office hours. The lecture covers defining and initializing pointer variables, dereferencing pointers, pointer arithmetic, comparing pointers, null pointers, and using pointers to iterate through arrays. An example is provided and step-through to illustrate pointer arithmetic on arrays.

Uploaded by

jeep2014
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 7

CSCI-1200 Data Structures Spring 2017

Lecture 5 Pointers, Arrays, Pointer Arithmetic


Announcements
Submitty iClicker registration is still open. Even if you already registered on the iClicker website,
submit your code on Submitty.

Starting with HW2, when Submitty opens for the homework assignment, there may be a message at the top
regarding an extra late day for earning enough autograder points by Wednesday night.
In fact, right now its set for 12 autograder points. This is the number you see and is the points from visible
test cases.

Announcements: Test 1 Information


Test 1 will be held Monday, Feb 6th, 2017 from 6-7:50pm,
Your seating assignment will be posted on Submitty / through the gradesheet. Details will be given out Friday.
No make-ups will be given except for pre-approved absence or illness, and a written excuse from the Dean of
Students or the Student Experience office or the RPI Health Center will be required.
Contact Mrs. Eberwein by email by Friday Feb 3rd to arrange for extra time accommodations. You can
alternately e-mail the ds instructors list.
Coverage: Lectures 1-6, Labs 1-3, and Homeworks 1-2.
Closed-book and closed-notes except for 1 sheet of notes on 8.5x11 inch paper (front & back) that may be
handwritten or printed. Computers, cell-phones, calculators, PDAs, music players, etc. are not permitted and
must be turned off and placed under your desk.
All students must bring their Rensselaer photo ID card.
At the start of the exam, proctors will check that you have your ID card, and if you have a sheet of notes, they
will staple it to the back of your exam.
Practice problems from previous exams are available on the course website. Solutions to the problems will be
posted on Sunday. The best way to prepare is to completely work through and write out your solution to each
problem, before looking at the answers.
The exam will involve handwriting code on paper (and other short answer problem solving). Neat legible
handwriting is appreciated. We will somewhat forgiving about minor syntax errors it will be graded by
humans not computers :)

Review from Last Week


C++ class syntax, designing classes, classes vs. structs;
Passing comparison functions to sort; Non-member operators.
More practice with const and reference (the &)

Todays Lecture Pointers and Arrays


Pointers store memory addresses.
They can be used to access the values stored at their stored memory address.
They can be incremented, decremented, added and subtracted.

Dynamic memory is accessed through pointers.


Pointers are also the primitive mechanism underlying vector iterators, which we have used with std::sort and
will use more extensively throughout the semester.
Before *p=72 After *p=72
5.1 Pointer Example
Consider the following code segment:
float x = 15.5;
float *p; /* equiv: float* p; or float * p; */
x 15.5 x 72.0
p = &x;
*p = 72;
if ( x > 20 )
cout << "Bigger\n";
else
cout << "Smaller\n"; p p

The output is Bigger


because x == 72.0. Whats going on?
Computer memory

5.2 Pointer Variables and Memory Access


x is an ordinary float, but p is a pointer that can hold the memory address of a float variable. The difference
is explained in the picture above.
Every variable is attached to a location in memory. This is where the value of that variable is stored. Hence,
we draw a picture with the variable name next to a box that represents the memory location.
Each memory location also has an address, which is itself just an index into the giant array that is the computer
memory.
The value stored in a pointer variable is an address in memory. The statement p = &x; takes the address
of xs memory location and stores it (the address) in the memory location associated with p.
Since the value of this address is much less important than the fact that the address is xs memory location,
we depict the address with an arrow.
The statement: *p = 72; causes the computer to get the memory location stored at p, then go to that
memory location, and store 72 there. This writes the 72 in xs location.
Note: *p is an l-value in the above expression.

5.3 Defining Pointer Variables


In the example below, p, s and t are all pointer variables (pointers, for short), but q is NOT. You need the *
before each variable name.
int * p, q;
float *s, *t;

There is no initialization of pointer variables in this two-line sequence, so the statement below is dangerous,
and may cause your program to crash! (It wont crash if the uninitialized value happens to be a legal address.)
*p = 15;

5.4 Operations on Pointers


The unary (single argument/operand) operator * in the expression *p is the dereferencing operator. It means
follow the pointer *p can be either an l-value or an r-value, depending on which side of the = it appears on.
The unary operator & in the expression &x means take the memory address of.
Pointers can be assigned. This just copies memory addresses as though they were values (which they are).
Lets work through the example below (and draw a picture!). What are the values of x and y at the end?
float x=5, y=9;
float *p = &x, *q = &y;
*p = 17.0;
*q = *p;
q = p;
*q = 13.0;

2
Assignments of integers or floats to pointers and assignments mixing pointers of different types are illegal.
Continuing with the above example:

int *r;
r = q; // Illegal: different pointer types;
p = 35.1; // Illegal: float assigned to a pointer

Comparisons between pointers of the form if ( p == q ) or if ( p != q ) are legal and very


useful! Less than and greater than comparisons are also allowed. These are useful only when the pointers are
to locations within an array.

5.5 Exercise
Draw a picture for the following code sequence. What is the output to the screen?
int x = 10, y = 15;
int *a = &x;
cout << x << " " << y << endl;
int *b = &y;
*a = x * *b;
cout << x << " " << y << endl;
int *c = b;
*c = 25;
cout << x << " " << y << endl;

5.6 Null Pointers


Like the int type, pointers are not default initialized. We should assume its a garbage value, leftover from
the previous user of that memory.
Pointers that dont (yet) point anywhere useful should be explicitly assigned to NULL.
NULL is equal to the integer 0, which is a legal pointer value (you can store the NULL in a pointer variable).
But NULL is not a valid memory location you are allowed to read or write. If you try to dereference or
follow a NULL pointer, your program will immediately crash. You may see a segmentation fault, a bus
error, or something about a null pointer dereference.
NOTE: In C++11 (the server still uses C++03), we are encouraged to switch to use nullptr, to avoid
some subtle situations where NULL is incorrectly seen as an int type instead of a pointer.
We indicate a NULL value in diagrams with a slash through the memory location box.
Comparing a pointer to NULL is very useful. It can be used to indicate whether or not a pointer variable is
pointing at a useable memory location. For example,
if ( p != NULL )
cout << *p << endl.

tests to see if p is pointing somewhere that appears to be useful before accessing and printing the value stored
at that location.

But dont make the mistake of assuming pointers are automatically initialized to NULL.

5.7 Arrays
Heres a quick example to remind you about how to use an array:
const int n = 10;
double a[n];
int i;
for ( i=0; i<n; ++i )
a[i] = sqrt( double(i) );

Remember: the size of array a is fixed at compile time. STL vectors act like arrays, but they can grow and
shrink dynamically in response to the demands of the application.

3
5.8 Stepping through Arrays with Pointers (Array Iterators)
The array code above that uses [] subscripting, can be equivalently rewritten to use pointers:
const int n = 10;
double a[n];
double *p;
for ( p=a; p<a+n; ++p )
*p = sqrt( p-a );

The assignment: p = a; takes the address of the start of the array and assigns it to p.
This illustrates the important fact that the name of an array is in fact a pointer to the start of a block of
memory. We will come back to this several times! We could also write this line as: p = &a[0]; which
means find the location of a[0] and take its address.
By incrementing, ++p, we make p point to the next location in the array.
When we increment a pointer we dont just add one byte to the address, we add the number of bytes
(sizeof) used to store one object of the specific type of that pointer. Similarly, basic addition/subtraction
of pointer variables is done in multiples of the sizeof the type of the pointer.
Since the type of p is double, and the size of double is 8 bytes, we are actually adding 8 bytes to the
address when we execute ++p.

The test p<a+n checks to see if the value of the pointer (the address) is less than n array locations beyond
the start of the array.
In this example, a+n is the memory location 80 bytes after the start of the array (n = 10 slots * 8 bytes per
slot).
We could equivalently have used the test p != a+n
In the assignment:
*p = sqrt( p-a )

p-a is the number of array locations (multiples of 8 bytes) between p and the start. This is an integer. The
square root of this value is assigned to *p.
Heres a picture to explain this example:

const int n 10

a[10]
3.00 a[9]
2.83 a[8]
increasing 2.65 a[7]
address 2.45 a[6]
value
2.23 a[5]
2.00 a[4]
1.73 a[3]
1.41 a[2]
1.00 a[1]
double [] a 0.00 a[0]

double* p

4
Note that there may or may not be unused memory between your array and the other local variables. Similarly,
the order that your local variables appear on the stack is not guaranteed (the compiler may rearrange things
a bit in an attempt to optimize performance or memory usage). A buffer overflow (attempting to access an
illegal array index) may or may not cause an immediate failure depending on the layout of other critical
program memory.

5.9 Sorting an Array


Arrays may be sorted using std::sort, just like vectors. Pointers are used in place of iterators. For example,
if a is an array of doubles and there are n values in the array, then heres how to sort the values in the array
into increasing order:

std::sort( a, a+n );

5.10 Exercises
For each of the following problems, you may only use pointers and not subscripting:
1. Write code to print the array a backwards, using pointers.

2. Write code to print every other value of the array a, again using pointers.

3. Write a function that checks whether the contents of an array of doubles are sorted into increasing order. The
function must accept two arguments: a pointer (to the start of the array), and an integer indicating the size of
the array.

5
5.11 C Calling Convention
We take for granted the non-trivial task of passing data to a helper function, getting data back from that
function, and seamlessly continuing on with the program. How does that work??
A calling convention is a standardized method for passing arguments between the caller and the function.
Calling conventions vary between programming languages, compilers, and computer hardware.
In C on x86 architectures here is a generalization of what happens:
1. The caller puts all the arguments on the stack, in reverse order.
2. The caller puts the address of its code on the stack (the return address).
3. Control is transferred to the callee.
4. The callee puts any local variables on the stack.
5. The callee does its work and puts the return value in a special register (storage location).
6. The callee removes its local variables from the stack.
7. Control is transferred by removing the address of the caller from the stack and going there.
8. The caller removes the arguments from the stack.
On x86 architectures the addresses on the stack are in descending order. This is not true of all hardware.

6
5.12 Poking around in the Stack & Looking for the C Calling Convention
Lets look more closely at an example of where the compiler stores our data. Specifically, lets print out the
addresses and values of the local variables and function parameters:

int foo(int a, int *b) {


int q = a+1;
int r = *b+1;
std::cout << "address of a = " << &a << std::endl;
std::cout << "address of b = " << &b << std::endl;
std::cout << "address of q = " << &q << std::endl;
std::cout << "address of r = " << &r << std::endl;
std::cout << "value at " << &a << " = " << a << std::endl;
std::cout << "value at " << &b << " = " << b << std::endl;
std::cout << "value at " << b << " = " << *b << std::endl;
std::cout << "value at " << &q << " = " << q << std::endl;
std::cout << "value at " << &r << " = " << r << std::endl;
return q*r;
}

int main() {
int x = 5;
int y = 7;
int answer = foo (x, &y);
std::cout << "address of x = " << &x << std::endl;
std::cout << "address of y = " << &y << std::endl;
std::cout << "address of answer = " << &answer << std::endl;
std::cout << "value at " << &x << " = " << x << std::endl;
std::cout << "value at " << &y << " = " << y << std::endl;
std::cout << "value at " << &answer << " = " << answer << std::endl;
}

Note that the first function parameters is regular integer, passed by copy. The second parameter is a passed
in as a pointer.

Note that we can print out data values or pointers the address is printed as a big integer in hexadecimal
format (beginning with Ox). This example was compiled as 32-bit program, so our addresses are 32-bits. A
64-bit program will have longer addresses.
Lets look at the program output and reverse engineer
a drawing of the stack:
0xbf23ef18
address of a = 0xbf23eef0 x= 0xbf23ef14 5
address of b = 0xbf23eef4
address of q = 0xbf23eee4 y= 0xbf23ef10 7
address of r = 0xbf23eee0 answer=0xbf23ef0c 48
value at 0xbf23eef0 = 5
value at 0xbf23eef4 = 0xbf23ef10 0xbf23ef08
value at 0xbf23ef10 = 7 0xbf23ef04
value at 0xbf23eee4 = 6
value at 0xbf23eee0 = 8 0xbf23ef00
address of x = 0xbf23ef14 0xbf23eefc
address of y = 0xbf23ef10
address of answer = 0xbf23ef0c
0xbf23eef8
value at 0xbf23ef14 = 5 b= 0xbf23eef4 0xbf23ef10
value at 0xbf23ef10 = 7
value at 0xbf23ef0c = 48
a= 0xbf23eef0 5
0xbf23eeec
Note: The unlabeled portions in our diagram of the stack 0xbf23eee8
will include the frame pointer, the return address, temp
variables (complex C++ expressions turn into many smaller q= 0xbf23eee4 6
steps of assembly), space to save registers, and padding r= 0xbf23eee0 8
between variables to meet alignment requirements. 0xbf23eedc
Note: Different compilers and/or different optimization 0xbf23eed8
levels will produce a different stack diagram.

You might also like