Указатели. Часть 1. Общие понятия. Типы указателей. Управляемые и неуправляемые указатели. Указатели на функцию. Примеры использования

Указатели. Часть 1. Общие понятия. Типы указателей. Управляемые и неуправляемые указатели. Указатели на функцию. Примеры использования


Содержание



1. Что такое указатель? Определение указателя. Способы доступа к переменной

Язык C++ поддерживает два способа доступа к переменной: ссылка на переменную по ее имени и использование механизма указателей.

Одним из преимуществ языка C++ в сравнении с другими языками программирования есть использование указателей для доступа к переменной. Также, как известно, доступ к переменной можно получить по ее имени.

С помощью указателей можно удобно организовывать, например, связанные списки, динамическое выделение памяти и т.д.

Указатель – это переменная, которая содержит адрес другой переменной в памяти. Например, если переменная a содержит адрес переменной b, то это означает, что переменная a указывает на переменную b.

2. Какие типы указателей поддерживаются в C++?

В Visual C++ (CLR – Common Language Runtime) поддерживаются три типа указателей:

  • управляемые указатели (managed pointers);
  • неуправляемые указатели (unmanaged pointers);
  • неуправляемые указатели на функции (unmanaged function pointers).

3. Какое отличие между управляемыми и неуправляемыми указателями?

Отличие между управляемыми и неуправляемыми указателями состоит в следующем:

в отличие от управляемого указателя неуправляемому указателю можно присвоить любой адрес памяти, даже той, которая находится за пределами исполнительной среды.

В этом случае память есть нерегулируемой.

В случае регулируемой памяти управляемому указателю присвоить адрес за пределами исполнительной среды не удастся.

4. Что такое указатели ссылочного типа или управляемые указатели (managed pointers)?

Управляемые указатели – это указатели ссылочного типа. Эти указатели передаются для аргументов, методов, которые передаются по ссылке. Эти указатели являются совместимыми со спецификацией Common Language Runtime (CLR). Управляемые указатели являются ссылками на объекты. Эти объекты размещаются в общей управляемой памяти, которая выделяется для программы в момент ее выполнения.

В таких указателях вместо символа ‘*’ применяется символ ^’. Для выделения памяти под управляемый указатель используется утилита gcnew.

5. Пример использования управляемых указателей. Утилита gcnew и ключевое слово ref в Visual Studio C++

В управляемых указателях утилита gcnew формирует экземпляр некоторого объекта. Утилита gcnew выделяет память для экземпляра объекта и возвращает ссылку на этот объект.

Описание этого объекта должно начинаться с ключевого слова ref. Ключевое слово ref дает информацию о том, что описание есть ссылочного типа. Общая форма использования ключевого слова ref имеет вид:

ref описание;

После такого описания память под объект выделяется с помощью утилиты gcnew.

Пример. В примере демонстрируется:

  • описание структуры типа BOOK ссылочного типа, который содержит информацию о книге;
  • использование ключевого слова ref для указания того, что структура есть ссылочного типа;
  • использование утилиты gcnew для выделения памяти под структуру;
  • демонстрация использования управляемых указателей для оперирования объектами.

Пусть за пределами описания класса описывается следующая структура:

ref struct BOOK // ref - обозначает, что структура есть ссылочного типа
{
    System::String  Title;  // заголовок книги
    System::String  Author; // фамилия автора книги
    int year;                // год издания
    float price;             // стоимость книги
};

Тогда в методах этого класса можно использовать структуру BOOK приблизительно следующим образом:

...

BOOK ^B; // переменная B есть типа "управляемый указатель"

B = gcnew BOOK; // выделение памяти под структуру

// заполнение полей структуры
B->Title = "Title of Book";
B->Author = "Author of Book";
B->year = 1998;
B->price = 399.85f;

...

6. Что такое неуправляемые указатели (unmanaged pointers) и неуправляемые указатели на функции (unmanaged function pointers)?

Неуправляемые указатели – это традиционные указатели C/C++. Они являются указателями на объекты в неуправляемом объеме памяти, которая выделяется для выполнения программы. Неуправляемые указатели не являются совместимыми со спецификацией CLR.

Неуправляемые указатели на функции – это указатели на функции, которые можно обрабатывать таким же образом как и неуправляемые указатели на объекты (данные).

Особенности использования неуправляемых указателей описываются в следующей теме.

7. Какая общая форма объявления неуправляемого указателя (unmanaged pointer)? Пример

Общая форма объявления указателя:

тип_переменной * имя_указателя;

где

  • тип_переменной – это тип той переменной, на которую указывает указатель. Его еще называют базовый тип указателя;
  • имя_указателя – имя переменной-указателя.

Пример. Описание указателей, которые указывают на разные типы переменных.

int * pi; // указатель с именем pi на тип int
float * pf; // указатель с именем pf на тип float

8. Неуправляемые указатели. Операция взятия адреса &. Пример

Для получения адреса переменной в памяти используется операция взятия адреса &. Эта операция есть унарной. Она возвращает адрес своего операнда в памяти.

Пример операции взятия адреса и использование указателя для доступа к переменной.

int * pi;   // указатель с именем pi на тип int
float * pf; // указатель с именем pf на тип float
float f;
int i;

i = 8;
pi = &i;  // pi указывает на i
*pi = 10; // i = 10

f = 2.93f;
pf = &f; // pf указывает на f
*pf = 8.85f; // f = 8.85

Рисунок 1 схематично показывает использование указателей для типа int.

C++ указатель int рисунок

Рисунок 1. Использование указателя на тип int

Как видно из рисунка 1, с помощью указателя можно получить доступ к переменной и изменить ее значение. В этом случае тип, на который указывает указатель, должен совпадать с типом переменной.

9. Пример использования неуправляемого указателя на функцию.

Указатели на функцию разрешают организовывать вызов функций в виде цепочки в зависимости от требований. В этом случае функции, которые выполняют разную работу, должны:

  • иметь одинаковое количество и типы аргументов;
  • возвращать значение одинакового типа.

При объявлении, указатель на функцию записывается следующим чином:

тип * (имя_функции) (список_параметров);

где

  • тип – тип значения, которое возвращается функцией.

Пример. Использование неуправляемого указателя на функцию для консольного приложения.

Описание и использование указателя на функцию с именем func_ptr, которая получает два целочисленных параметра x, y и возвращает значение типа double.

Пусть даны 2 функции, которые имеют одинаковое число параметров и возвращают одинаковое значение:

// функция, которая возводит число x в степень y
double Power(int x, int y)
{
    double res;
    res = System::Math::Pow((double)x, (double)y);
    return res;
}

// функция, которая делит x на y
double Div(int x, int y)
{
    double res;
    res = (double)x / (double)y;
    return res;
}

В консольном приложении можно использовать данный указатель следующим образом:

// описать указатель на функцию, которая получает 2 параметры типа int
double (*func_ptr)(int x, int y);

double res;

...

// вызов функции возведения в степень
func_ptr = Power; //  указывает на Power - возведение в степень
res = (*func_ptr)(4,3); // res = 64

// вызов функции деления чисел через указатель
func_ptr = Div; // func_ptr указывает на Div
res = (*func_ptr)(4,3); // res = 1.33333333333333

Такой способ использования указателей на функцию подходит только для консольных приложений.

10. Пример использования управляемого указателя на функцию в среде C++/CLI

Использование указателя на функцию в среде C++/CLI для приложений, созданных по шаблону Windows Forms. В этом случае нужно использовать так называемые делегаты. Делегаты поддерживаются в .NET Framework.

Пусть даны 2 функции, которые имеют одинаковое количество параметров и возвращают одинаковое значение:

// функция, которая возводит x в степень y
public: double Power(int x, int y)
{
    double res;
    res = System::Math::Pow((double)x, (double)y);
    return res;
}

// функция, которая делит x на y
public: double Div(int x, int y)
{
    double res;
    res = (double)x / (double)y;
    return res;
}

Чтобы использовать делегат нужно сначала в некотором месте класса формы (или другого класса) дать следующее описание:

// описание делегата DelFun, который получает два параметра типа int
// и возвращает значение типа double
public: delegate double DelFun(int , int);

...

После этого, в некотором методе (например, обработчике события клика на кнопке) можно использовать этот указатель следующим образом:

double result;

DelFun ^ PFun = gcnew DelFun(this, &Lab1::Form1::Power);
result = PFun(3,2); // result = 9

PFun = gcnew DelFun(this, &Lab1::Form1::Div);
result = PFun(3,2); // result = 1.5

В вышеприведенном примере в строках

&Lab1::Form1::Power

...

&Lab1::Form1::Div

идентификаторы имеют следующее назначение:

  • Lab1 – название проекта;
  • Form1 – название главной формы приложения (имя переменной);
  • Power – название функции.
11. Какое отличие между указателем-переменной и указателем-константой?

Указатель-переменная (указатель) – это переменная, которая предназначена для сохранения адреса в памяти.

Указатель-константа – это значение адреса оперативной памяти.


Связанные темы