Download presentation
Presentation is loading. Please wait.
1
310201 Fundamental Programming
Pointers and Dynamic Memory.
2
Topics for this Week : Week 9
Pointers, Dynamic Memory, Memory Addresses, New and Delete Operations.
3
Address of Objects - & Operator
All C++ variables occupy memory within the computer. All memory locations within the computer can be accessed by using a unique address to that segment of memory. If, for instance, we made the declarations : int i = 8, k = 12; then we have declared 2 integers : i and k, and assigned initial values to them.
4
Address of Objects - & Operator (cont …)
Remembering the fact that standard integers in C++ require 2 bytes of memory, then the data in memory would look something like this : Memory Address (Will vary depending on your PC, setup, etc). Contents Name/Tag 0x118f2410 8 i 0x118f2412 12 k
5
Address of Objects - & Operator (cont …)
C++ allows us to find out the memory address at which an object is located by using a special version of the ampersand & operator. WARNING: The ampersand & operator is used in several different roles, for example, it is used in : Pass by reference operator. i.e. Reference Parameter, Bitwise AND, Address of memory variable. i.e. an address of an object. You must ensure that you are aware of which particular role the ampersand & operator is playing in the different parts of a program !
6
Address of Objects - & Operator (cont …)
An & (ampersand) in front of an object name means “give me the memory address of the object” : int i = 8, k = 12; // Declare and initialise 2 integers. cout << "Memory address of i = " << &i << endl; cout << "Memory address of k = " << &k << endl; The output from this program would be something like : Memory address of i = 0x118f2412 Memory address of k = 0x118f2410
7
Pointers An object that can store the address of another object is called a pointer. Pointers are declared by using the asterisk * operator. WARNING: The asterisk * operator can be used in different roles, for example, it is used in : Multiplication of numbers, Declaring a pointer, and, De-referencing operator for pointers (more on this soon). You must ensure that you are aware of which particular role the asterisk * operator is playing in the different parts of a program.
8
Pointer and Address Example
int i = 8, j =12; // Declare and initialise 2 integers. int *iPtr = &i; // iPtr is a pointer to an integer stored at address &i int *jPtr = &j; // jPtr is a pointer to an integer stored at address &j cout << "At address " << iPtr << " which is the same as " << &i << ", the value " << *iPtr << " is stored." << endl; cout << "At address " << jPtr << " which is the same as " << &j << ", the value " << *jPtr << " is stored." << endl; The output from this program would be something like : At address 0x4e which is the same as 0x4e772430, the value 8 is stored. At address 0x4e77242e which is the same as 0x4e77242e, the value 12 is stored.
9
Pointer and Address Example (cont …)
In this example : Two integers (i and j) are declared and given initial values. Then, 2 pointers (iPtr and jPtr) are declared - using the asterisk * operator - and initialised to the addresses - using the ampersand & operator - of the 2 integers (i and j) respectively. Then some lines of output are displayed using the pointers, the ampersand & operator, and the asterisk * operator. The result of this is that iPtr contains the memory address the address of integer i. And, *iPtr contains the contents or value of the integer i. Similarly for j and jPtr.
10
Pointer and Address Example (cont …)
The use of the ampersand & operator and asterisk * operator is very important in this example. You cannot use these operators inter-changeability : &P means the address of a particular object P. *P means the contents of memory pointed to by P.
11
Pointer and Address Example (cont …)
Consider the following : Name/Tag Stored in Memory Memory Address i ===========> x4e772430 *iPtr ========================> 0x4e772430 where : i is a direct reference to an address value. i.e. 8. &i is the address of the value referred to by i. i.e. 0x4e iPtr contains the address of the value referred to by i. i.e. 0x4e *iPtr is the contents of the memory pointed to by iPtr, which is the contents of the memory location 0x4e772430, which is the value 8 – which, ofcourse, is the value of i.
12
Pointer De-Referencing
In other words iPtr is an indirect reference to (in this case) the integer object referred to by i. To obtain direct access to an object via an indirect reference we must use the asterisk (*) operator on the indirect reference. This is called pointer de-referencing.
13
Pointers in General It so happens that the concept of pointers is probably only exceeded in its importance to modern computing by the concept of Object Oriented Programming (OOP). In fact, without pointers, OOP could not be implemented ! The concept of pointers could be argued as one of the single most important concepts in all of programming.
14
Pointers and Strings Consider the following pointer declaration :
char *str_ptr = "HAL"; This places the string "HAL" into an array pointed to by str_ptr, and automatically appends the null terminator ‘\0’ to the end. That is : An array of 4 characters is allocated ! str_ptr points to the start of this array !
15
Pointers and Strings (cont …)
If we do the following : cout << *str_ptr << endl; then we would see "H" displayed onto the screen only - this is the memory location pointed to by str_ptr pointer. Similarly : cout << str_ptr[0] << endl; would also display just the "H" onto the screen, and once again this is the value at index location 0 of the array pointed to by str_ptr.
16
Pointers and Strings (cont …)
However, the following : cout << str_ptr << endl; would display the entire string "HAL" to the screen, and because we are using cout, output automatically stops at the null terminator ‘\0’.
17
Pointer De-referencing
The asterisk (*) operator can be used as a modifier in declaring a pointer. It can also be used in a different role to act as a de-referencing operator. Before we can obtain direct access to an object via an indirect reference we must perform an operation on the indirect reference called pointer de-referencing.
18
Pointer De-referencing (cont …)
int num1, num2; // Declare two integers. int *nPtr = &num1; // Declare and initialise an integer pointer. num1 = 6; // Assigns 6 to num1. num2 = *nPtr; // Assign value of num1 to num2 via pointer de-referencing. *nPtr = 7; // Assign value 7 to num1 via pointer de-referencing. cout << "num1 = " << num1 << endl; cout << "num2 = " << num2 << endl;
19
Pointer Operations - Initialisation
We have already seen that a pointer can be initialised to an address of an existing object. For example : int num = 2; int *numpointer = # However, you cannot do this : double DoubleVar ; int *iPtr = &DoubleVar; // ERROR !! This is not valid, because the pointer type must match the type of whatever it points at. In this case, iPtr should be of type double like DoubleVar, instead of an int.
20
Let’s Recap – with some exercises …
What would be the result of this code : int i; int *My_Ptr; My_Ptr = &i; *My_Ptr = 25; Is this code valid ? Why ? int i = 25; int *My_Ptr = i;
21
Pointer Operations - the NULL Address
It is valid to do this : char *cPtr = 0; // Initialise to the NULL address int *iPtr = NULL; int *kPtr = 0; The NULL (or 0) address is a special value that can be assigned to a pointer to indicate that it is not currently pointing to any particular memory location. This is not the same as the pointer variable being undefined. i.e. having no particular value.
22
Pointer Operations - the NULL Address (cont …)
We can use the NULL memory address to see whether the pointer is defined before using it : int *iPtr = 0; ::::: other code to manipulate the pointer if (iPtr == 0) // iPtr is not pointing to a memory location else // iPtr is pointing to a memory location
23
Pointer Operations - the NULL Address (cont …)
Or, instead of 0, we can use the NULL keyword directly : int *iPtr = NULL; ::::: other code to manipulate the pointer if (iPtr == NULL) // iPtr is not pointing to a memory location else // iPtr is pointing to a memory location
24
Pointer Operations - Assignment
As we have already seen, pointers can be used to assign values to the objects that they point at. Once a pointer points at something, it can easily be changed to point at another object of the same type. The statement : jPtr = iPtr; // Assignment. has the effect of changing the memory address pointed to by jPtr, to the same memory address pointed to by iPtr.
25
Pointer Operations – Assignment (cont …)
int i = 8, j =12; // Declare and initialise 2 integers int *iPtr = &i, *jPtr = &j; // Declare and initialise 2 integer pointers cout << "Before pointer assignment :" << endl; cout << "\nAt address " << iPtr << ", the value " << *iPtr << " is stored" << endl; cout << "At address " << jPtr << ", the value " << *jPtr << " is stored" << endl; jPtr = iPtr; // Assignment. cout << "After pointer assignment :" << endl;
26
Pointer Operations – Assignment (cont …)
The output produced by this code would be something like : Before pointer assignment : At address 0x4faf24ac, the value 8 is stored At address 0x4faf24aa, the value 12 is stored After pointer assignment :
27
Pointer Operations - Comparison
You may use the relational operators to compare two pointers as long as they are of the same type. For example, the following comparisons are legal : if (iPtr == jPtr) // as long as both pointers are same type. if (iPtr != 0) // check pointer is not pointing to NULL address. You can also use of other comparison operators : >, <, >=, <=, etc, but these do not make much sense for pointers, because they contain memory addresses.
28
Exercise #2 ** What would be output by this code : int i = 3, k=3;
int *iPtr = &i; int *kPtr = &k; if (iPtr == kPtr) cout << "Same"; else cout << "Different";
29
Exercise #3 ** What would be output by this code : int i = 3, k=3;
int *iPtr = &i; int *kPtr = &k; if (&iPtr == &kPtr) cout << "Same"; else cout << "Different";
30
Exercise #4 ** What would be output by this code : int i = 3, k=3;
int *iPtr = &i; int *kPtr = &k; if (*iPtr == *kPtr) cout << "Same"; else cout << "Different";
31
Pointer Operations - Arithmetic
The various operators such as : ++, --, += , -= may also be used on pointers to enable us to rapidly move through memory locations, but be very careful where you start and finish in memory. As with arrays, C++ will not warn or inform you if you go outside the memory locations allowed for the pointer.
32
Pointer Operations – Arithmetic (cont …)
Here we are initialising an array - without even using the array's name : double dArray[10]; double *dPtr = dArray; // Points to the address of dArray[0]. for(int x = 0; x < 10; x++) { *dPtr = 0.0; // Set elements to zero dPtr++; // Increment memory location pointed at } // to point to next array element.
33
Sizeof : Advanced For pointer arithmetic it is sometimes useful to know the size of a particular object in memory. The C++ sizeof function provides this information. For example : sizeof (double); // Would return 8. sizeof (int); // Would return 2 or 4 depending on your // operating system / computer hardware. sizeof returns the number of bytes of storage for a particular object or data type.
34
Sizeof : Advanced (cont …)
This is also true for arrays : float fArray [20]; cout << sizeof (fArray); // Would output 20 x 4 = 80 cout << sizeof (fArray) / sizeof (float); // Would output 80 / 4 = 20 As you can see from this example sizeof (fArray) returns the total number of bytes of memory reserved for the array. In order to obtain the number of elements in the array, we need to divide this value by sizeof (float) !
35
Sizeof : Structures and Arrays of Structures
struct People { char Name [20]; // 20 characters = 20 bytes of memory char Address [50]; // 50 characters = 50 bytes of memory float Age; // 4 bytes of memory int Weight; // 2 bytes of memory // Total = 76 bytes of memory per object delcared. }; void main () People Village [10]; // Declare array of 10 People cout << sizeof (People); // Would output 76 cout << sizeof (Village); // Would output 76 * 10 = 760 cout << sizeof (Village) / sizeof (People); // Would output 760 / 76 = 10 }
36
Pointers, Strings, and (Ptr + i)[k] notation
As we have seen earlier, this code : char *str_ptr = "Hi There"; declares array containing the text plus the null terminator '\0', and : cout << *str_ptr << endl; would output just a "H". And : cout << str_ptr[0] << endl; would also output just a "H".
37
Pointers, Strings, and (Ptr + i)[k] notation (cont …)
However : cout << str_ptr[5] << endl; would output the contents of location 5 (i.e. the 6th element) in the array. i.e. "e". In a similar way : cout << (str_ptr + 3)[2] << endl; would jump to the 3rd location in the string / array and then use this as the base of an array and jump to the 2nd location in this array, and this would output the letter "e" to the screen. This has the same effect as :
38
Pointers - HAL and IBM Example
Using pointers with array notation : char *str_ptr = "HAL"; for (int i = 0; i < 3; i++) { str_ptr [i] = str_ptr[i] + 1; } cout << str_ptr << endl;
39
Pointers - HAL and IBM Example (cont …)
Or, we could do the same thing using pointers with pointer notation : char *str_ptr = "HAL"; for (int i = 0; i < 3; i++) { *(str_ptr + i) = *(str_ptr + i) + 1; } cout << str_ptr << endl;
40
Pointer Arithmetic and Scaling
Arithmetic operations on pointers are scaled. That is, when you perform pointer arithmetic, the compiler scales the result relative to the type of data being pointed to, rather than by bytes of memory. Example : float *fPtr; // Floats occupy 4 bytes of memory !! ++fPtr; The ++fPtr would increment the pointer by one float location. This new memory address will be 4 bytes away from its original location – not one byte ! It is very important that you remember this scaling aspect of pointers !
41
Exercise #5 ** If we had : What would result in each instance below :
char *str_ptr = "HAL"; What would result in each instance below : cout << *(str_ptr + 0) << endl; cout << *(str_ptr + 1) << endl; cout << *(str_ptr + 2) << endl; cout << *(str_ptr + 3) << endl; cout << *(str_ptr + 4) << endl; cout << *(str_ptr - 1) << endl; cout << *(str_ptr – 15)[17] << endl; Warning: Be extremely careful when using pointers !!
42
Pointers and Call by Reference
Pointers can be used to implement a call by reference parameter passing mechanism. In fact in C, the precursor of C++, this was the only method possible of passing parameters ! Function to swap two integer values, and has 2 pointers as parameters : void swap (int *x, int *y) { int temp = *x; // temp = value in x *x = *y; // set x’s value to y’s *y = temp; // set y’s value to x’s original value } // Exercise : Why isn't temp a pointer ?
43
Pointers and Call by Reference (cont …)
void main() { int a = 10; int b = 20; swap (&a, &b); // Exercise : Why use '&' ? cout << "\na = " << a << ", b = " << b << endl; }
44
Pointers and Call by Reference (cont …)
void main() { int a = 10; int b = 20; int *p = &a; // Exercise : What's happening here ? int *q = &b; swap (p, q); // Exercise : How does this work ?? cout << "\na = " << a << ", b = " << b << endl; }
45
Pointers and the New and Delete Operations
When we allocate memory at compile-time, for example to an array object, we are allocating a fixed amount of memory which cannot be changed during program operation. The only way to change the array's size is to change our source code and re-compile our program. Such arrays are said to be static arrays. For example, this code : const int Length = 20; double List [Length]; declares an array of 20 double length floating point elements, with valid index locations 0 to 19. This array is fixed in size ! i.e a static array !
46
Pointers and the New and Delete Operations (cont …)
To allow us to dynamically allocate memory (i.e. so we can declare an array of variable size) at run-time, we require the C++ new and delete operations. The new operator dynamically allocates or reserves memory and has the form : new <Type of object> The new operation returns a pointer and so is used in conjunction with a pointer variable. If the request for run-time memory can not be granted then new operation returns the NULL memory address (i.e. zero), otherwise it allocates a block of memory large enough to hold the required object(s), and returns a pointer to the start of this allocated memory.
47
Pointers and the New and Delete Operations (cont …)
When we declare a pointer that does not point to an existing object, then all this pointer can hold is a memory address, it cannot be used to store any real data until the new operation has been used. Also, when we use the new operation, we should check the pointer's value to see if the memory allocation was successful.
48
Pointers and the New and Delete Operations (cont …)
double *dPtr; // Define a Pointer of type double. Uses no memory ! dPtr = new double; // Allocate 8 bytes of memory. i.e. sizeof(double). if (dPtr == 0) // Was the memory allocated successfully ? { cerr << "\n*** Out of Memory!\n"; // No ! exit(-1); // Exit the program. } cout << "\nEnter a double float value : "; cin >> *dPtr; // Dereference Ptr to store an input value. cout << "\nYou entered : " << *dPtr; // Dereference Ptr to display value.
49
Pointers and the New and Delete Operations (cont …)
Since we do not have an inexhaustible amount of dynamic memory to allocate from the free store or heap we should always reclaim or unallocate any previously reserved memory when it is no longer required, and this can be done using the delete operation. For example, we could reclaim / unallocate the memory previously allocated, via the new operation above, using the following : delete dPtr; // Deallocate memory reserved earlier.
50
Pointers and the New and Delete Operations (cont …)
If we try and use or dereference the pointer after this delete operation, such as : cout << *dPtr; then unpredictable results may occur. For example, you could access memory no longer reserved by this program and cause a system crash. It is good practice to set the pointer to NULL memory address (i.e. zero) after the delete operation : delete dPtr; // Reclaim the memory. dPtr = 0; // Point to the Null - for safety ! and then test that the pointer does not point to zero before using it.
51
Memory Leaks If you do not deallocate memory that you have previously reserved, and the program exits, then the memory will remain allocated – even after the program has ended ! Such a program is said to have a memory leak. If you repeatedly run the program (which allocates memory but does not unallocate it), then more and more of your computers free memory will be reserved. Eventually, your computer will run out of free memory, and the program will not be able to run at all. Memory leaks are the sign of sloppy or careless work. Always ensure your programs have no memory leaks.
52
Structure Pointer : Indirect Component Selection Operator (->)
In C++ there is another operator, called the indirect component selection operator (->) which combines the functionality of the pointer dereferencing operator (*) and the dot component selection operator (.). A common name for the indirect component selection operator (->) is the arrow. Let's see this in action : struct Student_Record { int Stud_ID; int Exam_Marks; };
53
Structure Pointer : Indirect Component Selection Operator (->) (cont …)
We could then define a pointer as follows : Student_Record *sPtr; // Student Pointer - uses no memory ! And, then allocate memory for the structure object : sPtr = new Student_Record; // Allocate memory. if (sPtr == 0) // Was the memory allocated successfully ? { cerr << "\n*** Out of Memory!\n"; // No ! exit(-1); // Exit the program. }
54
Structure Pointer : Indirect Component Selection Operator (->) (cont …)
We could then access the fields in the structure for this object using a mixture of pointer dereferencing and dot notation : cout << "\nEnter a Student ID : "; cin >> (*sPtr).Stud_ID; cout << "\nYou entered : " << (*sPtr).Stud_ID; This can look a bit messy / confusing. Arrow to the rescue !! cin >> sPtr->Stud_ID; cout << "\nYou entered : " << sPtr->Stud_ID;
55
Structure Pointer : Indirect Component Selection Operator (->) (cont …)
As you can see, the arrow makes the code nicer to look at. At the end, we need to unallocate the memory we had previously reserved : delete sPtr;
56
Arrays and the New and Delete operations – Pointer to an Array
In practice the new operation is mostly used to dynamically allocate arrays. e.g. arrays of structures. Arrays that are declared using the new operation are no longer constrained to a fixed size that is set when the program is compiled. Instead, the array size can be set at run-time, and the only real limits for the size of the array is the amount of free memory on your computer. Arrays that are allocated at run-time are said to be dynamic arrays. There are two main strategies that we will look at for the allocation of arrays dynamically. Each method has its own advantages and disadvantages.
57
Strategy #1 : Prompt the user to enter the size of the array, and then allocate all in one new operation. int NumEntries; // The size of our dynamic array. float *fPtr; // Pointer to our dynamic array int count = 0; cout << "How many values do you require : "; cin >> NumEntries; fPtr = new float [NumEntries]; // Allocated memory for our entire array. if (fPtr == 0) // Was the memory allocated successfully ? { cerr << "\n*** Out of Memory!\n"; // No ? exit(-1); // Exit the program. }
58
Strategy #1 : Prompt the user to enter the size of the array, and then allocate all in one new operation. (cont …) for (count = 0; count < NumEntries; count++) { cout << "\nEnter value [" << (count+1) << "] : "; cin >> fPtr [count]; // Store value in array. } cout << "\nYou entered these values :" << endl; cout << "\nValue [" << (count+1) << "] = " << fPtr [count]; // Display the array element. delete [] fPtr; // Unallocate / Reclaim ALL memory reserved for the array. fPtr = 0; // Set pointer to zero - for safety !
59
Strategy #1 : Prompt the user to enter the size of the array, and then allocate all in one new operation. (cont …) The advantages of this method are : We can allocate memory in one big block – which makes our code simple. The size of the dynamic array is only limited by the amount of free memory in our computer. We don't need to use any pointer dereferencing to access the data at each row / element – because we have a pointer to an array - we can just use stock standard array notation once the array is allocated. The disadvantages of this method are : Once the memory has been allocated, the array size cannot be changed. So, while or after entering array data, for example, the user cannot decide to enter 20 more values than they originally requested.
60
Strategy #2 : Use a static array of pointers.
For this method we will declare a static array of pointers – which contains room for more elements than we will ever need – and then we can allocate memory for each row of the array as we go. This enables the user to keep entering data for as long as they like (as long as they don't exceed the array of pointers size).
61
Strategy #2 : Use a static array of pointers. (cont …)
const MAX_SIZE = 200; // The maximum size of our array. int NumEntries; // The actual size of our dynamic array. float *fPtr [MAX_SIZE]; // Static array of pointers. int count = 0; char User_Input = 'Y'; while ((count < MAX_SIZE) && (User_Input == 'Y')) { fPtr [count] = new float; // Allocated memory for the current array row. if (fPtr [count] == 0) // Was the memory allocated successfully for this row ? cerr << "\n*** Out of Memory!\n"; exit(-1); // Exit the program. }
62
Strategy #2 : Use a static array of pointers. (cont …)
cout << "\nEnter value [" << (count+1) << "] : "; cin >> *fPtr [count]; // Store value in array. cout << "Enter more data (Y/N) ?"; cin >> User_Input; cin.ignore (80, '\n'); if ((User_Input == 'y') || (User_Input == 'Y')) { User_Input = 'Y'; // Convert to uppercase. count++; } } // while loop NumEntries = count;
63
Strategy #2 : Use a static array of pointers. (cont …)
cout << "\nYou entered these values :" << endl; for (count = 0; count <= NumEntries; count++) { cout << "\nValue [" << (count+1) << "] = " << *fPtr [count]; // Display the array element. } // For each new operation, we need a corresponding delete operation. delete fPtr [count]; // Unallocate memory reserved for this array row. fPtr [count] = 0; // Set pointer for this row to zero - for safety !
64
Strategy #2 : Use a static array of pointers. (cont …)
The advantages of this method are : The user can keep entering data until they have entered enough – as long as the maximum size of our array of pointers is not exceeded. Once the user has finished entering data, we can (later on) allow the user to then append more data onto the end of what they have already entered – as long as the maximum size of our array of pointers is not exceeded. The disadvantages of this method are : We must use a new and delete operations to allocate and deallocate the memory for each row / element of our array – which makes our code more complex. We need to use pointer dereferencing to access the data at each row / element – because we have an array of pointers instead of a pointer to an array - which is not really a big deal, but we do have to remember to dereference !
65
The void pointer type and Type Casting
On some occasions you may decide that you require a void pointer type. Void pointers are just like void functions – they have no type. Be very careful if you use a void pointer as it can lead to problems, and it can be a dangerous programming practice. In fact, C++ is so worried when you use a void pointer, that it makes you type cast it for many operations.
66
The void pointer type and Type Casting (cont …)
int number_1 = 10; // Declare and initialise two integers. int number_2 = 20; void *Ptr = NULL; // Declare a void pointer - has NO type ! Ptr = &number_1; // Assign our Void Pointer to the address of number_1 // Display the value pointed to by our Void Pointer cout << "\nVoid Pointer Dereference = "; cout << *Ptr; // ERROR: dereferencing a void pointer ! We must typecast ! cout << endl; // Assign the value pointed to by Ptr to number_2 number_2 = *Ptr; // ERROR: dereferencing a void pointer ! We must typecast ! cout << "\nNumber 1 = " << number_1 << ", Number 2 = " << number_2 << endl;
67
The void pointer type and Type Casting (cont …)
int number_1 = 10; // Declare and initialise two integers. int number_2 = 20; void *Ptr = NULL; // Declare a void pointer - has NO type ! Ptr = &number_1; // Assign our Void Pointer to the address of number_1 // Display the value pointed to by our Void Pointer cout << "\nVoid Pointer Dereference = "; cout << *(int*)Ptr; // The first * is the dereference. // (int *) is the cast or type casting. cout << endl; // Assign the value pointed to by Ptr to number_2 number_2 = *(int *)Ptr; // The first * is the dereference. cout << "\nNumber 1 = " << number_1 << ", Number 2 = " << number_2 << endl;
68
Array of Pointers : 2D Arrays
Quite a useful structure to have is an array of pointers with a fixed number of rows with variable size / length columns. Such an array would be a two dimensional (2D) array, and would provide efficient storage for an array of variable length strings. Such an array could be declared as follows : char *Ptr_Words[5];
69
Array of Pointers : 2D Arrays (cont …)
const int MAX_STRINGS = 5; const int MAX_STRING_SIZE = 81; char *Ptr_Words [MAX_STRINGS]; // Base address of 2D array char answer [MAX_STRING_SIZE]; int row = 0; cout << "\nPlease enter 5 strings of any length up to 80 characters :" << endl;
70
Array of Pointers : 2D Arrays (cont …)
for (row = 0; row < MAX_STRINGS; row++) { cout << "\nEnter string " << (row + 1) << " "; cin.getline (answer, MAX_STRING_SIZE); // Allocate memory for this string - add 1 for the null terminator. Ptr_Words [row] = new char [strlen (answer) + 1]; if (Ptr_Words [row] == 0) // Was the memory allocated successfully ? cerr << "\n*** Out of Memory!\n"; // No ! exit(-1); // Exit the program. } // Store String in array. strcpy (Ptr_Words [row], answer);
71
Array of Pointers : 2D Arrays (cont …)
// Display Strings entered by the user. for (row = 0; row < MAX_STRINGS; row++) { cout << "\nString Num = " << (row + 1) << ", Length = " << strlen (Ptr_Words [row]) << ", String = " << Ptr_Words [row] << endl; } // Clean-up deallocate memory. delete [] Ptr_Words [row]; // Unallocate memory reserved for string. Ptr_Words [row] = 0; // For safety !
72
Review of this week's material
Pointers will seem difficult at first ! Your understanding of pointers is crucial if you plan to undertake further studies in C++, Java, etc. As with everything we have covered this term, the only way to learn is hands on experience : write lots of programs using pointers until you become comfortable with them.
Similar presentations
© 2025 SlidePlayer.com Inc.
All rights reserved.