Pointer arithmetic.

Slides:



Advertisements
Similar presentations
Recursive binary search
Advertisements

For loops.
Templates.
Introduction to classes
Binary and hexadecimal numbers
Static variables.
Pointers.
Dynamically allocating arrays
Anatomy of a program.
Binary search.
Command-line arguments
Pointer arithmetic.
Console input.
Dangling pointers.
This.
Arithmetic operators.
Sorted arrays.
Floating-point primitive data types
Dynamically allocating arrays within structures
Dynamic memory allocation
Break statements.
Linked Lists.
Wild pointers.
Console output.
The comma as a separator and as an operator
Bucket sort.
The call stack and recursion and parameters revisited
The ternary conditional operator
Dynamically allocating structures
Memory leaks.
Pushing at the back.
Bit-wise and bit-shift operators
Command-line arguments
Passing pointers as parameters to and from functions
Templated Linked Lists
Dynamically allocating arrays
Insertion sort.
Problems with pointers
A list-size member variable
Protecting pointers.
Dynamically allocating arrays
Code-development strategies
Throwing exceptions.
Anatomy of a program.
Console output.
Insertion sort.
Pointers as arguments and return values
Reference variables, pass-by-reference and return-by-reference
Addresses and pointers
Class variables and class functions
Operator overloading.
Dynamic allocation of arrays
Templates.
This.
Dynamic memory allocation
Insertion sort.
Sorted arrays.
Sorting algorithms.
Issues with classes.
Dangling pointers.
Counting sort.
Selection sort.
Searching and sorting arrays
Protecting pointers.
Arithmetic operators.
Data structures: class
An array class: constructor and destructor
This.
Recursive binary search
Presentation transcript:

Pointer arithmetic

Outline In this lesson, we will: Review that pointers store addresses of specific types See that we can add integers to addresses The result depends on the type See that you can also take differences of pointers Again, the result depends on the type

Pointer arithmetic Recall that: It makes no sense to add two addresses Pointers are addresses Addresses are positive integers It makes no sense to add two addresses int *p_value_1{new int{42}}; int *p_value_2{new int{91}}; std::cout << (p_value_1 + p_value_2) << std::endl; It’s like asking “What address is 111 Wellington St + 24 Sussex Dr?”

Pointer arithmetic You can, however, ask “What is the next address?” However, the next address isn’t always the next chronologically What address follows 24 Sussex Dr.? The French Embassy at 42 Sussex Dr. 25 Sussex Dr. does not exist What address comes two before 24 Sussex Dr.? The South African High Commission at 15 Sussex Dr.

Pointer arithmetic Suppose you have an array: int *a_data{new int[20]{}}; std::cout << a_data << std::endl; Suppose that 'a_data' is assigned 0x93a3d0 Question: What is the value of a_data + 1?

Next address Let’s try it out: Output a_ray == 0x1fb1010 #include <iostream> int main(); int main() { int *a_ray{new int[10]}; std::cout << "a_ray == " << a_ray << std::endl; std::cout << "a_ray + 1 == " << (a_ray + 1) << std::endl; delete[] a_ray; a_ray = nullptr; return 0; } Output a_ray == 0x1fb1010 a_ray + 1 == 0x1fb1014

Next address Let’s try it with an array of double: Output #include <iostream> int main(); int main() { double *a_ray{new double[10]}; std::cout << "a_ray == " << a_ray << std::endl; std::cout << "a_ray + 1 == " << (a_ray + 1) << std::endl; delete[] a_ray; a_ray = nullptr; return 0; } Output a_ray == 0x1af7010 a_ray + 1 == 0x1af7018

Next address Recall that The compiler says: An int occupies 4 bytes A double occupies 8 bytes The compiler says: a_ray is the address of a double, so the next double is 8 bytes after the current address…

The kth address We can even walk though an array: Output #include <iostream> int main(); int main() { double *a_ray{new double[6]{0, 1, 2, 3, 4, 42}}; for ( std::size_t k{0}; k < 6; ++k ) { std::cout << (a_ray + k) << " stores the value " << *(a_ray + k) << std::endl; } delete[] a_ray; a_ray = nullptr; return 0; Output 0x1af0010 stores the value 0 0x1af0018 stores the value 1 0x1af0020 stores the value 2 0x1af0028 stores the value 3 0x1af0030 stores the value 4 0x1af0038 stores the value 42

Array entries The following two statements are equivalent: a_ray[k] *(a_ray + k) The first says: Access the kth entry of the array a_ray The second says: Find the address of k entries beyond a_ray and access that address In general, the first is much easier to read

Array entries The following two statements are also equivalent: &( a_ray[k] ) a_ray + k The first says: Find the address of the kth entry of the array a_ray The second says: Find the address of k entries beyond a_ray In this case, the second is easier to read!

Array entries Now you understand why all arrays in C++ start at index 0 a_ray[0] == *(a_ray + 0) and *(a_ray + 0) == *a_ray The decision to start array indices at 0 was entirely practical

Walking through arrays We can walk through arrays: #include <iostream> int main(); int main() { int *a_ray{new int[6]{0, 1, 2, 3, 4, 42}}; for ( int *p_ray{a_ray}; p_ray < a_ray + 6; ++p_ray ) { std::cout << p_ray << " stores the value " << *p_ray << std::endl; } delete[] a_ray; a_ray = nullptr; return 0; Increment 'p_ray' to the next valid address of an int Output 0x183a010 stores the value 0 0x183a014 stores the value 1 0x183a018 stores the value 2 0x183a01c stores the value 3 0x183a020 stores the value 4 0x183a024 stores the value 42

Walking through arrays Question: Should you, or should you not delete p_ray? Why? #include <iostream> int main(); int main() { int *a_ray{new int[6]{0, 1, 2, 3, 4, 42}}; for ( int *p_ray{a_ray}; p_ray < a_ray + 6; ++p_ray ) { std::cout << p_ray << " stores the value " << *p_ray << std::endl; } delete[] a_ray; a_ray = nullptr; return 0;

Pointer differences Remember, it makes no sense to ask “What is 111 Wellington St + 24 Sussex Dr?” On the other hand… 1100 S Ocean Blvd + 1600 Pennsylvania Avenue NW == &hell You might ask, however, “How many buildings away is Sir Winston Churchill Secondary School from Denis Morris Catholic High School?” Given the addresses 101 Glen Morris Dr and 40 Glen Morris Dr: The wrong answer is 61… The right answer is 3

Pointer differences Similarly, we may ask about a difference of addresses: #include <iostream> int main(); int main() { double *a_ray{new double[6]{0, 1, 2, 3, 4, 42}}; double *p_1{&(a_ray[2])}; double *p_2{&(a_ray[5])}; std::cout << p_1 << " " << p_2 << std::endl; std::cout << (p_1 - p_2) << std::endl; std::cout << (p_2 - p_1) << std::endl; delete[] a_ray; a_ray = nullptr; return 0; } Output 0xd02020 0xd02038 -3 3

Pointer differences Question: What is the type of a pointer difference? Remember that pointers are addresses The type std::size_t depends on the address size This type is unsigned—addresses are unsigned A reasonable approximation would be a signed equivalent of std::size_t Such a type is defined: std::ptrdiff_t Literally, a pointer-difference type This is unique for operators: the return type is different from the types of the operands Not a problem, a function can have a return type that is different from its arguments, too

Pointer differences Question: What is the type of a pointer difference? #include <iostream> int main(); int main() { double *a_ray{new double[6]{0, 1, 2, 3, 4, 42}}; double *p_1{&(a_ray[2])}; double *p_2{&(a_ray[5])}; std::ptrdiff_t diff = p_1 - p_2; std::cout << diff << std::endl; delete[] a_ray; a_ray = nullptr; return 0; } Output -3

Pointer differences The memory occupied by a pointer, std::size_t and std::ptrdiff_t are all the same: #include <iostream> int main(); int main() { std::cout << sizeof( int * ) << std::endl; std::cout << sizeof( std::size_t ) << std::endl; std::cout << sizeof( std::ptrdiff_t ) << std::endl; return 0; } Output on ecelinux: 8

Summary Following this lesson, you now Know that integers can be added to addresses and the difference depends on the type Understand that array[k] and *(array + k) are equivalent The first, however, is clearer to read Know that we can calculate the difference between addresses

References [1] No references?

Colophon These slides were prepared using the Georgia typeface. Mathematical equations use Times New Roman, and source code is presented using Consolas. The photographs of lilacs in bloom appearing on the title slide and accenting the top of each other slide were taken at the Royal Botanical Gardens on May 27, 2018 by Douglas Wilhelm Harder. Please see https://www.rbg.ca/ for more information.

Disclaimer These slides are provided for the ece 150 Fundamentals of Programming course taught at the University of Waterloo. The material in it reflects the authors’ best judgment in light of the information available to them at the time of preparation. Any reliance on these course slides by any party for any other purpose are the responsibility of such parties. The authors accept no responsibility for damages, if any, suffered by any party as a result of decisions made or actions based on these course slides for any other purpose than that for which it was intended.