Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ЛР8. Использование указателей в разработке прог...doc
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
78.85 Кб
Скачать

Лабораторная работа №8

Использование указателей в разработке программ

Цель работы: приобретение навыка разработки программ с использованием указателей.

  1. Теоретическая часть

    1. Указатели

Указатель это адрес поля памяти, занимаемого программным объектом.

Пусть в программе определены три переменные разных типов:

int a = 5;

char с = ”G”;

float r = 1.2E8;

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

Операция & — адрес. Применение этой операции к имени переменной дает в результате ее адрес в памяти. Для переменных из данного выше примера: &а равно FFC0, &с - FFC2, &r - FFC3.

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

тип *имя_переменной

Примеры описания указателей:

int *pti; char *ptc; float *ptf;

После такого описания переменная pti может принимать значение указателя на величину целого типа; переменная ptc предназначена для хранения указателя на величину типа char; переменная ptf — на величину типа float.

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

1) присвоить указателю адрес некоторой переменной. Для этого используется операция взятия адреса, которая обозначается амперсандом &. Например, строка

a = &c;

указателю а присваивает значение адреса переменной с;

2) получить объект, адрес которого содержится в указателе; для этого используется операция звездочка '*', которая записывается перед указателем. (Заметим, что звездочкой обозначается также операция умножения.) Например, строка

d = *a;

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

d = c.

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

Как и для других типов данных, значения указателей могут инициализироваться при описании. Например:

int a = 5; int *pti = &a;

char c = ”G”; char *ptc = &c;

float r = 1.2E8; float *ptf = &r;

В заголовочном файле stdio.h определена константа — нулевой указатель с именем NULL. Ее значение можно присваивать указателю.

Например:

ptf = NULL;

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

Использованный в описаниях указателей символ * (звездочка) в данном контексте является знаком операции разадресации. С ее помощью можно сослаться через указатель на соответствующую переменную.

После приведенных выше описаний в записи выражений этой программы взаимозаменяемыми становятся а и *pti, с и *ptc, r и *ptf. Например, два оператора

х = а + 2; и x = *pti + 2;

тождественны друг другу.

В результате выполнения оператора

cout << *pti << a;

на экран выведется 55.

Сложные описания

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

• для группировки можно использовать круглые скобки, например, описание

int *(x[10]);

означает «массив из 10 элементов типа указатель на int»;

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

- операция * определения указателя имеет самый низкий приоритет. Например, описание

int *x[10];

означает «массив из 10 элементов типа указатель на int». Здесь к имени переменной x сначала применяется операция определения массива [] (квадратные скобки), поскольку она имеет более высокий приоритет, чем звездочка. Затем к полученному массиву применяется операция определения указателя. В результате получается «массив указателей», а не указатель на массив! Если нам нужно определить указатель на массив, то следует использовать круглые скобки при описании:

int (*x)[10];

Здесь к имени x сначала применяется операция * определения указателя;

- операции определения массива [] (квадратные скобки после имени) и определения функции (круглые скобки после имени) имеют одинаковый приоритет, более высокий, чем звездочка. Примеры:

int f();

Описан прототип функции / без аргументов, возвращающей значение типа int.

int (*f())[10];

Описан прототип функции / без аргументов, возвращающей значение типа указатель на массив из 10 элементов типа int;

• последний пример уже не является очевидным. Общий алгоритм разбора сложного описания можно охарактеризовать как «чтение изнутри». Сначала находим описываемое имя. Затем определяем, какая операция применяется к имени первой. Если нет круглых скобок для группировки, то это либо определение указателя (звездочка слева от имени), либо определение массива (квадратные скобки справа от имени), либо определение функции (круглые скобки справа от имени). Таким образом, получается первый шаг сложного описания. Затем находим следующую операцию описания, которая применяется к уже выделенной части сложного описания, и повторяем это до тех пор, пока не исчерпаем все описание. Проиллюстрируем этот алгоритм на примере:

void (*a[100])(int x);

Описывается переменная a. К ней сначала применяется операция описания массива из 100 элементов, далее — определение указателя, далее — функция от одного целочисленного аргумента x типа int, наконец — определение возвращаемого типа int. Описание читается следующим образом:

1) a — это

2) массив из 100 элементов типа

3) указатель на

4) функцию с одним аргументом x типа int, возвращающую значение типа

5) void.

Ниже расставлены номера операций в порядке их применения в описании переменной a:

void (* a [100])(int x);

5) 3) 1) 2) 4)