Pointers, Variables, and Memory
Variables and Pointers When you declare a variable, memory is allocated to store a value. A pointer can be used to hold the address to a chunk of memory. The operator & is used to return a variable’s memory address int intVar;// allocate memory by // declaring a variable cout << " Address = " << &intVar << endl;
Variables and Pointers A pointer variable is used to hold an address to a memory location. A pointer variable has a type associated with it, and the memory address it points to, should hold data of that type int myInt = 0;// Allocates memory, stores 0 int *pMyInt;// Declares an empty pointer // variable
Dereferencing Pointers If we want to set or get the value that is stored at the memory address in the pointer then we have to use operator* int myInt = 0;//Allocates memory, stores 0 int *pMyInt;//Declares an empty pointer pMyInt = &myInt;//Puts address in the ptr variable name myInt pMyInt valuememory address 3012
Dereferencing Pointers If we want to set or get the value that is stored at the memory address in the pointer then we have to use operator* int myInt = 0;//Allocates memory, stores 0 int *pMyInt;//Declares an empty pointer pMyInt = &myInt;//Puts address in the ptr *pMyInt = 5;//puts 5 into myInt variable name myInt pMyInt valuememory address 3012
Dereferencing Pointers If we want to set or get the value that is stored at the memory address in the pointer then we have to use operator* int myInt = 0;//Allocates memory, stores 0 int *pMyInt;//Declares an empty pointer pMyInt = &myInt;//Puts address in the ptr *pMyInt = 5;//puts 5 into myInt cout << myInt << endl;//Prints 5 cout << *pMyInt << endl;//Also prints 5 cout << pMyInt << endl;//What prints? variable name myInt pMyInt valuememory address 3012
Where are the errors? int main(){ int m; int *pm; *pm = 5; int n; int *pn = &n; pn = 5; }
Where are the errors? int main(){ int m; int *pm; *pm = 5; int n; int *pn = &n; pn = 5; } ERROR! No address in pm //Correction pm = &m; *pm = 5;
Where are the errors? int main(){ int m; int *pm; *pm = 5; int n; int *pn = &n; pn = 5; } ERROR! No address in pm //Correction pm = &m; *pm = 5; ERROR! Missing operator* //Correction *pn = 5;
Pointers & Arrays Pointers are simply, by definition, variables that hold addresses. A name of an array also holds the address of the first element in the array. This value cannot change. Therefore, an array name can be considered to be a pointer constant.
Pointers & Arrays const int CAPACITY = 5; int myArray[CAPACITY];//Declare an array cout << myArray;//Prints its address for( int i=0; i<CAPACITY; ++i ) myArray[i] = i;//initialize the elements cout << *myArray << endl;//Prints 0 cout << myArray[0] << endl;//Prints myArray
Pointer Arithmetic Pointer arithmetic allows a few arithmetic operators for manipulating the addresses in pointers. In an array a, a is a constant pointer to the first element and a+1 is a constant to the second element. In the same way, if p points to the second element in an array, then p-1 point to the preceding element in that array, and p+1 points to the succeeding element.
Pointer Arithmetic int a[5] = {2, 4, 6, 8, 10}; int *p; p = &a[1]; cout << a[0] << ", " << p[-1]<< ", " << *(p - 1) << endl; cout << a[1] << ", " <<p[0]<< ", " << *(p) << endl; cout << a[2] << ", " << p[1]<< ", " << *(p + 1) << endl; Output: 2, 2, 2 4, 4, 4 6, 6, 6
Incrementing and Decrementing Addresses with Postfix and Prefix Operators Adding 1 to a pointer causes the pointer to point to the next element of the type being pointed to. Therefore p++ should point to the next element. However, when a combination of indirection and postfix operators are used ( as is often done), use the precedence table to figure out what gets executed first. Postfix operators have higher precedence than the indirection operator.
Incrementing and Decrementing Addresses with Postfix and Prefix Operators int *p; int a[5] = {12, 4, 16, 98, 50}; p = a; cout << (*p)++ << endl;// prints 12 cout << *p << endl;// print 4 cout << *++p << endl;// prints 16 cout << *p << endl; // prints 16 cout << (*p)-- << endl; // prints 16 cout << *p << endl; // prints 4 cout << *--p << endl; // prints 12 cout << *p << endl; // prints 12 Is this the same as *p++? Is this the same as *(--p) ?
Dynamically Allocating Arrays An array can be created “dynamically” with “new” “new” allocates a section of memory for the array, and then returns a pointer to it. This is a major advantage when you don’t know how large the array will be until the program is running int capacity, *myData; cin >> capacity; myData = new int[capacity];//Creates the array for( int i=0; i<capacity; ++i ) //Initialize it myData[i] = i; cout << myData[0] << endl; //Prints 0 delete [] myData;
Using delete Memory allocated with “new” must always be recovered by “delete” Always “delete” an array when you don’t need it anymore if you created it with “new” delete [] myData;
Function Arguments The variables that are used to pass data into a function or to return results Example bool isPalindrome( string forw, string rev ){ if( forw == rev ) return true; } Arguments can be passed – By value – the default in C++ – By reference arguments or parameters function name return type
Passing Arguments by Value Used to pass data only into a function When a variable is passed by value, a copy of it is made inside the function. The copy is destroyed when the function returns. Example: passing an argument by value void noChange( int n ){ n = n + n; } int main() { int num = 5; noChange( num ); cout << num << endl; //prints 5 }
Reference Argument Syntax int &count Indicates that an alias for the argument is used inside the function
Using References Used primarily for function arguments to implement “passing by reference” Advantage: Efficiency – Passing variables to functions by reference is very useful if you want to change or update a variable through a function. – If your variable is a relatively large variable, or a whole class or struct or array, it is always advisable to pass the variable by reference since big amounts of data need not be copied when evaluating them.
Passing Arguments by Reference # include void Square(int &pVal); main() { int Number=10; printf("Number is %d\n", Number); Square(Number); printf("Number is %d\n", Number); } void Square(int &pVal) { pVal *= pVal; printf("Number is %d\n", pVal); }
Passing Arguments by Pointer #include void Square(int *pVal); main() { int Number=10; printf("Number is %d\n", Number); Square(&Number); printf("Number is %d\n", Number); } void Square(int *pVal) { *pVal *= *pVal; printf("Number is %d\n", *pVal); }
Reference vs. Pointers References cannot be re-assigned. They are like constant pointers. A good rule of the thumb is to use references when you can, and pointers when you ‘have to’
Passing an Array to a Function Since an array name is actually a pointer to its first element, when an array is passed to a function, an address operator is not needed. int findMax ( int [], int ); int main() { int a[5] = {12, 4, 16, 98, 50}; cout << "The maximum value is " << findMax(a, 5) << endl; return 1; } int findMax ( int vals[], int numElem ) { int max = vals[0]; for (int i=1; i<numElem; i++ ) if (max < vals[i] ) max = vals[i]; return max; }
Passing an Array to a Function However, since an address is actually passed, the code can also be written thus: int findMax ( int [], int ); int main() { int a[5] = {12, 4, 16, 98, 50}; cout << "The maximum value is " << findMax(a, 5) << endl; return 1; } int findMax ( int *vals, int numElem ) { int max = vals[0]; for (int i=1; i<numElem; i++ ) if (max < vals[i] ) max = vals[i]; return max; }
Passing an Array to a Function Here are two other versions of findMax, this time using pointers instead of array subscripts: int findMax ( int *vals, int numElem ) { int max = *vals; for (int i=1; i<numElem; i++ ) if (max < *(vals + i) ) max = *(vals + i); return max; }
Passing an Array to a Function int findMax ( int *vals, int numElem ) { int max = *vals++;// gets the first element and // then increments for (int i=1; i<numElem; i++, vals++ ) if (max < *vals ) max = *vals; return max; }
Dynamically Allocated structs We can dynamically create a structure with “new” Use “new” to allocate memory and return a pointer Person *pDonald = new Person; When a pointer is used to set values stored in a structure, each field must be accessed by the “ -> ” operator pDonald->ID = 1005; pDonald->firstName = "Donald"; pDonald->lastName = "Knuth"; cout firstName << endl; delete pDonald;//Always delete after new Optional syntax: cout << (* pDonald ).firstName;
Dynamic Memory Allocation Data structures that use arrays, structs, and objects to store their data often use dynamic memory allocation to create the storage space More efficient – We don’t need to know in advance how much space to set aside – Pointers can be passed to functions more efficiently because pointer parameters are passed by reference
Struct Constructors Creating a structure that initializes itself struct Person{ int ID; string name; Person(int i = 0000, string n = "Adam") : ID(i), name(n) { } }; Constructor is called whenever a Person is declared Person adam;//Default values used cout << adam.name << endl;//Prints Adam Person david(1002,"David");//Arguments used Person *pFrank = new Person;//Default values used pFrank->name = "Frank"; Initializer list Default value Parameter Constructor name