Pointers Part I (Fundamentals) Computer Programming I Lecture 12 Copyright (C) 2004 by Wong Ya Ping updated : ver080415(KCLee)

Презентация:



Advertisements
Похожие презентации
Operator Overloading Customised behaviour of operators Chapter: 08 Lecture: 26 & 27 Date:
Advertisements

Unit II Constructor Cont… Destructor Default constructor.
Data Variable and Pointer Variable Pass by Reference Pointer Arithmetic Passing Array Using Pointers Dynamic Allocation.
What to expect? How to prepare? What to do? How to win and find a good job? BUSINESS ENGLISH COURSE NOVA KAKHOVKA GUMNASUIM 2012.
Учимся писать Эссе. Opinion essays § 1- introduce the subject and state your opinion § 2-4 – or more paragraphs - first viewpoint supported by reasons/
Lesson 2. How to say hello & goodbye ?. When we first meet someone whether it is a person we know or someone we are meeting for the first time, we will.
1/27 Chapter 9: Template Functions And Template Classes.
REFERENCE ELEMENTS 64. If your REFERENCE ELEMENTS toolbar is not in view and not hidden, you can retrieve it from the toolbars menu seen here. 65.
Data Types in C. A Data Type A data type is –A set of values AND –A set of operations on those values A data type is used to –Identify the type of a variable.
Ecology and fashion. Project was done by Borodina Ludmila from 10 B.
Pointers Part II (Dynamic Memory Allocation) Allocation) Lecture 13 Copyright (C) 2004 by Wong Ya Ping updated : ver080916(KCLee) Computer Programming.
How can we measure distances in open space. Distances in open space.
The most important technological inventions Think of as many words as possible related to the topic Think of as many words as possible related to the.
Indirect Questions How do you make indirect questions? When do you use this grammar?
Inner Classes. 2 Simple Uses of Inner Classes Inner classes are classes defined within other classes The class that includes the inner class is called.
How to crack technical interview ? Yogesh Mehla. Many of my friends who are technically good and even great, but they are unable to crack their first.
DRAFTING TECHNIQUES I 136. Here is a basic shape. From here, we will do some advanced drafting once we put this shape on a sheet as a drawing. Select.
Goals and values. What are goals? Goals can be anything you want to achieve in a short period of time or in a long time period. Eg, get better grade,
Introduction Expressing opinion Giving arguments (not less than 3) Justifications (explanations, examples) Conclusion.
1. Do you like your school? I should say that I love my school a lot. For me its not only a building where I get knowledge, but also the second home of.
Транксрипт:

Pointers Part I (Fundamentals) Computer Programming I Lecture 12 Copyright (C) 2004 by Wong Ya Ping updated : ver080415(KCLee)

Lecture Objectives To define the basic concepts of pointers in C/C++, their declaration and use. To illustrate the applications of pointer variables and pointer operators. To understand the relationship between pointers and arrays. To understand what is pointer arithmetic. 2

Problem How can I write a function that returns more than one value? How can I write a function that modifies the values pass to it as parameters? In C++, we know this can be achieve using reference variables. How about in C, which is a subset of C++? We also know that when we pass an array to a function, the function may change the values stored in the array, but how does it work actually? 3

#include using namespace std; void swap( int& a, int& b ) { int temp = a; a = b; b = temp; } int main() { int a = 3, b = 9; cout << a << " " << b << endl; swap(a, b); cout << a << " " << b << endl; system("pause"); } Let's consider a function that swaps two integers, in C++ we can write the function as below: This function uses passing-by- reference as parameter passing, there are 2 questions to be asked here: 1. What mechanism is used in this function? 2. Reference variable is a concept in C++, how does C program pass parameters for similar purposes? output: 4

'Z' Before we move on, let's represent the memory of the computer: memory addresses = a unique index for each bytes a lot more memory in betweenmore memory a character occupies only one byte of memory a 32-bits integer occupies 4 bytes a 32-bits floating points values occupies 4 bytes NOTE : the actual address of the data is usually determined by the computer, nevertheless, we usually put some values here as example for the purpose of illustration only. Memory Representation 5

'Z' Instead of represent the whole block of memory, we may just use diagram below: only specify the first address (We can draw it vertically as well) Sometime we may not want to draw out actually how many bytes the data occupies, we can do just as follows: Even though only one box is used, it does not imply the data represented using one byte

Likewise, we may represent a block of memory this way : Again, even though only one box is used, it does not imply that the data represented is using only one byte. Based on the way we write the example addresses, we can know each box actually represent 4 bytes. The address written here actually is the address of the first byte of the data (which is a 32-bits floating point value.) Based on the way we write the example addresses, we can know each box actually represents 4 bytes. The address written here actually is the address of the first byte of the data (which is a 32-bits integer value.) 7

These addresses are just examples for the purpose of illustration, we may omit them if it is not needed. 327 int num1 = 25; int num2 = 327; int num3; float f1 = 345.2; char ch1 = 'A'; Consider the declaration below: 'A' 2432 num1 num2 num3 f1 [float] ch1 [char] We can write 65 here too, as this is the numerical equivalent of the character 'A. These are the variable names. We leave this out because the value is undetermined. The actual position of where we put the addresses and variable names is not important. We represent them as below: For clarity, we may want to write the data type here. 8

Numbers Swapping Function Problem #include using namespace std; void tryToSwap( int a, int b ) { int temp; temp = a; a = b; b = temp; } int main() { int a = 3, b = 9; tryToSwap(a, b); cout << a << " " << b; } a b a b 2644 temp function tryToSwap() function main() Lets consider how we can swap numbers without using references, (especially if you are using C). We can consider the program below, which obviously will not work. Let's try to trace and see what happens: NOTE : Even though the variable names a and b are unique within each of the functions, the memory addresses are unique throughout the whole system, the actual memory are not local to the functions but accessible by all the functions if the addresses are known. 9

a b a b 2644 temp function tryToSwap() function main() Next... The value of variables a and b in main() are being COPIED to variables a and b of function tryToSwap()... #include using namespace std; void tryToSwap( int a, int b ) { int temp; temp = a; a = b; b = temp; } int main() { int a = 3, b = 9; tryToSwap(a, b); cout << a << " " << b; } 10

a b a b temp function tryToSwap() function main() Next... the value of variables a and variable b of function tryToSwap() are being swapped... #include using namespace std; void tryToSwap( int a, int b ) { int temp; temp = a; a = b; b = temp; } int main() { int a = 3, b = 9; tryToSwap(a, b); cout << a << " " << b; } 11

a b a b 2644 temp function tryToSwap() function main() Next... After the function tryToSwap() is executed, nothing happens to variables a and b of main() and their value remain unchanged. #include using namespace std; void tryToSwap( int a, int b ) { int temp; temp = a; a = b; b = temp; } int main() { int a = 3, b = 9; tryToSwap(a, b); cout << a << " " << b; } 12

a b function main() So, what do we do? It would be nice if we can do something like this: Function main() says to function tryToSwap() : Hey, I have 2 values I want to swap, I am not going to merely give you the values, I am letting you know their addresses, so please get the value from those addresses, swap them and put the new values back to those memory.... The story continues..... function tryToSwap() 13

a b function main() continues..... Function tryToSwap() says : Alright, let me store up these two addresses in two variables called pA and pB. function tryToSwap() pA pB 14

a b function main() continues..... Function tryToSwap() says : Now... let me read the value in the addresses as indicated by pA and pB and copy these values into my own variable called x and y. Then after swapping x and y, I will copy the values back to the location as indicated by pA and pB. function tryToSwap() pA pB x y 2763 temp 15

a b function tryToSwap() function main() continues..... Function main() says to tryToSwap() : Horray, it is like magic, my value of a and b has been swapped! Thank you pA pB x y 2763 temp 16

Let's put the story aside first: Let's introduce some new symbols and operators in C: 1432 a [ int ] int a; If I want to store the address of a, what do I do? int* p; If I want to store an integer, I store it in a integer data type called 'int'... but if I want to store an address, then I must have some kind of data type specially for storing address. Here it is : 4324 p [ int* ] What? Don't worry, just accept it for the time being that this is the data type for storing addresses, the type is int*. we get... Basics of Pointers 17

1432 a [ int ] int a; int* p; 4324 p [ int* ] If we declare this.. we get... 18

a [ int ] int a; int* p; a = 3; p = &a; p [ int* ] Lets assign a value of 3 to variable a. Since p is meant for storing address, we store the address of variable a into variable p. we get... This symbol & is called ampersand, it is an operator which returns an address (in this case, the address of variable a.) The address of variable a is now stored here. We now say that the value of variable p is the address of variable a. Take note and DO NOT BE CONFUSED, since p is itself a variable, it has its own address too. In our example above : p has a value of 1432, which is to say, p has the value of &a (i.e. address of a ) BUT &p has a value of 4324 [we are not using this for our example here]. 19

p is called a pointer, just as a home address points to a physical house location, a pointer in C points to a values stored elsewhere in the computer memory, in our case, p actually points to the value of variable a. We can visualize it as follows: a [ int ] p [ int* ] 3 a [ int ] p [ int* ] Very often, we will just leave out the example address as we don't really need to know its actual values. int a; int* p; a = 3; p = &a; 20

a [ int ] int a = 3; int* p; p = &a; 1432 p [ int* ] we get... What can we do with pointers? Well, now we know that given the declaration: p is a pointer pointing to the value of variable a, to access (to get the value or to change the value) the value of variable a indirectly using the pointer, we can use a special operator in C/C++ called indirection operator (or dereference operator), the symbol for the unary operator is '*' (asterisk). p being &a; then... *p equals to a; Whenever we have : NOTE : This '*' has nothing to do with the '*' in the pointer declaration (as in int* p; ). 21

If we want to read the value pointed by p, we can do this: int a = 3; int* p; p = &a; cout << *p << endl; cout << *p + 2 << endl; a = 10; cout << *p << endl; output: Here we are accessing the value of variable a indirectly through the pointer, thus the ' * ' operator is called indirection operator. It is also called dereferencing operator as we are accessing the value by referencing another variable (which is the pointer). 22

If we want to change the value pointed by p, we can do this: int a = 3; int* p; p = &a; *p = 9; cout << a << endl; cout << *p << endl; 9999 output: a [ int ] 1432 p [ int* ] *p = 9 23

More example: output: #include using namespace std; int main() { int x = 10, y = 20; int* pX; int* pY; pX = &x; pY = &y; cout << x << " " << y << " " << *pX << " " << *pY << endl; *pX = 32; /* equals to x = 32; */ *pY = 45; /* equals to y = 45; */ cout << x << " " << y << " " << *pX << " " << *pY << endl; x = 31; y = 24; cout << x << " " << y << " " << *pX << " " << *pY << endl; *pX = *pY + 5; /* equals to x = y + 5 */ cout << x << " " << y << " " << *pX << " " << *pY << endl; } 24

More example: output: #include using namespace std; int main() { int x = 10, y = 20; int* p1; int* p2; p1 = &x; p2 = &y; cout << x << " " << y << " " << *p1 << " " << *p2 << endl; (*p1)++; /* equals to x++; */ *p2 += 4; /* equals to y+=4; */ cout << x << " " << y << " " << *p1 << " " << *p2 << endl; p1 = p2; cout << x << " " << y << " " << *p1 << " " << *p2 << endl; p2 = &x; cout << x << " " << y << " " << *p1 << " " << *p2 << endl; } Must have the brackets, without them it means very different thing. You can assign one pointer to another as long as they are of the same type. p1 now also points to y. You can always reassign a pointer to point to another variable as long as it is not a constant pointer (more on this later.) 25

Warning !!! int main() { int *p; *p = 10; } What's wrong with this code? p [ int* ] ? Where does the value 10 end up? Since we did not initialize p to point to anything, the value in p is undetermined and unpredictable, the above code may end up corrupting some crucial part of the operating system and it may hang your system. Therefore always save your programs regularly when programming with pointers, if you make some mistake, it may very well hang your whole system. 26

More on Pointer Declaration int* p; The ' * ' here indicates that we declare p as a pointer. Even though the same symbol is used for the deferencing operator, they are not the same. Don't get confused. This tells us that the memory it is pointing to contains data of type int. It affects how the dereferencing operator works and also the pointer arithmetic (to be discussed later when we discuss "pointer and array".) We can declare a pointer type which points to any data type in C/C++ including user-defined type. 27

output: #include using namespace std; int main() { int i = 6; float f = 4.3f; double d = 34.1; int* pI = &i; float* pF = &f; double* pD = &d; cout << i << " " << f << " " << d << endl; cout << *pI << " " << *pF << " " << *pD << endl; i *= 2; f += 10.0f; d = 31.4; cout << i << " " << f << " " << d << endl; cout << *pI << " " << *pF << " " << *pD << endl; (*pI)++; *pF = 3.1f; *pD /= 2.0; cout << i << " " << f << " " << d << endl; cout << *pI << " " << *pF << " " << *pD << endl; } More example: 28

int *p1, *p2, m, n, *q; To declare a few pointers of the same type in one line, you can do this: equivalent int *p1; int *p2; int m,n; int *q; int n; int *p = &n; equivalent int n; int *p; p = &n; 29

int* p1, p2; equivalent int *p1; int p2; int* p1, p2; int *p1; int *p2; int n; int *p = n; int n; int *p; p = n; NOT equivalent The C++ compiler will give you a syntax error saying that this is invalid conversion. In C, the compiler will most likely give you a warning only. NOT equivalent 30

#include using namespace std; void swap( int *pA, int *pB ) { int temp; temp = *pA; *pA = *pB; *pB = temp; } int main() { int a=3, b=9; swap( &a, &b ); cout << a << " " << b; } 9 3 output: Solution to the Numbers Swapping Function 31

Passing Parameters to Function By Reference a b void swap( int* pA, int* pB ){ int temp = *pA; *pA = *pB; *pB = temp; } int main() { int a=3,b=9; swap(&a,&b); } When function main() calls function swap(), it passes the value of &a and &b to the swap() function as input. &a is the address of variable a while &b is the address of variable b Then

a b void swap( int* pA, int* pB ){ int temp = *pA; *pA = *pB; *pB = temp; } int main() { int a=3,b=9; swap(&a,&b); } pA pB When the function swap() is being executed, it will copy the values pass to it into its own variables as defined in its function parameter list. That means in effect, variable pA will take the value of &a and variable pB will take the value &b, it is equivalent of stating : then... pA = &a; pB = &b; Therefore

a b void swap( int* pA, int* pB ){ int temp = *pA; *pA = *pB; *pB = temp; } int main() { int a=3,b=9; swap(&a,&b); } pA pB temp In effect, in function swap(), pA now directly points to variable a of main() and pB directly points to variable b of main(), that means: Therefore... *pA now will be equivalent to variable a in main(), and *pB now will be equivalent to variable b in main(). Continues... temp = *pA is equivalent to temp = a (of main() ) 34

a b void swap( int* pA, int* pB ){ int temp = *pA; *pA = *pB; *pB = temp; } int main() { int a=3,b=9; swap(&a,&b); } pA pB temp Continues... *pA = *pB is equivalent to a = b in main() 35

a b void swap( int* pA, int* pB ){ int temp = *pA; *pA = *pB; *pB = temp; } int main() { int a=3,b=9; swap(&a,&b); } pA pB temp Therefore, the values are now swapped! continues... *pB = temp is equivalent to b = 3 in main() 36

37 p == &n *p == n Summary so far: Whenever we have : then : True. *p == n p == &n Whenever we have : then : True? Assuming p is a pointer (type [ int *] ) and n is an integer (type is [ int] )

38 p == &n *p == n Whenever we have : then : True. *p == n p == &n Whenever we have : then : Not necessarily true! Why? Assuming p is a pointer (type [ int *] ) and n is an integer (type is [ int] )

39 p == &n *p == n Whenever we have : then : *p == n p == &n Whenever we have : then : n [ int ] 1432 p [ int* ] x [ int ] 1560 p [ int* ] n [ int ] Assuming p is a pointer (type [ int *] ) and n is an integer (type is [ int] )

40 *&p == p &*p == p The following are always true: Assuming p is a pointer (type [ int *] ) and n is an integer (type is [ int] ) *&n == n n [ int ] 1432 p [ int* ] 1320 &*n == n not valid

41 #include void swap( int* pA, int* pB ) { int temp = *pA; *pA = *pB; *pB = temp; } void main() { int a=3,b=9; swap(&a,&b); printf("a = %d, b = %d\n",a,b); } Parameters passing by address: NOTE: We refer to this as "parameters passing by reference", strictly speaking, there is no such thing as pass-by-reference in C (many books actually got this wrong!), but there is in C++. In C, strictly speaking, everything is exclusively pass-by-value, in the above case, the value is an address, therefore it is called "pass-by-address", sometime we say that we simulate "pass-by-reference" using this. In C++, pass-by-reference using reference is preferred over using pointers as it is more straight forward and less error-prone. Copies the address of b and assigns this address as the value of pointer pB.

42 const int n = 3; int* p; p = &n; Pointers and Constants Pointer to Constant Valid? n is a constant.

43 const int n = 3; int* p; p = &n; Not valid !! const int n = 3; int* p; p = &n; *p = 8; consider : Here, we attempt to change the value of n, BUT n is a constant! Therefore, a pointer of type [ int * ] CANNOT point to a value of type [ const int ]. What if I just want to read (using a pointer) the value of the constant integer without changing its value? INVALID

44 int z = 1; const int n = 3; const int* p; p = &n; *p = 5; z = *p + 3; p = &z; *p = 10; z = 10; Pointer p has the type [const int *]. VALID. A pointer of type [const int *] can be used to point to a variable of type [const int]. INVALID: A pointer of type [const int *] CANNOT be used to MODIFY the value it is pointing to. VALID. A pointer of type [const int *] CAN be used to READ the value it is pointing to. VALID. A pointer of type [const int *] CAN be used to point to a variable of type [int], even though the integer is not a constant. Take note, p itself is NOT constant. CONCLUSION : A pointer of type [const int *] is a Read-Only Pointer. VALID. Even though we cannot modify z via the pointer, z still can be modify by other means since it is not a constant.

45 int m, n; int * const p = &n; *p = 10; n = 5; p = &m; Constant Pointer p has a type [int * const] and this means it is a constant pointer, the value it is pointing to is not constant, but p itself is a constant and cannot be changed. p must be initialized, just as any constant variable must be initialized. VALID. The value pointed by p is not constant, also it is VALID because the type of p is not [const int *]. VALID. because n is not a constant. INVALID: A pointer of type [int * const] is a constant pointer and CANNOT be modified (cannot be reassigned to point to another variable).

46 int m, n; const int * const p = &n; *p = 10; n = 5; p = &m; p has a type [const int * const] and this means it is a constant pointer, the value it is pointing to also IS a constant. p must be initialized, just as any constant variable must be initialized. INVALID: A pointer of type [const int * const] is a constant pointer and CANNOT be modify (cannot be reassigned to point to another variable). Constant Pointer to Constant = VALID. Even though we cannot modify n via the pointer, n can still be modified by other means since it is not a constant. INVALID: A pointer of type [const int * const] CANNOT be used to MODIFY the value it is pointing to. pointer to constant + constant pointer

47 Summary of Pointers and Constants Constant Pointer To Constant Constant Pointer Pointer To Constant const int * const int * const const int * const int * const p = &n; or int const * const p = &n; int * const p = &n; const int * p; p = &n; or int const * p; p = &n; Assuming n and m is an integer (type is [ int] ) Name Data Type Sample Declaration Must Optional Need Initialization? Continues… NOTE: Here we use int as example, it is valid for other data type as well.

48 const int * const p = &n; or int const * const p = &n; int * const p = &n; const int * p; p = &n; or int const * p; p = &n; Sample Declarations No n must be constant? from previous page... Valid Invalid Valid Invalid Valid Invalid Valid Invalid If n is a constant, is the sample declaration valid? Can p be reassigned using p = &m ? Is : *p = 3; valid?

49 const int * const p = &n; The data it is pointing to is constant, thus *p = 3; is not valid. The data type of the data it is pointing to. This is a pointer. The pointer itself is constant, thus p = &m; is not valid. Need initialization if pointer is constant.

50 More on Pointer Type int* p; If a pointer is simply a variable that contains an address value, why do we need to specify the data type before the ' * ' symbol? What is the purpose? float* p; char* p;

51 NOW, p1 and p2 point to the same memory address, why is it that the values dereferenced by * p1 and *p2 are different? int n = ; int* p1 = &n; short* p2 = reinterpret_cast ( &n ); cout << "p1=" << p1 << " " << "p2=" << p2 << endl; cout << "*p1=" << *p1 << " " << "*p2=" << *p2 << endl; First Purpose: Pointers p1 and p2 both point to the location of where n is being stored. However, p1 has type [ int* ] and p2 has type [ short* ]. p1=0x1ff3 p2=0x1ff3 *p1= *p2=928 output: Address value displayed in hexadecimal NOTE: type [ short ] is same with type [ short int ] We can't use static_cast here because type [ int* ] is not related to type [ short* ], any conversion will not be portable and platform dependent. To force conversion, we use reinterpret_cast, which is non-portable and non-safe.

52 p1 is a pointer of type [int*], the dereference operator '*' will interpret *p1 as a value of type [ int ] which can be constructed using 4 bytes since it takes 4 bytes to store a int in our compiler. Therefore it will read 4 bytes starting from address 1FF3 and interpret it as: 0 x 2^ x 2^ x 2^ = ! On the other hand, p2 is a pointer of type [ short* ], the dereference operator '*' will interpret *p2 as a value of type [ short ] which can only be constructed using 2 bytes in our compiler, therefore it will read 2 bytes starting from address 1FF3 and interpret it as: 3 x 2^ = 928! FF3 1FF2 1FF3 1FF4 1FF5 1FC0 n [int] p2 [short*] 1FF3 1FA0 p1 [int*] Explanation of the First Purpose: int n = ; int* p1 = &n; short* p2 = reinterpret_cast ( &n ); cout << "p1="<<p1<<" "<<"p2="<<p2<<endl; cout << "*p1="<<*p1<<" "<<"*p2="<<*p2<<endl; 0 1FF6

53 Therefore, the First Purpose is: int* p; The pointer data type determined how the dereference (' * ') operator is interpreted, it simply states how the pointer should interpret the memory it is pointed to and how many bytes should be used to interpret the value, the pointer is simply the address of the starting memory location of the data pointed to, the actual value of the data may need to be constructed using more bytes starting from that address. float* p; char* p;

54 int* p; The Second Purpose has something to do with what we called Pointer Arithmetic. See the next section. float* p; char* p; Second Purpose: By The Way: We are declaring p to be a derived type based on a fundamental type. Here int, char and float are fundamental types, other fundamental types are void, double, long double as well as signed and unsigned char, short and long. Here the derived type we are declaring is a pointer, other derived types are constant, array, function and structure.

55 Pointers and Arrays The Subscript Operator ( Operator [ ] ) Observe that the pointer p behaving just like the array n Why? int n[5] = {1,4,3,8,7}; int *p; p = n; cout << n[0] << " " << n[1] << " " << n[2] << endl; cout << p[0] << " " << p[1] << " " << p[2] << endl; p[0]=43; p[1]=82; p[2]=58; cout << n[0] << " " << n[1] << " " << n[2] << endl; cout << p[0] << " " << p[1] << " " << p[2] << endl; output: What does this mean?

56 int n[5] = {1,4,3,8,7}; int *p; p = n; cout <<n[0]<<" "<<n[1]<<" "<<n[2]<<endl; cout <<p[0]<<" "<<p[1]<<" "<<p[2]<<endl; p[0] = 43; p[1] = 82; p[2] = 58; cout <<n[0]<<" "<<n[1]<<" "<<n[2]<<endl; cout <<p[0]<<" "<<p[1]<<" "<<p[2]<<endl; Since the name of the array is actually the address of the first element in the array and we have assigned the same address value to pointer p, pointer p now behave and can be used just like an array. Assigns the value of n (which is the address of the first element of the array) to p p [ int* ] n [ int[5] ] The name of the array is actually a constant pointer pointing to the address of the first element in the array! n[0] [int] n[1] [int] n[2] [int] n[3] [int] n[4] [int]

57 int n[5] = {1,4,3,8,7}; int *p; p = n; p == &n[0] Therefore, given: the following are always true: p[i] == n[i]*p == n[0] OR int n[5] = {1,4,3,8,7}; int *p; p = &n[0];

58 *p == p[0];p == &p[0]; Also, given any pointer p : the following are always true:

59 int n[5] = {1,4,3,8,7}; int *p; p = &n[2]; cout <<n[0]<<" "<<n[1]<<" "<<n[2]<<endl; cout <<p[0]<<" "<<p[1]<<" "<<p[2]<<endl; p[0] = 43; p[1] = 82; p[2] = 58; cout <<n[0]<<" "<<n[1]<<" "<<n[2]<<endl; cout <<p[0]<<" "<<p[1]<<" "<<p[2]<<endl; Observe that the pointer p is still behaving just like an array, but this time the array it is simulating seems to start only at element n[2] output: Now p is pointing at the address of the element 2 of the array n.

60 int n[5] = {1,4,3,8,7}; int *p; p = &n[2]; cout <<n[0]<<" "<<n[1]<<" "<<n[2]<<endl; cout <<p[0]<<" "<<p[1]<<" "<<p[2]<<endl; p[0]=43; p[1]=82; p[2]=58; cout <<n[0]<<" "<<n[1]<<" "<<n[2]<<endl; cout <<p[0]<<" "<<p[1]<<" "<<p[2]<<endl; now: p[0] is equivalent to n[2], p[1] is equivalent to n[3], p[2] is equivalent to n[4], p[i] is equivalent to n[i + 2] p [ int* ] n [ int[5] ] 7 n[0] [int] n[1] [int] n[2] [int] n[3] [int] n[4] [int]

61 int n[] = { 222, 333, 444 }; int *p1, *p2, i; p1 = &n[1]; p2 = p1 + 1; cout (p2) - reinterpret_cast (p1); Pointer Addition and Subtraction 1 output: ? What should be the output? Is it 1?

62 int n[] = { 222, 333, 444 }; int *p1, *p2, i; p1 = &n[1]; p2 = p1 + 1; cout (p2) - reinterpret_cast (p1); 4 output: The answer is 4 in our compiler, the answer may vary if you are using different platform (for example, you will get 2 in TCLite). BUT most likely the answer is NOT 1. WHY? OK. We will come back to this question later, let's just put it aside for awhile.

63 Remember that given a pointer, we can always treat the pointer as if it is an array starting at the memory it is pointed to. The data type of each elements in this "simulated" array will be determined by the actual pointer type. In the above example, we can see that whenever we add a number to the pointer, we are actually making the pointer to move a certain number of position in the simulated "array". int n[] = {1,4,3,8,7,2,5,9}; int *p; p = &n[1]; cout <<p[0]<<" "<<p[1]<<" "<<p[2]<<endl; p++; cout <<p[0]<<" "<<p[1]<<" "<<p[2]<<endl; p+=2; cout <<p[0]<<" "<<p[1]<<" "<<p[2]<<endl; p--; cout <<p[0]<<" "<<p[1]<<" "<<p[2]<<endl; p-=3; cout <<p[0]<<" "<<p[1]<<" "<<p[2]<<endl; output: Consider:

64 Here we do the similar thing as in the previous slide, here we do not change the value of the pointer p (that is, pointer p always points at the same memory address), instead we dereference the memory location which is a certain number of elements relative to the pointer p. int n[] = {1,4,3,8,7,2,5,9 }; int *p; p = &n[1]; cout <<p[0]<<" "<<p[1]<<" "<<p[2]<<endl; cout << *p << endl; cout << *(p+1) << endl; cout << *(p+2) << endl; cout <<p[3]<<" "<<p[4]<<endl; *(p+3) = 23; *(p+4) = 76; cout <<p[3]<<" "<<p[4]<<endl; output: Consider:

65 Consider: p [ int* ] n [ int[6] ] n[0] [int] n[1] [int] n[2] [int] n[3] [int] n[4] [int] n[5] [int] p+1 p+2 You would notice by now, if you add 1 to a pointer, it only means moving 1 position (that is 1 element of the array) in the "simulated" array the pointer is pointing to, BUT the memory address actually increases by 4 in the above example! int n[] = {1,4,3,8,7,2 }; int *p; p = &n[1]; cout <<p[0]<<" "<<p[1]<<" "<<p[2]<<endl; cout << *p << endl; cout << *(p+1) << endl; cout << *(p+2) << endl; cout <<p[3]<<" "<<p[4]<<endl; *(p+3) = 23; *(p+4) = 76; cout <<p[3]<<" "<<p[4]<<endl;

66 int n[] = {1,4,3,8,7,2 }; int *p = &n[1]; p [ int* ] n [ int[6] ] 7 p[0] [int] p[1] [int] p[2] [int] p[3] [int] 2 p[4] [int] p+1 p+2 REMEMBER: Given: p+i will point to the array element i position relative to p. p+i does NOT mean add i to the address value of p. The exact increment of the address value is equals : i x sizeof(datatype). The Second Purpose (mentioned earlier) of the pointer data type is in order to know the size of each element of the "simulated" array so that the calculation for the actual memory location can be achieved given a relative position in the array

67 Back to the earlier question: int n[] = { 222, 333, 444 }; int *p1,*p2, i; p1 = &n[1]; p2 = p1 + 1; cout (p2) - reinterpret_cast (p1) << endl; cout << p2 - p1 << endl; 4141 output: Pointer Arithmetic : + and - operator operates on the relative position of each element in the array Numerical Arithmetic : Since both address values have been type- casted as int values, the arithmetic operator (minus operator in this case) here operates on the actual numerical address values of the addresses.

68 Another conclusion we can draw from this: p + i&p[i] equivalent *(p + i)p[i] equivalent Given a pointer or an array p of any data type: i is an offset from the pointer p. Thus also: p == &p[0]*p == p[0]

69 If ptr is declared as : datatype * ptr; then, ( ptr + i ) points to the calculated location of: ptr plus numerically i * sizeof(datatype)

70 In fact in C/C++ : n[i]i[n] equivalent *(n+i) equivalent The subscript operator is commutative (that is a+b is always equal to b+a), this is actually valid, but it is rather peculiar. Do not use this!

71 *(p+i) *p+i NOT equivalent *p++ (*p)++ NOT equivalent

72 Major difference between pointer and array: int m[] = {3,1,4,6,5,3 }; int n[] = {1,4,3,8,7,2 }; int *p; p = &m[1]; cout << *p << endl; cout << *(p+1) << endl; *(p+3) = 23; cout << *n << endl; cout << *(n+1) << endl; *(n+3) = 23; n = &m[1]; Valid. INVALID: Because n is similar to a constant pointer and you cannot change the address values of n.

73 Arrays and Function Calls #include using namespace std; int arraySum( int array[], int size ) { int sum = 0; for (int i = 0; i < size; i++) sum += array[i]; return sum; } int main() { int m[] = {1,5,4,3,2}; int sum; sum = arraySum( m, 5 ); cout << sum; system("pause"); } Only the name of the array is passed into the function, in fact, it is the address of the first element of the array is being passed into the function. Instead of writing int array[] in the parameter list, we can also write it as int* array. This means that the function simply accept an address. In fact, even if we write int array[] the compiler always convert it to int * array. However, you should write int array[] for clarity if you are really expecting an array. It should be clear by now: why the values of the array can be changed when the array is being passed into a function. The reason is that, it is the address of the first element that is being passed. Example:

74 Examples #include using namespace std; int myStrLen( char str[] ) { int i; for (i = 0; str[i] != '\0'; i++) { //empty } return i; } int main() { char str[] = "hello world"; cout << myStrLen( str ); } 11 output: To write our own function to compute the length of a C-string, this is one possible solution: Iterates the pointer p through every element in the C-string (array of char) until the ' \0 ' is found.

75 #include using namespace std; int myStrLen( char str[] ) { char *p; for (p = str; *p != '\0'; p++) { //empty } return p - str; } int main() { char str[] = "hello world"; cout << myStrLen( str ); } Iterates the pointer p through every element in the C-string (array of char) until the ' \0 ' is found. 11 output: Improved version: How does this improve the previous version?

76 int myStrLen( char str[] ) { int i; for (i=0; str[i] != '\0'; i++) { //empty } return i; } Need to evaluate this, str[i] is evaluated as *(str+i). int myStrLen( char str[] ) { char *p; for (p = str; *p != '\0'; p++) { //empty } return p - str; } First version:Improved version: Incrementing a pointer is much more efficient.

77 #include using namespace std; int myStrCmp( char s1[], char s2[] ) { int i = 0; while (true) { if(s1[i] != s2[i]) return s1[i] - s2[i]; if(s1[i] == '\0' && s2[i] == '\0') return 0; i++; } int main() { char str1[] = "good"; char str2[] = "afternoon"; char str3[] = "morning"; char str4[] = "morning"; cout << myStrCmp( str1, str2 ) << endl; cout << myStrCmp( str2, str3 ) << endl; cout << myStrCmp( str3, str4 ) << endl; } output: To write our own function to compare C- strings, this is one possible solution:

78 #include using namespace std; int myStrCmp( char s1[], char s2[] ) { char *p1 = s1, *p2 = s2; while (true) { if(*p1 != *p2) return *p1 - *p2; if(*p1 == '\0' && *p2 == '\0') return 0; p1++; p2++; } int main() { char str1[] = "good"; char str2[] = "afternoon"; char str3[] = "morning"; char str4[] = "morning"; cout << myStrCmp( str1, str2 ) << endl; cout << myStrCmp( str2, str3 ) << endl; cout << myStrCmp( str3, str4 ) << endl; } output: Improved version: KC Lee: *p1 – *p2 == g – a KC Lee: *p1 – *p2 == g – a

79 Next Lecture In the next lecture, we will learn: – Pointers and Dynamic Arrays It is very IMPORTANT that you make sure you understood this lecture before coming to the next lecture.