Способы инициализации указателей




  • Присваивание указателю адреса существующего объекта:

· с помощью операции получения адреса:

intа=5;// целая переменная

int*р=&а;//в указатель записывается адрес а

int*р(&а);// то же самое другим способом

· с помощью значения другого инициализированного указателя:

int*r=р;

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

intb[10];// массив

int*t=b;// присваивание адреса начала массива

voidf(intа){ /*... * / } // определение функции

void (*pf)(int); // указатель на функцию

pf = f; // присваивание адреса функции

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

char*vp=(char*)0хВ8000000;

Здесь 0хВ8000000 — шестнадцатеричная константа, (char *) — операция приведения типа: константа преобразуется к типу «указатель на char».

· Присваивание пустого значения:

int* suxx = NULL;

int* rulez = 0;В первой строке используется константа NULL, определенная в некоторых заголовочных файлах С как указатель, равный нулю. Рекомендуется использовать просто 0, так как это значение типа int будет правильно преобразовано стандартными способами в соответствии с контекстом. Поскольку гарантируется, что объектов с нулевым адресом нет, пустой указатель можно использовать для проверки, ссылается указатель на конкретный объект или нет.

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

спомощьюоперации new:

int* n = new int; //1

int* m = new int (10); // 2

int* q = new int [10]; // 3

с помощью функции malloc (для того чтобы использовать malloc, требуется подключить к программе заголовочный файл <malloc.h>.)

int* u = (int *)malloc(s1zeof(int)); // 4

В операторе 1 операция new выполняет выделение достаточного для размещения величины типа 1 nt участка динамической памяти и записывает адрес начала этого участка в переменную n. Память под саму переменную п (размера, достаточного для размещения указателя) выделяется на этапе компиляции.

В операторе 2, кроме описанных выше действий, производится инициализация выделенной динамической памяти значением 10.

В операторе 3 операция new выполняет выделение памяти под 10 величин типа int (массива из 10 элементов) и записывает адрес начала этого участка в переменную q. которая может трактоваться как имя массива. Через имя можно обращаться к любому элементу массива.

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

В операторе 4 делается то же самое, что и в операторе 1, но с помощью функции выделения памяти та 11 ос, унаследованной из библиотеки С, В функцию передается один параметр — количество выделяемой памяти в байтах. Конструкция (int*) используется ц,ля приведения типа указателя, возвращаемого функцией, к требуемому типу.Если память выделить не удалось, функция возвращает 0.

Операцию new использовать предпочтительнее, чем функцию malloc, особенно при работе с объектами.

Освобождение памяти, выделенной с помощью операции new, должно выполняться с помощью delete, а памяти, выделенной функцией malloc — посредством функции free. При этом переменная-указатель сохраняется и может инициализироваться повторно. Приведенные выше динамические переменные уничтожаются следующим образом:

deleten;deletem;delete[]q;free(u);

Если память выделялась с помощью new[], для освобождения памяти необходимо применять delete[]. Размерность массива при этом не указывается. Если квадратных скобок нет, то никакого сообщения об ошибке не выдается, но помечен как свободный будет только первый элемент массива, а остальные окажутся недоступны для дальнейших операций. Такие ячейки памяти называются мусором.

Важно

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

становится недоступной. При этим память из-под самой динамической переменной не освобождается. Другой случай появления «мусора» — когда инициализированному указателю присваивается значение другого указателя. При этом старое значение бесследно теряется.

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

int*(*р[10])();

объявляется массив из 10 указателей на функции без параметров, возвращающих указатели на int.

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

При интерпретации сложных описаний необходимо придерживаться правила «изнутри наружу»:

1. если справа от имени имеются квадратные скобки, это массив, если скобки круглые — это функция;

2. если слева есть звездочка, это указатель на проинтерпретированную ранее конструкцию;

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

4. в последнюю очередь интерпретируется спецификатор типа.

Для приведенного выше описания порядок интерпретации указан цифрами:

int*(*р[10])();

 

54213// порядок интерпретации описания;

Операции с указателями в C++

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

· разадресация, или косвенное обращение к объекту (*)

· присваивание,

· сложение с константой,

· вычитание,

· инкремент (++),

· декремент (—),

· сравнение,

· приведение типов.

При работе с указателями часто используется операция получения адреса (&).

Операция разадрееации, или разыменования, предназначена для доступа к величине, адрес которой хранится в указателе. Эту операцию можно использовать как для получения, так и для изменения значения величины (если она не объявлена как константа):

charа;// переменная типа char

char*р=newchar;/* выделение памяти под указатель и под динамическую переменную типа char */

*р='Ю';а=*р;// присваивание значения обеим переменным

Как видно из примера, конструкцию *имя_указателя можно использовать в левой части оператора присваивания, так как она является L-значением, то есть определяет адрес области памяти. Для простоты эту конструкцию можно считать именем переменной, на которую ссылается указатель. С ней допустимы все действия, определенные для величин соответствующего типа (если указатель инициализирован). На одну и ту же область памяти может ссылаться несколько указателей различного типа. Примененная к ним операция разадресации даст разные результаты. Например, программа

#include <stdio.h>

int main(){

unsignedlongint A =0Xcc77ffaa;

unsignedshortint* pint =(unsignedshortint*)&A;

unsignedchar*pchar=(unsignedchar*)&A;

printf("| %x | %x | %x |", A, *pint, *pchar);

return0;

}

на IBM РС-совместимом компьютере выведет на экран строку ():

|cc77ffaa|ffaa|аа|

Значения указателей pint и pchar одинаковы, но разадресацияpchar дает в результате один младший байт по этому адресу, а pint — два младших байта.

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

При смешивании в выражении указателей разных типов явное преобразование типов требуется для всех указателей, кромеvoid*. Указатель может неявно преобразовываться в значение тина booд (например, в выражении условного оператора), при этом ненулевой указатель преобразуется в true, а нулевой в false.

Присваивание без явного приведения типов допускается в двух случаях:

· указателям типа void*:

· если тип указателей справа и слева от операции присваивания один и тот же.

Таким образом, неявное преобразование выполняется только к типу void*. Значение 0 неявно преобразуется к указателю на любой тип. Присваивание указателей на объекты указателям на функции (и наоборот) недопустимо. Запрещено и присваивать значения указателям-константам, впрочем, как и константам любого типа (присваивать значения указателям на константу и переменным, на которые ссылается указатель-константа, допускается).

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

Инкремент перемещает указатель к следующему элементу массива, декремент — к предыдущему. Фактически значение указателя изменяется на величину sizeof (тип). Если указатель на определенный тип увеличивается или уменьшается на константу, его значение изменяется на величину этой константы, умноженную на размер объекта данного типа, например:

short*р=newshort[5];

p++;// значение р увеличивается на 2

long*q=newlong[5];

q++;// значение q увеличивается на 4

Разность двух указателей — это разность их значений, деленная на размер типа в байтах (в применении к массивам разность указателей, например, на третий и шестой элементы равна 3). Суммирование двух указателей не допускается.

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

*р++=10;

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

*р=10;p++;

Выражение (*р)++. напротив, инкрементирует значение, на которое ссылается указатель.

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

Ссылки в C++

Ссылка представляет собой синоним имени, указанного при инициализации ссылки. Ссылку можно рассматривать как указатель, который всегда разыменовывается. Формат объявления ссылки:

тип & имя;

где тип — это тип величины, на которую указывает ссылка, & — оператор ссылки, означающий, что следующее за ним имя является именем переменной ссылочного типа, например:

intкоl;

int&pal=kol;// ссылка pal - альтернативное имя для коl

constchar&CR=' \n ';// ссылка на константу

Запомните следующие правила.

· Переменная-ссылка должна явно инициализироваться при ее описании, кроме случаев, когда она является параметром функции, описана как extern или ссылается на поле данных класса.

· После инициализации ссылке не может быть присвоена другая переменная.

· Тип ссылки должен совпадать с типом величины, на которую она ссылается.

· Не разрешается определять указатели на ссылки, создавать массивы ссылок и ссылки на ссылки.

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

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

Программы из лекции

1. Создание указателя

2. Сравнение указателей

3. Указатели на указатели

4. Ссылка на функцию

 



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2018-01-31 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: