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;
}

Leave a comment