Presentation is loading. Please wait.

Presentation is loading. Please wait.

HKUST Summer Programming Course 2008

Similar presentations


Presentation on theme: "HKUST Summer Programming Course 2008"— Presentation transcript:

1 HKUST Summer Programming Course 2008
Pointers and References ~ Pointers, Memory Allocation and References

2 Overview Pointers What are Pointers? Declaration of Pointer Variables
Pointer Operator - & (Address-Of) Pointer Operator - * (Dereference) The Different Uses of Operator * Pointer Assignments Pointer Arithmetic Pointer Comparisons Multiple Indirection (Pointer to Pointer) Arrays and Pointers Dereference Array Pointers Array of Pointers Const and Pointer Null Pointer void Pointer

3 Overview Memory Allocation References Static and Dynamic Allocation
De-allocation of Memory Allocating and De-allocating Dynamic Arrays Illegal Delete on Dynamic Arrays Dangling Pointer Memory Leakage References What are References? The Different Uses of Operator & Call by Reference Pointer vs. Reference Return-by-Reference

4 Pointers and References

5 What are Pointers? A pointer or pointer variable is a VARIABLE that holds a memory address of another object (typically another variable) in memory. Memory Address Variables in memory If one variable contains the address of another variable, the first variable is said to point to the second. 1000 ‘a’ 1004 1008 1012 1000 1016 Pointer / Pointer variable 1020 Memory

6 Declaration of Pointer Variables
If a variable is going to hold an address of another variable, it must be declared as follows. SYNTAX: <type>* <name>; OR <type> *<name>; where type is the type of variable that this pointer variable can point to (e.g. int, char, double, user-defined type). The name is the name of the pointer variable. Actually, we can treat <type>* as a special type which is a pointer type.

7 Example // declare a pointer that points to a variable of int type
int* a; // the value of a is undefined and contains garbage. // declare a pointer that points to a variable of double type double* b; // the value of b is undefined and contains garbage. // declare a pointer that points to a variable of char type char* c; // the value of c is undefined and contains garbage. // there is no difference for you to put * close to type // OR close to name int* d; // the value of d is undefined and contains garbage. int *d;

8 Pointer Operator - & (Address-Of)
There are two special operators for pointers: & and * The first operator, & is a unary operator that returns the memory address of another variable. Usage: &<variable_name> We can think of & as returning “the address of”. Example: // pint receives the address of var1 int var1 = 5; int* pint = &var1; // pdouble receives the address of var2 double var2 = 1.23; double* pdouble = &var2; // ERROR: pdouble2 must point to a variable of type double double* pdouble2 = &var1;

9 Pointer Operator - & (Address-Of)
Graphic representation of last example Memory Address Variables in memory Variables Name 1000 5 var1 1004 1000 pint 1008 1012 pdouble 1.23 1012 var2 1016 Memory

10 Example - & (Address-Of)
#include <iostream> using namespace std; int main(){ int a, b; a = 88; b = 100; cout << "The address of a is: " << &a << endl; cout << "The address of b is: " << &b << endl; return 0; } Memory Address Variables in memory Variables Name 1000 1004 88 a 1008 100 b 1012 Memory Output: The address of a is: 1004 The address of b is: 1008

11 Pointer Operator - * (Dereference)
The second operator, *, is the complement of operator &. It is also a unary operator that accesses the value located at the address that it points to. We can think of * as “at address”. Example: int var1 = 5; // if (&var1) = 1004 int* pint = &var1; // pint points to var1 (then, pint = 1004) // var2 receives the value at address pint (var2 = 5). int var2 = *pint; // change the value of the memory location pointed to by pint // to 10, therefore var1=10, but note that var2=5 *pint = 10;

12 Pointer Operator - * (Dereference)
Graphic representation of last example Note that there is no guarantee that two variables are stored consecutively, even if they are defined consecutively. Memory Address Variables in memory Variables Name 1000 5 10 var1 1000 1016 pint 1020 5 var2 1024 Memory

13 The Different Uses of Operator *
Do not confuse the use of dereference operator * versus the * as a part of a pointer type. Example: int* p; // this means to declare a pointer variable int i,j=10; p = &j; i = *p; // this means to dereference the variable p

14 Pointer Assignments As with any variable, you may use a pointer variable on the right-hand side of an assignment statement to assign its value to another address. Example: int main(){ int x = 1; int *p1, *p2; // remember to add a * before p2 p1 = &x; // address of x is assigned to p1 p2 = p1; // content of p1 (which is address of x) // is assigned to p2 *p2 = 100; cout << "The address of x: " << p2 << “, x = ” << x << endl; return 0; }

15 Example on Pointers int main (){ int value1 = 5, value2 = 15;
Output: value1=10, value2=20 int main (){ int value1 = 5, value2 = 15; int *p1, *p2; // why not int* p1, p2? p1 = &value1; // p1 = address of value1 p2 = &value2; // p2 = address of value2 *p1 = 10; // value pointed to by p1=10 *p2 = *p1; // value pointed to by p2= value // pointed to by p1 p1 = p2; // p1 = p2 (pointer value copied) *p1 = 20; // value pointed to by p1 = 20 cout << "value1=" << value1 << “, value2=" << value2; return 0; }

16 Example – Swap Two Elements
void indirect_swap(char* ptr1, char* ptr2){ char temp = *ptr1; *ptr1 = *ptr2; *ptr2 = temp; } int main() { char a = 'y'; char b = 'n'; indirect_swap(&a, &b); cout << a << “” << b << endl; return 0;

17 Pointer Arithmetic There are ONLY two arithmetic operations that you may use on pointers: Addition and Subtraction. Therefore, you can use +, -, ++, --, +=, -=. No multiplication nor division. To understand what occurs in pointer arithmetic, let p1 be an integer pointer with current value of Also, assume integers are 4 bytes long. After the expression p1++, p1 contains 2004, NOT 2001. The same is true of decrements. For example, assuming that p1 has the value 2000, the expression p1-- causes, p1 to have the value 1996.

18 Pointer Arithmetic Graphic representation of last example p1 2000 p1--
Memory Address Variables in memory Variables Name 2000 p1 p1-- 1996 200 2000 100 p1++ 300 2004 Memory

19 Pointer Arithmetic Generalizing from the preceding example, the following rules govern pointer arithmetic. Each time a pointer is incremented, it points to the memory location of the next element of its base type. Each time a pointer is decremented, it points to the location of the previous element. When applied to character pointers, this will appear as “normal” arithmetic because characters are 1 byte long. All other pointers will increase or decrease by the length of the data type they point to. Eg. The size of a double variable is 8 bytes.

20 Pointer Arithmetic You are not limited to the increment and decrement the pointer by one. For example, you may add or subtract integers to or from pointers. The expression p1 = p1 + 2; makes p1 point to the second element of p1’s type beyond the one it currently points to. p1 = p1 - 2; makes p1 point to the second element of p1’s type precede the one it currently points to.

21 Pointer Arithmetic 2004 p1 int* p1 = 2004; p1-2 1996 2000 p1+2 2004
Memory Address Variables in memory Variables Name 2004 p1 int* p1 = 2004; p1-2 1996 2000 p1+2 2004 2008 2012 Memory

22 Pointer Comparisons We can compare two pointers in a relational expression. For instance, given two pointers, p and q, the following statements are perfectly valid. if(p < q) cout << "p points to lower memory than q" << endl; if(p > q) cout << "p points to higher memory than q" << endl; if(p == q) cout << "p points to the same memory as q" << endl;

23 Multiple Indirection (Pointer to Pointer)
You can have a pointer point to another pointer that points to the target value. This is called “multiple indirection” or “pointer to pointer”. Pointers to pointers can be confusing. The figure below helps clarify the concept of multiple indirection. Pointer variable Variable Single Indirection address value Pointer variable Pointer variable Variable Multiple Indirection address address value

24 Multiple Indirection (Pointer to Pointer)
The value of a normal pointer is the address of the object that contains the value. In the case of pointer to pointer, the first pointer contains the address of second pointer, which points to the object that contains the value desired. A variable that is a pointer to pointer can be declared as: SYNTAX: <type>** <name>; Example: int i = 10; int* ptr = &i; int** p_ptr = &ptr; int *q = ptr, **r = &ptr;

25 Multiple Indirection (Pointer to Pointer)
Multiple indirection can be carried on to whatever extent required, but more than a pointer to a pointer is rarely needed. In fact, excessive indirection is difficult to follow and prone to logical errors.

26 Example – Multiple Indirection
int a = 80; int *p = &a; int **q = &p; int ***r = &q; cout << a << " "; cout << *p << " "; cout << **q << " "; cout << ***r << " "; Memory Address Variables in memory Variables Name 80 a 1000 2000 1000 p 2004 2000 q 2004 r 2008 2012 Output: Memory

27 Arrays and Pointers There is a close relationship between pointers and arrays. An array name is actually a CONSTANT pointer to the first element of the array. A constant pointer means we cannot change the value of the pointer variable (it cannot point to another location). Example: int main (){ // Demonstrate array name is a constant pointer int a[5]; cout << "Address of a[0]: " << &a[0] << endl << "Name as pointer: " << a << endl; return 0; } Output: Address of a[0]: 0x0065FDE4 Name as pointer: 0x0065FDE4

28 Problem Can we do something like the following? int a = 10;
int* p = &a; int A[6] = {0,2,4,8,10,12}; A = p;  Can we do this? No! Since A is a constant pointer.

29 Arrays and Pointers // defines an array of integers int A[6] = {0,2,4,8,10,12}; int* p; p = A; // p points to A[0]; Since array names and pointers are equivalent, we can also use p as the array name. For example p[3] = 7; or *(p+3) = 7; is equivalent to A[3] = 7; Memory Address Variables in memory Variables Name 2000 p 2000 A[0] 2004 2 A[1] 2008 4 A[2] 2012 8 7 A[3] 2016 10 A[4] 2020 12 A[5] Memory

30 Example - Arrays and Pointers
#include <iostream> using namespace std; int main(){ int A[6] = {2,4,6,8,10,22}; int *p = &A[1]; //int *p = A+1; // more efficient cout << A[0] << " " << p[-1]; cout << " "; cout << A[1] << " " << p[0]; return 0; } Memory Address Variables in memory Variables Name 2004 p p[-1] 2000 2 A[0] 2004 4 p[0] A[1] 2008 6 A[2] 2012 8 A[3] 2016 10 A[4] 2020 22 A[5] Output: Memory

31 Pass-by-Array (new slide)
We can pass an array as a parameter into a function by two methods. void func1( double array[ ], int size ); void func2( double* array, int size ); int main( ) { double score[] = {1, 2.5, 6, 8.5}; func1(score, 4); func2(score, 4); return 0; } Both methods pass an array of type double into the function. C++ passes in the address of the first element of the array. Hence, we should pass in the size, to indicate the end of the array.

32 Pass-by-Array (new slide)
Indeed, there is no pass-by-array in C++. It is implemented by pass-by-value. But this time, it pass the address (of the first element) by value. Even if you change the pointer array to point to another array, the variable score is still pointing to the same array, without change (since it is pass-by-value). However, you can directly modify the content of the array, which is pointed to by both pointers array and score. The modification to the content of the array can affect the array score defined in the main function.

33 Pass-by-Array void inputToArray( double* array, int size ) { for (int i=0; i<size; i++) cin >> array[i]; } void print( const double* array, int size ) { for (int i=0; i<size; i++) cout << array[i] << endl; int main( ) { double score[10]; inputToArray( score, 10 ); print( score, 10 ); return 0; This will reset the content of the array with the input from keyboard and then print it to the screen. The keyword const will be covered in a minute.

34 Character String Character string is indeed a character array.
Recall that “Hello World” is of the type const char[12] We can pass this into some function with parameter type const char* Eg. char* strcpy(char* dest, const char* src) We can pass “Hello World” into the second parameter src, but not dest. However, the following definitions are different: char message1[12] = “Hello World”; “Hello World” is indeed a character array stored in a constant data area (cannot be modified). message1 is a character array, initialized by “Hello World”. You can modify message1, say: message1[0] = ‘a’; char* message2 = “Hello World”; message2 is simply a pointer, pointing to that constant data area. No separate array is allocated for storing “Hello World”. You cannot modify through this pointer, say: message2[0] = ‘a’; Otherwise, runtime error will be resulted.

35 Dereference Array Pointers
As array name is a constant pointer, dereference operator (*) can be used on it. A[0] is same as *(A+0) A[1] is same as *(A+1) A[2] is same as *(A+2) In general, A[n] is equivalent to *(A+n)

36 Array of Pointers Pointers may be arrayed like any other data type.
Example: int main(){ int a = 1, b = 2, c = 3; // array of pointers int *p[3]; p[0] = &a; p[1] = &b; p[2] = &c; return 0; } Memory Address Variables in memory Variables Name 2000 1 a 2004 2 b 2008 3 c 2000 p[0] 2012 2016 2004 p[1] 2020 2008 p[2] Memory

37 Const and Pointer When using a pointer, two objects are involved: the pointer itself, and the variable it is pointing to. The syntax for pointers to constants and constant pointers can be confusing . The rule is that any const to the left of the * in a pointer type refers to the object pointed to; any const to the right of the * refers to the pointer itself. The above rules are valid if you are considering single indirection. It may be a little bit more complicated if multiple indirection is considered. However, we won’t cover this in this course.

38 Example1 - Const and Pointer
char c = ‘Y’, d = ‘N’; // constant pointer (must be initialized), cpc = &d is invalid char *const cpc = &c; // pointer to a constant char, *pcc = ‘H’ is invalid char const* pcc = &c; const char* pcc2; // may not be initialized immediately // constant pointer points to a constant char (must be // initialized), both cpcc = &d and *cpcc = ‘H’ are invalid const char *const cpcc = &c; char const *const cpcc2 = &c;

39 Example2 - Const and Pointer
int main(){ char s[] = “Ming”; // type of “s” ~~ char *const char p[] = “Adam”; // type of “p” ~~ char *const const char* pcc = s; // Pointer to constant char pcc[0] = ‘5’; // Error! s[0] = ‘5’; // Fine! pcc = p; // OK, but what does that mean? char *const cpc = s; // Constant pointer cpc[0] = ‘5’; // OK cpc = p; // Error! // Constant pointer to constant char const char *const cpcc = s; cpcc[0] = ‘5’; // Error! cpcc = p; // Error! return 0; }

40 Example3 - Const and Pointer
It is an error if you try to use a “pointer to non-const object” to point to a “constant object”. Imagine that you can always modify the value indirectly through the “pointer to non-const”. You may then modify a constant object through using a pointer to point to it. int m = 10; // normal object const int N = 100; // constant object int* p = &m; // OK p = &N; // ERROR *p = 1000; // OK const int* q = &N; // OK *q = 1000; // ERROR

41 Example4 - Const and Pointer
You cannot assign “pointer to const” to a “normal pointer” Although it is valid to use a “pointer to const” to point to a non-constant object To check which object (const or non-const) a pointer is pointing to need trace the code C++ compiler won’t do this, so it simply disallow this type of assignment. int i = 151; int* pi = &i; // pi is a “normal pointer” const int* pic = &i; // pic is a “pointer to const” pi = pic; // Error! Cannot convert from // ‘const int*’ to `int *’, // although i is modifiable

42 NULL Pointer A null pointer is a special pointer that is currently pointing to nothing. Often pointers are set to zero (or predefined constant NULL) to make them null pointers, or tested against zero (or NULL) to see if they are null. Example: int* p = 0; if(p) // 0 (or null) is treated as false cout << "p is not a null pointer" << endl; int* p = NULL; if(p==NULL) cout << "p is a null pointer" << endl;

43 NULL Pointer We will get an error if we try to dereference a NULL pointer. Example #include <iostream> using namespace std; int main(){ int *p; p=NULL; cout << p << endl; // prints 0 cout << &p << endl; // prints address of p cout << *p << endl; // Error! return 0; }

44 void Pointer Recall that int* can only point to a variable of type int. It cannot point to a variable of type double. void pointer is a special pointer that can point to anything. void pointer is of the type void* Note that void is not a datatype itself. It can point to any datatype. Example: int n = 10; double x = 1.2; int* p_n = &n; void* p = &n; // point to int void* p = &x; // point to double void* p = &p_n; // point to an “integer pointer”

45 void Pointer However, since void pointer can point to anything, the length of its base type is unknown. We cannot use addition nor subtraction, whose result depends on the length of its base type. Also, we cannot interpret the result what it is pointing to. We cannot use dereference operator. We must first cast the void pointer to the “normal” pointer before using the dereference operator. int n = 10; void* p_n = &n; cout << *((int*)p_n) << endl; // output 10 cout << *((double*)p_n) << endl; // output garbage

46 void Pointer Recall that C++ is a strongly-typed programming language.
The advantage is that the compiler can detect many type mismatch errors. void pointer prevent the compiler from checking the type. The compiler never knows whether casting a void pointer to a “double pointer” (or an “integer pointer”) is valid or not. It is not recommended to use it in your programming. However, some C-styled functions make use of the advantage of void pointer that it can point to anything. You may find that some standard functions may take a void pointer as input. Some GUI programming also use void pointer. However, this is far outside our scope.

47 Pointers and References
Memory Allocation

48 Memory Allocation If we know prior to the execution of the program the amount of memory that we need, we can allocate memory statically prior to program start-up (i.e. compilation time).  We call this static memory allocation. However, we cannot always determine how much memory we need before our program runs. For example, the size of an array may not be known until your executing program determines what these values should be. So, what should we do?  We need dynamic memory allocation.

49 Memory Allocation In C++, we can request memory from operating system at runtime and we call this dynamic memory allocation. An area of memory called the heap (or free store) is available in the run-time environment to handle dynamic memory allocation. In C++ programs, we can use operator new to allocate memory from heap and operator delete to release heap memory.

50 Conceptual View of Memory
Heap is a special area of memory which is reserved for dynamic variables. MEMORY PROGRAM MEMORY code for functions DATA MEMORY global program heap (dynamic memory) system stack

51 Memory Allocation Static Memory Allocation Dynamic Memory Allocation
Memory is allocated at compilation time. The following fragment allocates memory for x, y and p at compilation time. int x, y; // x and y are integers int* p; // p is an integer pointer variable Memory is returned automatically when variable/object goes out of scope. Dynamic Memory Allocation Memory is allocated from heap at running time using new. Dynamic objects can exist beyond the function in which they were allocated. Memory is returned by a de-allocation request using delete. Recall that statically allocated local variables are all put on the stack (activation record) The stack stores the pointer, which will be “released” automatically after the activation record is popped out from the stack. The heap stores the newly-allocated object, which will not be automatically released, even after the function returns.

52 Dynamic Memory Allocation
SYNTAX: <name of type T*> = new <T>; where T is the type of variable to allocate (e.g. int, char, double, user- defined type) and name is the name of the pointer variable. The new operator allocates memory from heap and returns a pointer to it (or return its address value). Note that there is no identifier associated with the memory location in heap. All you can do on it is through the usage of pointers. Pointer is very useful  If all the memory is used up and new is unable to allocate memory then new returns the value NULL.

53 Example - Dynamic Memory Allocation
int* p; p = new int; // In a real programming situation, we should always // check for this memory allocation error if(p == NULL){ cout << "Memory allocation is not successful" << endl; exit(1); } Stack Heap Un-initialized int variable p

54 De-allocation of Memory
SYNTAX: delete <name>; where name is the name of the pointer variable that points to dynamic memory. The system has a limited amount of space on the heap. So as not to use it up, it is a good idea to return the USUSED dynamic memory to the heap, as soon as possible.

55 Example – new and delete
// Program to demonstrate new and delete int main(){ int* p = new int; // allocate space from heap if(p == NULL){ cout << "Memory allocation is not successful" << endl; exit(1); } *p = 100; // that space stores the value 100 cout << "At " << p << " "; cout << "is the value " << *p << endl; delete p; // Note that it DOES NOT modify p. After executing delete p, // the value of p is still pointing to the old memory location. cout << p << endl; // It is a good practice to reset p to NULL after deletion p = NULL; return 0;

56 Allocating and De-allocating Dynamic Arrays
The general forms of allocating dynamic array using new and delete are shown below. SYNTAX: <type>* <name> = new <type>[<size>]; delete [ ] <name>; Here size specifies the number of elements in the array. Note that size does not have to be a constant. It can be an expression evaluated at runtime. This is the key difference from static array. The [ ] informs delete that an array is being released.

57 Example – Dynamic Array
int main(){ int* p; p = new int[10]; // allocate an array of 10 integers if(p == NULL){ cout << "Allocation is not successful" << endl; exit(1); } for(int i=0; i<10; i++){ p[i] = i; cout << p[i] << " "; delete [] p; // release the array p = NULL; return 0;

58 Example – Dynamic Array
Need an array of unknown size int main(){ cout << "How many students? "; cin >> n; // The size of dynamic array is determined by user-input int *grades = new int[n]; for(int i=0; i<n; i++){ int mark; cout << "Input Mark for Student" << (i+1) << ": "; cin >> mark; grades[i] = mark; } for(int i=0; i<n; i++) cout << grades[i] << " "; delete[] grades; grades = NULL; return 0;

59 Example – Dynamic Array
Since static array is stored in the runtime stack, a very large array cannot be fitted in this limited memory location, so we may need to use dynamic array even if we know the size of array in prior We usually allocate a small amount of memory for runtime stack (say, 2MB) But the amount of memory you can allocated from heap depends on the amount of physical memory (RAM) you have int main(){ const int N = ; int *p = new int[N]; // an array of 1 million elements for(int i=0; i<N; i++) p[i] = i; delete [] p; p = NULL; return 0; }

60 Example - Dynamic 2D Array Allocation
int** table; table = new int*[4]; table[0]= new int[3]; table[1]= new int[1]; table[2]= new int[4]; table[3]= new int[2]; table[0][0] = 1; table[0][1] = 2; table[0][2] = 3; table[1][0] = 4; table[2][0] = 5; table[2][1] = 6; table[2][2] = 7; table[2][3] = 8; table[3][0] = 9; table[3][1] = 10; cout << "table[2][3]: " << table[2][3] << endl; table table[0] 1 2 3 table[1] 4 table[2] 5 6 7 8 table[3] 9 10 Output: Table[2][3]: 8

61 Example - Dynamic 2D Array De-allocation
Each row must be deleted individually. Be careful to delete each row before deleting the pointer table. // Delete each individual row for(int i=0; i<4; i++) delete [] table[i]; // Delete the pointer table afterwards delete [] table; table = NULL;

62 Illegal Deletion on Dynamic Arrays
p = new int[n] will allocate an array of n objects of type int and it will return a pointer to the start of the array. delete [] p will destroy the array to which p points and return the memory to the heap. p (in delete[ ] p) must point to the front of the dynamically allocated array. If it does not, the resulting computation will be unpredictable, i.e., it will depend upon what compiler you are using and what data you are inputting. You might get a run-time error, a wrong answer…

63 Example – Illegal Delete
// To demonstrate an illegal delete on dynamic arrays int main(){ int* A = new int[6]; A[0] = 0; A[1] = 1; A[2] = 2; A[3] = 3; A[4] = 4; A[5] = 5; int* p = A+2; cout << "A[1] = " << A[1] << endl; // this is ILLEGAL! Result is unpredictable. delete [] p; // the result depends upon the particular compiler return 0; }

64 Dangling Pointer Dangling pointers are pointers which do not point to a valid object. They arise when an object is deleted or de-allocated, without modifying the value of the pointer, so that the pointer still points to the memory location of the de-allocated memory. For example int* p; // p is an integer pointer variable int* q; // q is an integer pointer variable p = new int; // allocate memory from heap q = p;

65 Location does not belong to the program
Dangling Pointer The last example creates But then executing delete p; p = NULL; leaves q dangling. *q = 10; // Illegal p q Location does not belong to the program p ? q

66 Memory Leakage A memory leak is what happens when we forgot to return a block of memory allocated with the new operator or make it impossible to do so, e.g., losing all pointers to an allocated memory location. Recall that there is no identifier associated with the memory location in the heap. When this happens, the memory can never be de-allocated and is lost, i.e., never return to the heap. For example int* p; // p is an integer pointer variable int* q; // q is an integer pointer variable p = new int; // allocate memory from heap q = new int;

67 Memory Leakage The last example creates But then executing q = p;
leaves the location previously pointed by q lost p q p Location cannot be accessed by the program q

68 Problem of Memory Leakage
Memory leaks can seriously impact the ability of a program to complete its task. It may be the case that subsequent dynamic memory requests cannot be satisfied because of insufficient of heap memory. For this reason, memory leaks should be avoided. Write the delete statement in the same block as the new statement. Write the delete statement immediately after writing the new statement. Except some special conditions, say: A function used to allocating memory. In this case, add comment to explicitly ask the user of this function to deallocate the resultant pointer after use.

69 Pointers and References

70 References What are references? SYNTAX:
A reference is an alternative/another name (alias) for an object. Reference variables are USUALLY USED in parameter passing to functions. SYNTAX: // p is reference variable (an alternative name) of q <type>& p = q; OR <type> &p = q; q p

71 Example int j=1; // r is just another name of j int&r = j; // now r = j = 1 int x = r; // now x = 1 // As r is just j, r changes to 2 means // j changes to 2 as well r = 2; // now r = j = 2; j = 10; // now r = j = 10; A reference allows indirect manipulation of an object, somewhat like a pointer, without requiring complicated pointer syntax .

72 Important Points about Reference
A reference MUST always bound to an object (similar to a constant pointer). It must therefore be initialized when it is created. int j = 1; int& r1 = j; // ok int& r2; // errors! A reference cannot bound to another object once it initially was initialized to. But what does the following mean? int j = 10; int& r = j; int k = 50; r = k; // what does this mean? Assignment from a variable, say k, to a reference variable, say r, means r get assigned with the value of k.

73 The Different Uses of Operator &
Do not confuse the use of operator & in declaring reference variable versus the use of & as the address of operator for pointer. For example int j; // this means to declare a reference variable // “&” is a part of the reference type int& i = j; // this means the address of j to p int* p = &j;

74 Question The following is wrong. Why? int j; int& i = &j;
The following is correct. What does it mean? int* p = &j; int*& ref = p; // a reference of “integer pointer”

75 Example - Reference int main(){ int j=1;
// pi is an int* initialized to address of j int* pi = &j; // ref is a reference variable of type int* int*& ref = pi; cout << "j = " << j << endl; cout << "*pi = " << *pi << " *ref = " << *ref << endl; int k=2; pi = &k; return 0; }

76 Call by Reference Reference arguments are special case of references variable. For example int f(int& i){ ++i; return i; } int main(){ int j=7; cout << f(j) << endl; cout << j << endl; return 0; Output: 8

77 Call by Reference Variable i is a local variable in the function f. Its type is "int reference" and it is created when f is called. In the call f(j), i is created similarly to the construction: int& i = j; So within the function f, i will be an alias of the variable j, and i cannot be bind to another variable. But every time the function is called, a new variable i is created and it can be a reference to a different object.

78 Why Call by Reference? There are two reasons:
The function caller wants the function to be able to change the value of passed arguments. For efficiency: If you pass a function argument by value, the function gets a local copy of the argument. For large objects, copying is expensive; on the other hand, passing an object by reference does not require copying, only passing a memory address (since reference is similar to pointer).

79 Example – Call by Reference
void swap(char& y, char& z) { char temp = y; y = z; z = temp; } int main() { char a = 'y'; char b = 'n'; swap(a, b); cout << a << b << endl; return 0;

80 const: References as Function Arguments
There are two good reasons to pass an argument as a reference, you can express your intention to leave a reference argument of your function unchanged by making it const. This has two advantages: First, if you accidentally try to modify the argument in your function, the compiler will catch the error: void cbr(int& i){ i += 10; // Fine } void cbcr(const int& j){ j += 10; // Error!

81 const: References as Function Arguments
Second, you can call a function that has a const reference parameter with both const and non-const arguments. Conversely, a function that has a non-const reference parameter can only be called with non-const arguments. void cbr(int& i){ cout << i << endl; } void cbcr(const int& i){ cout << i << endl; } int main(){ int i = 50; const int j = 100; cbr(i); cbcr(i); cbr(j); // Error cbcr(j); return 0; }

82 Pointer vs. Reference A reference can be thought of as a special kind of pointer, but there are 3 big differences to remember! A pointer can point to nothing (NULL), but a reference is always bound to an object. No need to check NULL when using reference  A pointer can point to different objects at different times (through assignments). A reference is always bound to the same object. Assignments to a reference do NOT change the object it refers to but only the value of the referenced object. The name of a pointer refers to the pointer variable. The * or -> (details of operator -> will be covered later) operators have to be used to access the object. The name of a reference always refers to the object. There are no special operators .

83 Example – Pointers and References
void func1(int* pi){ (*pi)++; } void func2(int& ri){ ri++; } int main(){ int i=1; cout << "i = " << i << endl; // call using address of i func1(&i); // call using i func2(i); return 0; } Output: i = 1 i = 2 i = 3

84 Return-by-Reference It is possible to define a function which returns a reference variable (similar to returning a pointer). double& at( double* array, int size, int index ) { return array[index]; } int main( ) { double array[] = {12.3, 2.2, 3.4, 4.5, 4.3}; at(array, 5, 2) = 12; cout << array[2] << endl; // output = 12 return 0; More about this later when we are talking about operator overloading.

85 Return-by-Reference However, it is a pitfall to return the reference of a local variable in the function. Recall that local variable is stored in activation record. Once the function returns, the activation record will be destroyed and the variables inside cannot be used again. The returned reference cannot be modified (may lead to runtime error). Printing the returned reference may result in garbage value. double& at( double* array, int size, int index ) { double temp = array[index]; return temp; // ERROR: returning reference // of local variable }

86 Return-by-Reference Therefore, return-by-reference can be used in:
Parameter passes into the function. Static variable of the function. Dynamically allocated items in the function. All these items won’t be destroyed after the function returns. Similar rules apply on return-by-pointer.


Download ppt "HKUST Summer Programming Course 2008"

Similar presentations


Ads by Google