Lesson # 7. Pointer Basics

Theory

1. Pointer Basics

  • Pointers are a way to specify the location of a variable. Instead of storing a value such as 5 or the c character, the pointer value is the location of another variable.
  • Pointer variables have a size (the number of bytes in memory), a name, and a value. They must be declared and initialized.
  • «Pointer» (by itself) it is not a type. This is a type constructor — a language construct that, when applied to another type, gives us a new type. In particular, adding an asterisk ( * ) after any type names the type that is a pointer to that type. For example:
  • /*
    declares a variable named my_char_pointer 
    with the type - pointer to char 
    (a variable pointing to a character)
    */
    char * my_char_pointer; // pronounced "char star my char pointer"
    Declaring a pointer
  • The pointer declaration tells us the name of the variable and the type of the variable that this pointer will point to.
  • Assigning to a pointer
  • We can assign to pointers, changing their values. In the case of a pointer, changing its value means changing where it points.
  • We have to initialize a pointer (giving it something to point at) before we use it for anything else.
  • If we do not initialize a pointer before we use it, we have an arrow pointing at some random location in our program (this may cause the program’s crash).
  • To get an arrow pointing at some address of that cell in memory, we need a way to name that box, and then we need to use the & operator (the operator is named the «address-of» operator). Conceptually, the & operator gives us an arrow pointing at its operand.
  • /* you can see code that declares an integer x 
    and a pointer to an integer xPtr */
    int x = 5;
    int *xPtr;
    xPtr = &x; // sets the value of the variable xPtr to the address of x
  • the x is initialized to 5 in the same line in which it is declared;
  • the value of xPtr is initialized to the location of x;
  • after it is initialized, xPtr points to the variable x.
  • The code &x = 5; will not compile. A programmer can access the location of a variable, but it is not possible to change the location of a variable.
  • Dereferencing a pointer

  • Once we have arrows pointing at locations of variables, we want to make use of them by «following the arrow» and operating on the box it points at the end of the arrow (when the value of the variable is changing). Following the arrow is accomplished using the star symbol *, a unary (унарный) operator that dereferences the pointer.
  • 6
    7
    
    // An example of the use of the derference operator: 
    *xPtr = 6; // changes the value that xPtr points at 6
  • Note that the green arrow indicates that this line has not been executed yet, hence x still has the value 5 in the conceptual representation. Once line 7 is executed, however, the value will be 6.
  • Two contexts in which you will see the star (*) symbol, don’t mix them:
    1. In a variable declaration, such as int *p;, the asterisk is part of the type name, and tells us that we want a pointer to some other type (in our example, int * is the type of p).
    2. When the asterisk is the dereference operator. For example, the code r = *p; gives the variable r a new value, namely the value inside the memory cell that p points to. The code *p = r; changes the value inside the memory cell that p points at to be a new value, namely the value of the variable r.

  • When you work with pointers, you will use the asterisk first to declare the variable and then later to dereference it. Only variables that are of a pointer type may be dereferenced.
  • int * q = &y; // is the same as the two statements:
    int *q; 
    q = &y;
    Summarizing
  • There are only three basic pointer actions — declaring, assigning (including initializing), and dereferencing.

Correct version of swap

Solution: We have to pass the swap function pointers to the original variables a and b, as it follows below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
using namespace std;
 
void swap(int *x, int *y) {
	int temp = *x; // temp temporarily stores the value that x points at, =3
	*x = *y; // we take the value that y points to, which is the value of b (=4) 
                  //and store that in the value that x points to, namely the variable a
	*y = temp; // we take the temp = 3 and store that into the integer that y points at, 
                    //which is the variable b
}
 
int main() {
	int a = 3;
	int b = 4;
 
	swap(&a, &b);
	cout<<"a = "<< a<< ", b = "<<  b << endl;
	system("pause");
	return 0;
}
  
the swap function takes two arguments, x and y, which are pointers to integers;
as we access the values of the integers that x and y point to in the code inside swap, we’re going to have to de-reference x and y. And so, that’s why we’ll see these asterixes (*) before x and y throughout the code;
inside the main, instead of passing in a and b, we now have to pass in the address of a and address of b.

A program and memory

  • On a 32-bit machine, where addresses are 32 bits in size, the entire memory space starts with 0x00000000 (in hexadecimal format, each 0 represents 4 bits of 0) and ends at 0xFFFFFFFF (recall that 0x indicates hexadecimal, and that each F represents four binary 1’s). Each program has this entire address space at its disposal and there is a convention for how a program uses this range of addresses.
  • Let’s look at the figure to understand where the various components of the program are stored in memory.

    Null
  • When we use NULL, we’re going to indicate that it does not point at anything. Whenever we have NULL, we can use the pointer itself (which just has a numeric value 0), however, we cannot follow the arrow (as it does not point at anything).
  • The NULL pointer has a special type that we have not seen yet — void *. A void pointer indicates a pointer to any type, and is compatible with any other pointer type — we can assign it to an int *, a double*, or any other pointer type we want.

2. Accessing an Array

  • Accessing an array element using pointer arithmetic works fine, and sometimes is the natural way to access elements. But in the case when we just want the n-th element of an array, it would be cumbersome to declare a pointer, add n to it, then dereference it.
  • Let’s look at an example of a function that prints the elements of an array and uses pointer manipulation to access those elements:
  • // * array : the name of the array is a pointer to its first element, that is 3
    void printArr(int *array, int n) { 
    	int *ptr = array; // *ptr is a pointer to point at the loaction which *array pointer points at
    	for (int i = 0; i < n; i++) {
    		cout << *ptr; // 1st step output 3, 2dt step output 1, 3d step output 7,
                    // to make pointer point at the memory cell of the next element in the array:
                    // example address ptr = 0x00b5f9a0
    		ptr++;  // 1st 0x00b5f9a4, 2nd 0x00b5f9a8, 0x00b5f9ac, 
                            // no element in the array, but there be no error
                            // it would cause the problem if we dereference pointer 
    	}
    }
    int main(){
    	int arr[4] = { 3,1,7,2 };
    	printArr(arr, 4);
    }
  • We can achieve this goal more succinctly by indexing the array. When we index an array, we write the name of the array, followed by square brackets containing the number of the element we want to refer to. It is important to note that in C++, array indexes are zero-based — the first element of the array is myArray[0].
  • // * array : the name of the array is a pointer to its first element, that is 3
    void printArr(int *array, int n) { 
         for (int i = 0; i < n; i++) {
    	 cout << array[i]; // 1st step output 3, 2dt step - 1, 3d step - 7, and then 2
         }
    }
    int main(){
    	int arr[4] = { 3,1,7,2 };
    	printArr(arr, 4);
    }
    To put the pointer at the beginnig of array:
    int a[] {1,3,5,7,9};
    int* p = a;
    // shift the pointer to the second element:
    p++;
    p++;
    Access by index:
    int a[] {1,3,5,7,9};
    int* p = a;
    cout << *(p + 1) << endl; // = a[1]=3
    cout << *(p + 2) << endl; // = a[2]=5

Labs and tasks

All the tasks that you did not have time to complete in class automatically become your homework. The homework must be completed before the next lesson.

1. Pointer Basics

Lab 0:
To do: Create a function to calculate the addition of pointer and a variable of integer type. That is, function should return the sum of the pointer and the variable value.

Note: The signature of the function must be as follows:

int addP(int var1, int var2);

Expected output:

please, enter the first number (for pointer):
>>>2
please, enter the second number (to add):
>>>500
the result: pointer + 500 = 502

✍ How to do:

  1. To do the lab create empty console application with a name Lesson7. Add files: main7lab0.cpp, L7lab0.cpp, L7lab0.h.
  2. Include all the needed libraries and files.
  3. Open the main7.cpp file in the code editor. Type the code to output a message to the user, and enter the value of two variables. The first of them is for pointer to point to the location of it, and the second for adding to that pointer:
  4. cout << "Lab 0:" << endl;
    cout << "please, enter the first number (for pointer): "<< endl;
    int var1; 
    cin >> var1;
    cout << "please, enter the second number (to add): " << endl;
    int var2; 
    cin >> var2;
    
  5. After that, let’s turn to the string7.h file and include all the necessary directives:
  6. #pragma once
    #ifndef STRING7_H
    #define STRING7_H
    
    #include 
    // your code is here
    #endif
    
  7. The signature of the function was recommended in the task text, let’s add that code with a comments of the task text:
  8. //lab 0: Create a function to calculate the addition of pointer and a variable of integer type
    int addP(int var1, int var2);
    
  9. Now let’s create the implementation of the function. To do this, open the string7.cpp file, include all the directives you need, and add the code:
  10. //lab 0: to calculate the addition of pointer and a variable of integer type
    int addP(int var1, int var2) {
    	int* p_var = &var1;
    	return *p_var + var2;
    }
    
    Here, p_var is initialized with the location of var1. The function returs a derference operator — *p_var — which means we take the value that p_var points to (that is the value of var1) and add the value of var2 to it.
  11. Now you can call the function we’ve created and print the results. You should do this inside the main7.cpp file:
  12. cout << "Result:" << endl;
    cout << "pointer + " << var2 << " = " << addP(var1,var2) << endl;
    
  13. Add the code to return a value 0 inside the main function. Run the program and check the output.
Task 1:

To do: Create an application with a function that takes two arguments, they are integer numbers (var1 and var2), and prints the following calculations with its arguments, using pointers (p1 and p2):

1) multiplication of pointer and variable: p1 * var1
2) addition of pointers:  p1 + p2
3) changing the value of var1 = 5 using pointer p1
4) changing the address of pointer: p1(address) = p2(address)

Note 1: To change the value of a variable:

int* p1 = &var1; // if p1 is a pointer
*p1 = 1; // 1 is a new value for the variable

Note 2: The signature of the function must be as follows:

void task1(int var1, int var2);

Expected output:

Task 1:
please, enter the first var value:
>>5
please, enter the second var value:
>>10
multiplication of pointer and variable: 25
addition of pointers: 15
the result of changing the value of var1, var1 = 5
the result BEFORE changing the address, p1 = 0098F7E8
the result of changing the address, p1 = 0116FC90

[Solution and Project name: Lesson_7task1, file name L7Task1main.cpp, L7Task1imp.cpp, L7Task1.h]

2. Accessing an array

Lab 1:
To do: Create a function to calculate the addition of array elements. The task should be done using two functions. The first function shouldn't use a pointer accessing the array element by index, but the second function should use.

Note 1: The signature of the functions must be the same except the names:

int sumArray1(int * array, int n);
int sumArray2(int * array, int n);

Expected output:

array:
1 2 3 4 5
the result of the first function: sum = 15
the result of the second function: sum = 15

✍ How to do:

  1. At first, let's create a function which iterates over the array elements without using a pointer accessing the array element by index:
  2. int sumArray1(int * array, int n) // array points at the value 1
    {
    	// start with sum being 0
    	int AnswerSum = 0;
    	// count from 0 to n
    	for (int i = 0; i < n; i++) {
    		// update sum by adding array[i]
    		AnswerSum += array[i];
    
    	}
    	// give sum as your answer
    	return AnswerSum;
    }
    
    int main() {
    	int data[] = { 1,2,3,4,5 };
    	cout << "array: " << endl;
    	for (auto x : data) {
    		cout << x << " ";
    	}
    	cout << endl;
    	int sum= sumArray1(data, 5);
    	cout << "the result of the first function: sum = " << sum<< endl;
    
  3. Consider memory location:
  4. 1. The first loop iteration:
    2. The second loop iteration:
    3. The third loop iteration:
    4.The last loop iteration:
    Back to main function
    and destroying the frame of sumArray1:
  5. Now, let's create a function which iterates over the array elements using a pointer accessing the array element by index:
  6. int sumArray2(int * array, int n) // array points at the value 1
    {
    	// start with sum being 0
    	int AnswerSum = 0;
    	//  loop counter is a pointer to point at the address of the 1-st array element
    	for (int *p = array; p < array + n; p++) {
    		// update sum by adding redereference of p
    		AnswerSum += *p;
    
    	}
    	// give sum as your answer
    	return AnswerSum;
    }
    
  7. Call the function within the main and run the program.
Lab 2, Arrays:
To do: An array of integers is given (1, 2, 3, 4, 5, 6, 7, 8, 9). Make every second element being set to 0 (that is even elements must be = 0). You should use indexing the array.

Note 1: The signature of the function should be as follows:

void DelEveryEven(int* array, int n);

Expected output:

Lab 2, Arrays:
The array before lab is done:
1 2 3 4 5 6 7 8 9
Result:
1 0 3 0 5 0 7 0 9

✍ How to do:

  1. To do the lab open the application you've created for lab1 or, if you haven't done it, so make the next: Create empty console application with a name Lesson7. Add files: main7.cpp, string7.cpp, string7.h.
  2. Include all the needed libraries and files (if you didn't do it before).
  3. Open the main7.cpp file in the code editor. Type the code to output a message to the user, and to initialize an array in the same line in which you declare it:
  4. cout << "Lab 2, Arrays:\n";
    int my_arr[] = { 1,2,3,4,5,6,7,8,9 };
    
  5. Print the array elements before the lab is done:
  6. cout << "The array before lab is done: " << endl;
    for (auto x : my_arr) {
    	cout << x << " ";
    }
    
  7. After that, let's turn to string7.h file and include all necessary directives (if you didn't do it before for the lab1):
  8. #pragma once
    #ifndef STRING7_H
    #define STRING7_H
    
    #include 
    
    #endif
    
  9. The signature of the function was recommended in the task text, let's add that code with a comments of the task text:
  10. // lab 2, arrays: An array of integers is given (1, 2, 3, 4, 5, 6, 7, 8, 9). 
    // Make every second element being set to 0
    void DelEveryEven(int* , int);
    
  11. Now, let's create the implementation of the function. To do this, open the string7.cpp file, include all the directives you need, and add the code:
  12. //lab 2, arrays:  An array of integers is given. 
    // Make every second element being set to 0 (that is even elements must be = 0).
    void DelEveryEven(int* array, int n) {
    	for (int* p = array + 1; p < array + n; p = p + 2) {
    		*p = 0;
    	}
    }
    
    The * array parameter is initialized to the location of the array, that will be passed on.
    Here, as a loop counter we use a pointer to point at the address of the 1-st array element+1.
    Let's consider the line int* p = array + 1;.

  13. array returns the memory cell of the first element. But we must make the second element being set to 0. So, we have array + 1.
  14. Having *p = 0 we dereference the pointer, and we make the second element equal to 0.
  15. To do the same manipulations with the fourth element (even), we can increment the pointer by 2 (p = p + 2).
  16. array + n means the memory cell of the first element plus n, which is 9. This provides an exit from the loop.
  17. Now you can call the function we've created and print the results. You must do it inside the main7.cpp file:
  18. DelEveryEven(my_arr, 9);
    cout << "Result: " << endl;
    for (auto x : my_arr) {
    	cout << x << " ";
    }
    cout << endl;
    
  19. Add the code to return a 0 value inside the main function. Run the program and check the output.
Task 2_0:

To do: An array of integers is given. Create an application with two functions which return a value of the largest element of the array. One of the functions should be without using of a pointer accessing the array element by index, another one should use the pointer.

Note: The signature of the functions should be the same:

int maxArray1(int * array, int n);
int maxArray2(int * array, int n);

Expected output:

array:
1 2 3 4 5
the result of the first function: max = 5
the result of the second function: max = 5

[Solution and Project name: Lesson_7task2_0, file name L7Task2_0main.cpp, L7Task2_0imp.cpp, L7Task2_0.h]

Task 2:

To do: An array of positive and negative integers is given. Create an application with a function that sets its negative elements to positive values.

Note: The signature of the function should be as follows:

void SetNegToPos(int* array, int n);

Expected output:

Task 2:
The array before lab is done:
1 -2 3 4 5 -6 7 8 -9
Result:
1 2 3 4 5 6 7 8 9

[Solution and Project name: Lesson_7task2, file name L7Task2main.cpp, L7Task2imp.cpp, L7Task2.h]

Task 3:

To do: An array of integers is given. Create a function that returns an index of the largest element of the array.

Note 1: The signature of the function should be as follows:

int findLargestInd(int * array, int n);

Note 2: Withitn the function you should make the comparison:

if (array[i] > array[largestIndex]) 
{
...
}

Expected output:

Task 3:
array:
1 5 3 2 1
index of the largest element = 1

[Solution and Project name: Lesson_7task3, file name L7Task3main.cpp, L7Task3imp.cpp, L7Task3.h]

3. String characters access with pointer indexing

Lab 3:
To do: Two strings are given. Create the function which returns 1 if the strings are equal, and returns 0 otherwise.

Note 1: To access the characters of a string with pointer indexing you should use char type for that string:

char str1[] = "Apple";

Note 2: The signature of the function should be as follows:

int stringEqual(const char * str1, const char * str2);

Expected output:

// lab 3:
the result for 'apple' and 'apple' is: 1
++++
// lab 3:
the result for 'apple' and 'apples' is: 0

✍ How to do:

  1. Let's declare a function with two arguments, they are constant char pointers to the strings:
  2. int stringEqual(const char * str1, const char * str2){
    ...
    }
    
  3. Then, we're going to declare the pointers pointing at the first letter of str1 and the first letter of str2. Add the code into the function:
  4. const char* p1 = str1;
    const char* p2 = str2;
    
  5. Then, we're going to create a loop to be repeated as long as what p1 points at is the same as what p2 point at. We should use dereference operator to take the values, that is to take the characters of the strings:
  6.  while (*p1 == *p2) {
    
    	}
    
  7. Then, we need to check if p1 is equaled to \0, that is the last character which means a termination of a string. In this case the function must return 1 (that is answer "yes"):
  8. if (*p1 == '\0') {
        return 1;
    }
    
  9. After, we advance p1 and p2 to the next letter:
  10. p1++;
    p2++;
    
  11. After the loop we need to return answer "no", that is in our function means 0:
  12. return 0;
    
  13. Now, we're going to return to the main function and create two c-string. And then to call the function:
  14. char str1[] = "apple";
    char str2[] = "apples";
    cout << "the result for \'"<< str1 <<"\' and \'"<< str2 <<"\' is: " << stringEqual (str1,str2) << endl;
    
  15. Run the program and check the output.
Task 4:
To do: Create a function to check if c-string contains a combination of letters 'mo'. Return 1 (if found) or 0 (if not found). It is not allowed to use any standard functions. You should use pointer.

Note 1: The signature of the function must be as following:

bool findMo(const char * str);

Note 2: To check if this is not an end of the string, you can use the following loop condition:

while (*str != '\0')

Expected output:

Task 4:
the result for 'the movie was nice' is: 1

[Solution and Project name: Lesson_7task4, file name L7Task4main.cpp, L7Task4imp.cpp, L7Task4.h]

Lab 4:
To do: Reverse the order of characters in the string. To do this, define a pointer to the last character and, moving two pointers from the beginning and end of the line to the middle, swap the corresponding characters.

Note 1: To access the characters of a string with pointer indexing you should use char type for that string:

char str[] = "Hello world";

Note 2: The signature of the function should be as follows:

void reverseString (char * str);

Expected output:

// lab 4:
The string before lab is done:
Hello world
The string after lab is done:
dlrow olleH

✍ How to do:

  1. To do the lab open the application you've created for lab1 or, if you haven't done it, so make the next: Create empty console application with a name Lesson7. Add files: main7.cpp, string7.cpp, string7.h.
  2. Include all necessary libraries and files (if you didn't do it before).
  3. Open the main7.cpp file in the code editor. Type the code to output a message to the user, and to initialize an array of char type (the string will be treated as an array of chars):
  4. cout << "Lab 4:" << endl;
    char str[] = "Hello world"; 
    cout << "The string before lab is done: " << str << endl; 
    
  5. After that, let's turn to string7.h file and include all the needed directives (if you didn't do it before for the lab1):
  6. #pragma once
    #ifndef STRING7_H
    #define STRING7_H
    
    #include 
    
    #endif
    
  7. The signature of the function was recommended in the task text, let's add that code with a comments of the task text:
  8. // Lab 4: Reverse the order of characters in the string. 
    void reverseString(char* str);
    
    str is initialized to the location of str.
  9. Now, let's create the implementation of the function. To do this open the string7.cpp file, include all the directives you need, and add the code:
  10. // Lab 4: Reverse the order of characters in the string. 
    void reverseString(char* str) {
    	char* str1 = str;
    	char* res = nullptr;
    	while (*str1) {
    		res = str1; 
    		*(str1++); 
            } // end of while
    // ...
    } // end of function
    
    Here, we define a pointer to string's first character. The line (char* res = nullptr;) returns a "null pointer", that is 0x00000000 address. Then, we use the while loop to iterate over the array's elements addresses. After the loop, the variable res will get the value of the address of the last character of the string, that is can be 0x00cffae6, for example, for "d" character).
  11. Then we're going to assign the value of the characters to the *str variable, moving the pointer from the last character to the middle. To do it, we use the temporary variable t. And at the same time we're going to substitute the values of the array elements, beginning from the last element, and assigning the value of the first element to it. We should do the dereferencing for this purpose:
  12. 	auto l = res;
    	while ((l) > (str)) {
    		auto t = *l; // temporary variable to store the values, moving from the last, 'd'
    		*l = *str; // dereferencing, we take the value that *str points to, which is the value of str 
                               //and store that in the value that *l points to
    		*str = t; // changes the value that *str points to
    		l--; // moving from the last address to the middle
    		str++; // moving from the first address to the middle
    	}
    
    
  13. Now you can call the function we've created and print the results. You must do it inside the main7.cpp file:
  14. reverseString(str);
    cout << "The string after lab is done: " << str << endl;
    
  15. Add the code to return a 0 value inside the main function. Run the program and check the output.
Task 5:
To do: There is a string that contains the positive integer ('Happy 2021 year to u!'). Create a function to display a substring containing this number.

Note 1: Use the following function signature. It returns integer (you should make a number using the digits):

void numbFromStr(char* str);

To understand where the end of the string is, you can use the following loop:

while (*str1)
  {
	  pLast = str1; // the address of the last character
	  *(str1++);
   }

Note 2: The result must be of string type, so you should include the string library:

#include <string>

Note 3: to check if the character is a digit, you should create a function:

bool isdigit(char c) {
	if ((c <= '9') && (c >= '0')) {
		return true;
	}
	else
		return false;
}

  
Expected output:

Task 5:
The string for the task is: Happy 2021 year to u!
the substring of numbers: 2021

[Solution and Project name: Lesson_7task5, file name L7Task5main.cpp, L7Task5imp.cpp, L7Task5.h]

Task 6:

To do: A string of characters is given. Convert all uppercase Latin letters to lowercase in it. The function should return the number of substitutions performed.

Note 0: You should use the character string type:

char s[] ="...";

Note 1: Use the following function signature. It returns an integer (the number of substitutions):

 int to_lower(char * str);

Note 2: To check if this is not an end of the string, you can use the following loop condition:

while (*str != '\0')

Note 3: To check if a letter is uppercase letter, you should use the following function:

bool isCappital(char ch)
{
    return (ch >= 'A' && ch <= 'Z');
}

Note 4: To convert a letter to lowercase, you should use the standard tolower(ch) function.
  
Expected output:

Task 6:
The string for the task is: HelLo wOrLD
The result is: hello world

[Solution and Project name: Lesson_7task6, file name L7Task6main.cpp, L7Task6imp.cpp, L7Task6.h]

Task 7:

To do: A string of characters is given representing an arithmetic expression of the form "<digit>±<digit>±...±<digit>", where the arithmetic operator "+" (plus) or "" (minus) is located in place of the operation sign "±" (for example, "4+7-2-8"). Calculate the result of this expression (an integer).

Note 1: Use the following function signature. It returns an integer (the number of substitutions):

int eval(char * expr);

Note 2: To find the first operand (digit) you can use the following code:

int res = *expr++ - '0'; // here *expr++ is the first character in the string

or a function:

char r = *expr; //*expr is a code in ascii-table
int res = atoi(&r); // converts code to digit

Note 3: To store the operation or a digit you can use the following concept:

char ch1 = *expr++; //arithmetic operator or digit

ch1 will be set to 53'9' - 53 is an address and 9 is a digit stored.

// to take the digit only insted of ch1 you must use :
ch1-'0';

Expected output:

Task 7:
The string for the task is: 9+4-2-0
The result is: 11

[Solution and Project name: Lesson_7task7, file name L7Task7main.cpp, L7Task7imp.cpp, L7Task76.h]