C++. Pointers. Part 5. Memory allocation for the pointer. Arrays of pointers to basic types, functions, structures, classes




Pointers. Part 5. Memory allocation for the pointer. Arrays of pointers to basic types, functions, structures, classes


Content


Search other websites:

1. What programming errors can occur when allocating memory for a pointer?

While describing a pointer to a certain type, you must always take care that the pointer points to a variable (object) for which memory should be previously allocated. If this is not done, an exception will arise. The example demonstrates an attempt to use two pointers, for one of which is not allocated memory.

Example. Demonstration of allocation of memory for the pointer.

// pointer to int
int * p; // the value of pointer p is still indefinite
int *p2; // The value of p2 is undefined
int x;   // Variable for which memory is allocated

x = 240;
p = &x; // pointer p points to the element for which the memory is allocated
*p = 100; // it works, because p points to x, so x = 100

//*p2 = 100; // Error because p2 points to unknown value

Let’s explain some fragments of the above example. The example declares two pointers to int

int * p;
int *p2;

After such declaration the pointer value is indefinite. And this means that pointers point to arbitrary parts of memory. If you try to write some constant value into these pointers, for example

*p = 25;
*p2 = 100;

then there will be an exception  and the system will behave unpredictably. This is explained by the fact that an attempt is made to change the value by a pointer that points to an arbitrary memory location (for example, the pointer points to the program code).

After assignment

p = &x;

The pointer p points to the variable x, for which memory is already allocated. Therefore, the string

*p = 100;

is executed correctly. But a line of code

*p2 = 100;

is an error, since the pointer p2 is not defined.

2. What are the ways to allocate memory for the unmanaged pointer (*)? Example

There are 2 ways to allocate memory for the unmanaged pointer:

  • assign the pointer the address of variable for which the memory is already statically allocated;
  • allocate memory dynamically using the new operator;
  • allocate memory dynamically using a special function from the C++ library, for example, malloc().

Example. Methods for allocating memory for a pointer to a double.

double x;
double *p;

// Method 1. Assigning an address to a variable for which memory is already allocated
x = 2.86;
p = &x; // p points to x
*p = -0.85; // x = -0.85

// Method 2. Use the new operator
p = new double;
*p = 9.36;

// Method 3. Use the malloc() function
p = (double *)malloc(sizeof(double));
*p = -90.3;

In the above example, in method 3, to use the malloc() function, you must first connect the <malloc.h> module to the program’s text:

#include <malloc.h>

3. In which cases is it advisable to use an array of pointers?

The use of arrays of pointers is effective in cases when it is necessary to compactly place in memory “extended” data objects, which can be:

  • text strings;
  • structural variables;
  • objects of classes.

An array of pointers helps to save memory and increase performance in the case of variable-length strings.

4. What is the general form of describing a one-dimensional array of unmanaged pointers (*)?

General form of defining a one-dimensional array of pointers:

type * name_of_array[size]

where

  • name_of_array – directly the name of the array of pointers;
  • type – type to which the pointers points in the array;
  • size – the size of the array of pointers. In this case, the compiler reserves space for pointers, the number of which is specified in the value of the size. The size is reserved for the pointers themselves and not for the objects (data) to which they point.

5. Array of pointers to int type. Example

For each pointer in the array of pointers to the int type can be pre-allocated memory (see p.1).

Example. An array with 10 pointers to int.

// array of pointers to int
int * p[10]; // array of 10 pointers to int
int x;       // Variable to which memory is allocated

x = 240;
p[0] = &x; // this works, the pointer points to the element for which the memory is allocated
*p[0] = 100; // works because p[0] points to x, so x = 100

//*p[1] = 100; // error, since p[1] points an unknown value

6. An array of pointers to type double. Example of definition. Example of dynamic allocation of memory for array elements

An array of pointers to real types is declared exactly the same as an integer type (see p.2).

Example. Array of 20 pointers to double type

// array of pointers to double
double *p[20]; // the value of the pointers is undefined
// Memory is allocated dynamically in a loop
for (int i=0; i<20; i++)
{
    p[i] = new double; // dynamic allocation of memory for the pointer
    *p[i] = i+0.5;
}

7. Array of pointers to char. Example

Example. Definition array to a char* with simultaneous initialization.

// array of pointers to char
char * ap[] = { "C/C++",
                "Java",
                "C#",
                "Object Pascal",
                "Python" };

// displaying an array of pointers to char in the listBox1 component
String str; // additional variable

listBox1->Items->Clear();
for (int i=0; i<5; i++)
{
    str = gcnew String(ap[i]);
    listBox1->Items->Add(str);
}

In the above example, pointers get an initial value that is equal to the memory address of the corresponding lowercase literal.

8. Example of sorting an array of strings using an unmanaged pointer

Using pointers has its advantages if you want to sort arrays of strings. Sorting using pointers is faster because you do not need to copy strings when changing their positions in a string array. In fact, only the most pointers change positions. And this happens faster than rearranging the strings.

Example. Sorting strings using the insertion sort. A method is proposed without using the function strcmp() from the module <string.h>. Instead, the Compare() function of the System.String class is used. It also demonstrates the display of the rows pointed to by the array of pointers in the listBox1 control (ListBox type).

// array of pointers to char
char * ap[] = { "C/C++",
                "Java",
                "C#",
                "Object Pascal",
                "Python" };
int n = 5; // Number of strings in ap[]

// sorting strings by byble method
for (int i=0; i<n-1; i++)
    for (int j=i; j>=0; j--)
    {
        // additional variables
        String s1 = gcnew String(ap[j]);   // converting from char* to String
        String s2 = gcnew String(ap[j+1]);

        // use the method Compare() from class System.String
        int res; // additional variable
        res = s1->Compare(s1,s2);

        if (res==1) // s1>s2
        {
            // swap the pointers
            char * tp;
            tp = ap[j];
            ap[j] = ap[j+1];
            ap[j+1] = tp;
        }
    }

// displaying an array of pointers to char in the listBox1 component
String str; // additional variable

listBox1->Items->Clear();
for (int i=0; i<5; i++)
{
    str = gcnew String(ap[i]);
    listBox1->Items->Add(str);
}

In the above example, in fact, the pointers are sorted according to the values to which they point.

9. How to define an array of unmanaged pointers (*) to a function? Example

An array of unmanaged pointers (*) to a function can be defined only for console applications. If the program is developed using the Windows Forms template in the CLR environment, then you can not use the array of unmanaged pointers (*) to a function.

Let there be given 4 functions that receive two operands of type int and perform the following operations on them:

  • addition, function Add();
  • subtraction, function Sub();
  • multiplications, function Mul();
  • division, function Div().

The implementation of functions is as follows:

// function Add, returns x+y
int Add(int x, int y)
{
    return x+y;
}

// function Sub, returns x-y
int Sub(int x, int y)
{
    return x-y;
}

// function Mul, returns x*y
int Mul(int x, int y)
{
    return x*y;
}

// function Div, returns x/y
int Div(int x, int y)
{
    return (int)(x/y);
}

Demonstration of the function calling through an array of pointers:

// Only for the console application
// array o pointers to a function
int (*p[4])(int, int);
int res_add, res_sub, res_mul, res_div;

p[0] = Add; // p[0] points to Add
res_add = (*p[0])(7, 5); // res = 7+5 = 12

p[1] = Sub; // p[1] points to Sub
res_sub = (*p[1])(7, 5); // res = 7-5 = 2

p[2] = Mul; // p[2] points to Mul
res_mul = (*p[2])(7, 5); // res = 7*5 = 35

p[3] = Div; // p[3] points to Div
res_div = (*p[3])(7, 5); // res = 7/5 = 1

10. Array of unmanaged pointers (*) to the structure

In Visual C++, an array of pointers to a structure can be described for both unmanaged pointers (*) and for managed pointers (^). In this example, an array of unmanaged pointers (*) is considered.

More information about the types of pointers in C++ is described here.

Example. Given a structure that defines a point (pixel) on the screen. In the MS Visual Studio environment, if the project is created using the Windows Forms Application template, then the structure should be described outside the main form class of the Form1 program.

// the structure that describes the pixel on the monitor screen
struct MyPoint
{
    int x;
    int y;
    int color; // color
};

If the structure is described in another module, for example “MyStruct.h”, then at the beginning of the file of the main form module, you need to add line

#include "MyStruct.h"

Using an array of pointers to the MyPoint structure:

// unmanaged array of pointers to the structure
MyPoint *p[3];

// allocating memory for an array of structured variables
for (int i=0; i<3; i++)
{
    p[i] = new MyPoint;
}

// filling the values of the fields of structures
for (int i=0; i<3; i++)
{
    p[i]->x = i+5; // random values of x, y, color
    p[i]->y = 2*i+14;
    p[i]->color = i;
}

11. An array of unmanaged pointers (*) to the class. Example of definition and using

An array of pointers to a class is defined exactly as an array of pointers to the structure.

Example. Let there be given a class that describes the point on the monitor screen (pixel).

The class is defined in two modules:

  • the module “MyPixelClass.h” defines the declaration of the class, its methods (functions) and fields (internal variables);
  • the module “MyPixelClass.cpp” defines the implementation of the methods (functions) declared in the class (in the module “MyPixelClass.h”).

The text of the module “MyPixelClass.h” (class declaration):

#pragma once

// class is declared without the keyword ref
class MyPixelClass
{
    int x;
    int y;
    int color;

    public:
    MyPixelClass(void);

    int GetX(void);
    int GetY(void);
    int GetColor(void);
    void SetXYColor(int nx, int ny, int nc);
};

The text of the module “MyPixelClass.cpp” (implementation of class):

#include "StdAfx.h"
#include "MyPixelClass.h"

// implementation of methods of class MyPixelClass
MyPixelClass::MyPixelClass(void)
{
    x = y = color = 0;
}

int MyPixelClass::GetX(void)
{
    return x;
}

int MyPixelClass::GetY(void)
{
    return y;
}

int MyPixelClass::GetColor(void)
{
    return color;
}

void MyPixelClass::SetXYColor(int nx, int ny, int nc)
{
    x = nx;
    y = ny;
    color = nc;
}

Demonstration of using an array of pointers to the MyPixelClass class from another program code (for example, the event handler for a click event on a button):

// array of unmanaged pointers to a class
MyPixelClass *mp[5];

// Allocating memory for class objects,
// to which pointers will point
for (int i=0; i<5; i++)
    mp[i] = new MyPixelClass;

// Filling objects of a class with arbitrary values
// demonstration of calling SetXYColor() method
for (int i=0; i<5; i++)
    mp[i]->SetXYColor(i+5, 2*i+8, i);

// demonstration of other class methods
int d;
d = mp[1]->GetX();     // d = 6
d = mp[2]->GetColor(); // d = 2
d = mp[3]->GetY();     // d = 2*3+8 = 14

12. Creation of a dynamic array of class instances of type ClassName**. The number of instances is generated dynamically. Example of pointer of type Point**

In the previous examples (clauses 10, 11), the number of pointers to the class was known in advance and the declaration of an array of class instances had approximately the following form

ClassName* AP[n];

here

  • ClassName – the name of some class (type);
  • AP – the name of the array of pointers;
  • n is a constant that determines the number of elements in the array.

However, there are times when you need to create an array of class instances, the number of which in the program is not known in advance and is generated dynamically. In this case, a pointer to a class pointer is declared according to the pattern below

ClassName** AP;

Then, for this pointer, memory is allocated something like this

...

AP = new ClassName* [n];

for (int i = 0; i < n; i++)
  AP[i] = new ClassName(parameters_list);

...

here

  • AP – an array of pointers to the ClassName type;
  • parameters_list – a list of constructor parameters on the basis of which an instance of the class is created;
  • n – the number of pointers for which memory is to be allocated.

Example.

The following example demonstrates the creation and use of a fully dynamic array of pointers using the Point class as an example. To do this, the following operations are performed in the main() function:

  • an array AP of pointers to the Point type is declared;
  • memory for the AP array and its elements is allocated;
  • the array is filled with data;
  • the contents of the array are displayed on the screen;
  • the memory allocated for the elements of the array is freed.

 

#include <iostream>
using namespace std;

// A class that describes a point on a coordinate plane
class Point
{
private:
  int x, y; // internal class fields

public:
  // Constructor without a parameters
  Point() :Point(0, 0) // delegation to a constructor with two parameters
  {
  }

  // Constructor with two parameters
  Point(int x, int y)
  {
    this->x = x;
    this->y = y;
  }

  // Methods for accessing internal variables
  int GetX() { return x; }
  int GetY() { return y; }
  void SetXY(int _x, int _y)
  {
    x = _x;
    y = _y;
  }

  // Distance from point to origin of coordinates
  double Length()
  {
    return sqrt(x * x + y * y);
  }
};

void main()
{
  // Dynamically allocating memory for an array of Point ** pointers

  // 1. Declare an array of pointers of type Point
  Point** AP;

  // 2. Set the number of pointers
  int n;
  cout << "n = "; 
  cin >> n;

  // 3. Allocate memory for the array of n pointers of type Point
  // The try-cath block is required for correct program termination,
  // if an error occurred while allocating memory.
  try
  {
    // 3.1. Attempting to allocate memory for the array of n pointers 
    AP = new Point * [n];

    // 3.1. Attempting to allocate memory for each element of an array of type Point
    // and fill it with values
    for (int i = 0; i < n; i++)
      AP[i] = new Point(i, i + i);

    // 4. If memory is allocated and the array is formed, then display it
    for (int i = 0; i < n; i++)
      cout << AP[i]->GetX() << " - " << AP[i]->GetY() << endl;
  }
  catch (bad_alloc ba)
  {
    // Taking action in case of error
    cout << ba.what() << endl;
    return;
  }

  // 5. Free the memory that was allocated for the AP array
  // 5.1. Free the memory that was allocated for each element of the array
  for (int i = 0; i < n; i++)
    delete AP[i];

  // 5.2. Free the memory that was allocated for the array as a whole
  delete[] AP;
}


Related topics