Month: November 2016

Recursive Definitions in C++

Recursive definitions help us to define large finite and infinite sets. Recursive definitions are frequently used to define functions and sequences of numbers.

Recursive definitions have two parts. The first part, the ground or base case, contains the basic elements that are the building blocks for the other elements in the defined set. The second part allows for the construction of new objects using the elements defined in the first part. A trivial example is a list of the natural numbers, which begins with 0 and adds one from there, such that any number is the previous number plus one, going all the way back to 0.

#include 

using namespace std;

int main(){
    
    int iNumber;
    
    cout  iNumber;
    
    while(iNumber > 0){
        cout << iNumber << " = ";
        cout << --iNumber << " + 1" << endl;
    }
    
    cout << "Base Case: " << iNumber << endl;
    
    return 0;
    
}

Recursive definitions have two purposes: creating new elements, and testing whether or not an element belongs to a set. With testing, we reduce a complex problem to a simpler problem, and if that isn’t simple enough, we reduce it to a still simpler problem, again and again as needed, until we reach the base case. Being able to decompose big problems into smaller, more manageable problems is the essence of constructing algorithms, recursive or not.

The most famous basic example of recursion is the factorial, represented as !. A factorial function defines a base case of N equaling 0, which returns the value of 1. For each value N greater than 0, the value returned is N * (N – 1) . Thus, 5! = 5  * 4 * 3 * 2 * 1.

#include 

using namespace std;

int fact(int n);

int main(){
    
    int iNum;

    while(true){
        cout  iNum;
        if(iNum < 0){
            break;
        } else {
            cout << fact(iNum) << endl;
        }
        
    }    
    
    return 0;
    
}

Functions are allocated on the run-time stack in a stack frame. Each function contains its own state, including the contents of all automatic variables, the values of the function’s parameters, and by the return address indicating where to restart its caller.  Stack frames typically have a short lifespan as they are allocated when the function is called and deallocated when the function exits.

Because the system creates a new stack frame whenever a function is called it can handle recursion, as each function invocation is represented internally by a different stack frame storing a different state.

A good example of a recursive function is a function that raises a number x to an integer power. The function is defined as 1, if n equals 0, and x * x ^ (n – 1) if n is greater than 0. The base case produces a value of 1, which is passed back to the previous recursive call that has been waiting for this result. This previous recursive call then calculates its own return value, which is passed onto its previous recursive call, which has been waiting for the result, and so on.

#include 

using namespace std;

int exp(int iNum, int iExp){
	if(iExp == 0) { 
		cout << "1" << endl; 
		return 1;  
	} else {
		cout << iNum << " * ";		
		return iNum * exp(iNum, --iExp);
	}

}

int main(){

	int iNum, iExp;
	cout << iNum;
	cout << iExp;
	cout << exp(iNum, iExp) << endl;
	
        return 0;

}

A fun example of something that can be easily implemented using recursion is printing an input line in reverse order. It’s actually very simple to do in C++.

#include 

using namespace std;

void reverse(){
    char ch;
    cin.get(ch);
    if(ch != '\n'){
        reverse();
        cout.put(ch);
    }
}

int main(){
   
   cout << "Enter a line to have it written backwards: ";
    
    reverse();
    
    return 0;
    
}

The reverse() function up above works because each call to reverse() has its own local ch variable.

Pointers, New, and Delete

A data type has a set of values and a set of operations that can be performed on these values. Data types also typically have a name, such as double or int. The pointer data type, however, does not have a name; instead, it is represented by an asterisk. The values that belong to a pointer data type are the memory addresses of the computer. A pointer variable is thus a variable whose content is a memory address.

In C++, we declare a pointer variable by placing the asterisk symbol between the data type and the variable name. The value of a pointer variable is an address of another memory space; when we declare a pointer variable, we also specify the data type of the value stored in this other memory space.

int main()
{
	//pointer to an address storing an int
	int * iPtr;
	//pointer to an address storing a char
	char* chPtr;
	//pointer to an address storing a double
	double *dPtr;

    return 0;
}

Note that the asterisk can appear anywhere between the data type name and the variable name.

The asterisk symbol does double duty as both the stand-in for the pointer data type name, as well as the dereferencing operator (it is also the symbol for multiplication as well). The address of operator is represented by the & symbol.

The address of operator, &, is used to get the memory address of a variable. The dereferencing operator, *, is used to retrieve the value stored at the memory address that the pointer points to. The derefencing operator can also be used to store a new value in the memory address that is pointed to by the pointer.

using namespace std;

int main()
{
	int i = 42;
	int *iPtr = &i;

	cout << i << " is at " << &i << endl;
	cout << iPtr << " is at " << &iPtr << endl;

	cout << "The pointer stored at " << &iPtr << " stores the memory address " << iPtr << endl;
	cout << " which stores the value " << *iPtr << endl;

    return 0;
}

It is of course possible to have multiple pointer variables point to the same memory location. Likewise, we can even have pointers that point to other pointers!

using namespace std;

int main()
{

	double dOne = 19.99;
	
	double *dPtrOne = &dOne;
	double *dPtrTwo = &dOne;

	double **doubledPtr = &dPtrOne;

	cout << "dPtrOne is at " << &dPtrOne << " and stores the memory address " << dPtrOne << " which stores the value " << *dPtrOne << endl;
	cout << "dPtrTwo is at " << &dPtrTwo << " and stores the memory address " << dPtrTwo << " which stores the value " << *dPtrTwo << endl << endl;

	//we can change the value pointed to using either pointer
	*dPtrOne = 3.14159;
	//derefencing either pointer yields the changed value
	cout << "dPtrTwo points to the value " << *dPtrTwo << endl;

	cout << "doubledPtr is at " << &doubledPtr << " and stores the value " << doubledPtr << " which pointers to the value " << *doubledPtr <<  endl << endl;

	if (*doubledPtr == dPtrOne) {
		cout << "*doubledPtr and dPtrOne are the same" << endl;
	}

	if (*doubledPtr == &dOne) {
		cout << "*doubledPtr and &dOne are the same" << endl;
	}

	if (**doubledPtr == *dPtrOne) {
		cout << "**doubledPtr and *dPtrOne are the same" << endl;
	}
    return 0;
}

We can also create pointers that point to other data types like structures or classes.

int main()
{

	struct exampleStruct {
		int a;
		char b;
		double c;
	};

	struct exampleStruct exmp;
	struct exampleStruct *exmpPtr = &exmp;

	exmp.a = 73;
	exmp.b = 'z';
	exmp.c = 25.806975;

	cout << (*exmpPtr).a << endl;
	cout << (*exmpPtr).b << endl;
	cout << (*exmpPtr).c << endl;

    return 0;
}

Note that in C++ the dot operator has a higher precedence than the dereferencing operator. Thus, when dereferencing pointers to structs or class objects, we need to use parentheses to ensure that the pointer is dereferenced before the dot operator, or we can use the alternative arrow operator, ->, officially called the member access operator arrow.

We can allocate  and deallocate memory during the program’s execution using pointers. C++ provides two operators, new and delete, to create and destroy dynamically allocated variables. In C++, new and delete are reserved words.

The operator new can both allocate single variables as well as arrays of variables. The operator new allocates the memory for the designated type and returns a pointer to this memory.  Because dynamic variables can’t be accessed directly as they are not named, they must instead be accessed via the pointer that references the memory location where the dynamic variable is.

using namespace std;

int main()
{

	int *iPtr;
	double *dPtr;
	char *chPtr;

	iPtr = new int;
	dPtr = new double;
	chPtr = new char[64];

	*iPtr = 404;
	*dPtr = 169.254;

	strcpy_s(chPtr, 64, "Saluton Mundo!");

	cout << chPtr << endl;
	//note: not the same as
	cout << *chPtr << endl;

	cout << *iPtr << " " << *dPtr << endl;


    return 0;
}

The delete operator is used to free up memory allocated with new. It’s very important to free up the memory we use after we are done with it. Why? Because once the pointer to a block of memory goes out of scope, or is assigned a new block of memory, we no longer have a means of accessing the previously allocated memory. But it’s still there on the heap, taking up space. Such a situation is called a memory leak.

int main()
{
	double *dPtr = new double;

	*dPtr = 1.645;

	cout << *dPtr << "\t(" << dPtr << ")" << endl;
	
	//assign it new memory
	dPtr = new double;
	*dPtr = 1.96;

	//previously allocated memory is now lost
	//note the new address
	cout << *dPtr << "\t(" << dPtr << ")" << endl;

    return 0;
}