Upload
brandy
View
23
Download
0
Embed Size (px)
DESCRIPTION
HKUST Summer Programming Course 2008. Pointers and References ~ Pointers, Memory Allocation and References. Overview. Pointers What are Pointers? Declaration of Pointer Variables Pointer Operator - & (Address-Of) Pointer Operator - * (Dereference) The Different Uses of Operator * - PowerPoint PPT Presentation
Citation preview
1Department of Computer Science and Engineering, HKUST
HKUST SummerProgramming 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
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
4Department of Computer Science and Engineering, HKUST
Pointers and References
Pointers
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.
‘ a’‘ a’
MemoryAddress
1000
1012
10041008
1016
Variables inmemory
1020
Memory
10001000
If one variable contains the address of another variable, the first variable is said to point to the second.
Pointer / Pointer variable
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 variablecan 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 var1int var1 = 5;int* pint = &var1;
// pdouble receives the address of var2double var2 = 1.23;double* pdouble = &var2;
// ERROR: pdouble2 must point to a variable of type doubledouble* pdouble2 = &var1;
9
Pointer Operator - & (Address-Of)
Graphic representation of last example
55
10001000
10121012
MemoryAddress
1000
1012
1004
1008
Variables inmemory
Memory
1.231.23
VariablesName
pdouble
pint
var2
var1
1016
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;
}
……
8888
100100
MemoryAddress
1000
1012
10041008
Variables inmemory
Memory
……
VariablesName
ba
Output:The address of a is: 1004The 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) = 1004int* 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
5 105 10
10001000
55
MemoryAddress
1000
1024
1016
1020
Variables inmemory
Memory
VariablesName
var1
pint
var2
Note that there is no guarantee that two variables are stored consecutively, even if they are defined consecutively.
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 p2p1 = &x; // address of x is assigned to p1p2 = 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; int *p1, *p2; // why not int* p1, p2?p1 = &value1; // p1 = address of value1p2 = &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;
}
Output:
value1=10, value2=20
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 2000. 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
200200
100100
300300
MemoryAddress
1996
2000
2004
Variables inmemory
Memory
VariablesName
p1--
p1++
20002000 p1
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.
The expressionp1 = p1 - 2;makes p1 point to the second element of p1’s type precede the one it currently points to.
21
Pointer Arithmetic
MemoryAddress
1996
2000
2004
Variables inmemory
Memory
VariablesName
p1-2
p1+2
20042004 p1
2008
2012
int* p1 = 2004;
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.
addressaddress valuevalue
Pointer variable Variable
addressaddress addressaddress
Pointer variable Pointer variable
valuevalue
Variable
SingleIndirection
MultipleIndirection
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 << " ";
MemoryAddress
2000
2004
2008
Variables inmemory
Memory
VariablesName
p
r
8080 a
2012
10001000
20002000
20042004
1000
q
Output:80 80 80 80
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 pointerint a[5];cout << "Address of a[0]: " << &a[0] << endl << "Name as pointer: " << a << endl;return 0;
}
Output:Address of a[0]: 0x0065FDE4Name 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 integersint 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 examplep[3] = 7; or *(p+3) = 7;
is equivalent toA[3] = 7;
00
22
44
MemoryAddress
2000
2004
2008
Variables inmemory
Memory
VariablesName
A[0]
20002000 p
8 78 72012
10102016
A[1]
A[2]
A[3]
A[4]
12122020 A[5]
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;
}
22
44
66
MemoryAddress
2000
2004
2008
Variables inmemory
Memory
VariablesName
A[0]
20042004 p
882012
10102016
A[1]
A[2]
A[3]
A[4]
22222020 A[5]
p[-1]
p[0]
Output:2 2 4 4
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-Arrayvoid 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 pointersint *p[3]; p[0] = &a;p[1] = &b;p[2] = &c;return 0;
}
11
22
33
MemoryAddress
2000
2004
2008
Variables inmemory
Memory
VariablesName
a
200020002012
200420042016
b
c
p[0]
p[1]
200820082020 p[2]
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 invalidchar *const cpc = &c;
// pointer to a constant char, *pcc = ‘H’ is invalidchar 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 invalidconst char *const cpcc = &c; char const *const cpcc2 = &c;
39
Example2 - Const and Pointerint main(){
char s[] = “Ming”; // type of “s” ~~ char *constchar p[] = “Adam”; // type of “p” ~~ char *constconst char* pcc = s; // Pointer to constant charpcc[0] = ‘5’; // Error!s[0] = ‘5’; // Fine!pcc = p; // OK, but what does that mean?
char *const cpc = s; // Constant pointercpc[0] = ‘5’; // OKcpc = p; // Error!
// Constant pointer to constant charconst 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 objectconst int N = 100; // constant object
int* p = &m; // OKp = &N; // ERROR *p = 1000; // OKconst 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 0cout << &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 intvoid* p = &x; // point to doublevoid* 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 10cout << *((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.
47Department of Computer Science and Engineering, HKUST
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.
MEMORYMEMORY
PROGRAM MEMORYPROGRAM MEMORY
DATA MEMORYDATA MEMORY
code for functionscode for functions
globalglobal program heap(dynamic memory)
program heap(dynamic memory) system stacksystem stack
51
Memory Allocation Static 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 integersint* 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
Stack Heap
Example - Dynamic Memory Allocation
int* p;p = new int;
// In a real programming situation, we should always// check for this memory allocation errorif(p == NULL){
cout << "Memory allocation is not successful" << endl;exit(1);
}
p Un-initializedint variable
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 deleteint main(){
int* p = new int; // allocate space from heapif(p == NULL){
cout << "Memory allocation is not successful" << endl;exit(1);
}*p = 100; // that space stores the value 100cout << "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 deletionp = 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 integersif(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 arrayp = 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-inputint *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 = 1000000;int *p = new int[N]; // an array of 1 million elementsfor(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[0]
table[1]
table[2]
table[3]
1 2 3
4
5 6 7 8
9 10
Output:Table[2][3]: 8
table
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 arraysint 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 compilercout << "A[1] = " << A[1] << endl;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 exampleint* 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
Dangling Pointer
The last example creates
But then executing
delete p;
p = NULL;
leaves q dangling.
*q = 10; // Illegal
p
q?
Location does notbelong 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 variableint* q; // q is an integer pointer variablep = new int; // allocate memory from heapq = new int;
67
Memory Leakage
The last example creates
But then executing
q = p;
leaves the location previously pointed by q lost
p
q Location cannot beaccessed by the
program
p
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.
69Department of Computer Science and Engineering, HKUST
Pointers and References
References
70
References
What are references? 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;qp
71
Example
int j=1;// r is just another name of jint&r = j; // now r = j = 1int x = r; // now x = 1
// As r is just j, r changes to 2 means// j changes to 2 as wellr = 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; // okint& 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 exampleint 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 j;
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 jint* 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;cout << "j = " << j << endl;cout << "*pi = " << *pi << " *ref = " << *ref << endl;return 0;
}
76
Call by Reference
Reference arguments are special case of references variable.
For exampleint f(int& i){
++i;return i;
}int main(){
int j=7;cout << f(j) << endl;cout << j << endl;return 0;
}
Output:88
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); // Errorcbcr(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 ifunc1(&i);cout << "i = " << i << endl;// call using ifunc2(i);cout << "i = " << i << endl;return 0;
}
Output:i = 1i = 2i = 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 = 12return 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.